summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authordarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-19 05:24:57 +0000
committerdarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-19 05:24:57 +0000
commit552e600db9f51ac1efb0438bd749227226dffa64 (patch)
tree121e6a11a6f637e9b5cfd2d04244c94b1961420f /chrome/renderer
parent0dea18f231b425216aae0c956129bc80b7086770 (diff)
downloadchromium_src-552e600db9f51ac1efb0438bd749227226dffa64.zip
chromium_src-552e600db9f51ac1efb0438bd749227226dffa64.tar.gz
chromium_src-552e600db9f51ac1efb0438bd749227226dffa64.tar.bz2
Refactors RenderWidget to extract a PaintAggregator class.
After this change, I plan on changing the PaintAggregator algorithm. Some things to note: 1- Previously, it was possible to send overlapping ViewHostMsg_PaintRect and ViewHostMsg_ScrollRect messages. This happened when scrolling a page since the scrollbar would need to be repainted while the contents of the page are being scrolled. With this CL, this overlapping behavior is a bit more explicit. 2- There was a TODO about view_size clipping that I've eliminated. I was able to eliminate it because I realized that it is correct to clip the rects passed by didInvalidateRect and didScrollRect to the size of the RenderWidget. Apparently WebKit can sometimes invalidate regions outside the view. R=brettw BUG=25905 TEST=none Review URL: http://codereview.chromium.org/403005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32496 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/paint_aggregator.cc105
-rw-r--r--chrome/renderer/paint_aggregator.h49
-rw-r--r--chrome/renderer/paint_aggregator_unittest.cc128
-rw-r--r--chrome/renderer/render_widget.cc293
-rw-r--r--chrome/renderer/render_widget.h18
5 files changed, 397 insertions, 196 deletions
diff --git a/chrome/renderer/paint_aggregator.cc b/chrome/renderer/paint_aggregator.cc
new file mode 100644
index 0000000..ca4841b
--- /dev/null
+++ b/chrome/renderer/paint_aggregator.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/paint_aggregator.h"
+
+#include "base/logging.h"
+
+// We implement a very simple algorithm:
+//
+// - Multiple repaints are unioned to form the smallest bounding box.
+// - If a scroll intersects a repaint, then the scroll is downgraded
+// to a repaint and then unioned with the existing repaint.
+//
+// This allows for a scroll to exist in parallel to a repaint provided the two
+// do not intersect.
+
+gfx::Rect PaintAggregator::PendingUpdate::GetScrollDamage() const {
+ // Should only be scrolling in one direction at a time.
+ DCHECK(!(scroll_delta.x() && scroll_delta.y()));
+
+ gfx::Rect damaged_rect;
+
+ // Compute the region we will expose by scrolling, and paint that into a
+ // shared memory section.
+ if (scroll_delta.x()) {
+ int dx = scroll_delta.x();
+ damaged_rect.set_y(scroll_rect.y());
+ damaged_rect.set_height(scroll_rect.height());
+ if (dx > 0) {
+ damaged_rect.set_x(scroll_rect.x());
+ damaged_rect.set_width(dx);
+ } else {
+ damaged_rect.set_x(scroll_rect.right() + dx);
+ damaged_rect.set_width(-dx);
+ }
+ } else {
+ int dy = scroll_delta.y();
+ damaged_rect.set_x(scroll_rect.x());
+ damaged_rect.set_width(scroll_rect.width());
+ if (dy > 0) {
+ damaged_rect.set_y(scroll_rect.y());
+ damaged_rect.set_height(dy);
+ } else {
+ damaged_rect.set_y(scroll_rect.bottom() + dy);
+ damaged_rect.set_height(-dy);
+ }
+ }
+
+ // In case the scroll offset exceeds the width/height of the scroll rect
+ return scroll_rect.Intersect(damaged_rect);
+}
+
+bool PaintAggregator::HasPendingUpdate() const {
+ return !update_.scroll_rect.IsEmpty() || !update_.paint_rect.IsEmpty();
+}
+
+void PaintAggregator::ClearPendingUpdate() {
+ update_ = PendingUpdate();
+}
+
+void PaintAggregator::InvalidateRect(const gfx::Rect& rect) {
+ // If this invalidate overlaps with a pending scroll, then we have to
+ // downgrade to invalidating the scroll rect.
+ if (rect.Intersects(update_.scroll_rect)) {
+ update_.paint_rect = update_.paint_rect.Union(update_.scroll_rect);
+ update_.scroll_rect = gfx::Rect();
+ update_.scroll_delta = gfx::Point();
+ }
+
+ update_.paint_rect = update_.paint_rect.Union(rect);
+}
+
+void PaintAggregator::ScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
+ if (dx != 0 && dy != 0) {
+ // We only support scrolling along one axis at a time.
+ ScrollRect(0, dy, clip_rect);
+ dy = 0;
+ }
+
+ bool intersects_with_painting = update_.paint_rect.Intersects(clip_rect);
+
+ // If we already have a pending scroll operation or if this scroll operation
+ // intersects the existing paint region, then just failover to invalidating.
+ if (!update_.scroll_rect.IsEmpty() || intersects_with_painting) {
+ if (!intersects_with_painting && update_.scroll_rect == clip_rect) {
+ // OK, we can just update the scroll delta (requires same scrolling axis)
+ if (!dx && !update_.scroll_delta.x()) {
+ update_.scroll_delta.set_y(update_.scroll_delta.y() + dy);
+ return;
+ }
+ if (!dy && !update_.scroll_delta.y()) {
+ update_.scroll_delta.set_x(update_.scroll_delta.x() + dx);
+ return;
+ }
+ }
+ InvalidateRect(update_.scroll_rect);
+ DCHECK(update_.scroll_rect.IsEmpty());
+ InvalidateRect(clip_rect);
+ return;
+ }
+
+ update_.scroll_rect = clip_rect;
+ update_.scroll_delta = gfx::Point(dx, dy);
+}
diff --git a/chrome/renderer/paint_aggregator.h b/chrome/renderer/paint_aggregator.h
new file mode 100644
index 0000000..b3ecf4d
--- /dev/null
+++ b/chrome/renderer/paint_aggregator.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_PAINT_AGGREGATOR_H_
+#define CHROME_RENDERER_PAINT_AGGREGATOR_H_
+
+#include "base/gfx/rect.h"
+
+// This class is responsible for aggregating multiple invalidation and scroll
+// commands to produce a single scroll and repaint.
+class PaintAggregator {
+ public:
+
+ // This structure describes an aggregation of InvalidateRect and ScrollRect
+ // calls. If |scroll_rect| is non-empty, then that rect should be scrolled
+ // by the amount specified by |scroll_delta|. If |paint_rect| is non-empty,
+ // then that rect should be repainted. If |scroll_rect| and |paint_rect| are
+ // non-empty, then scrolling should be performed before repainting.
+ // |scroll_delta| can only specify scrolling in one direction (i.e., the x
+ // and y members cannot both be non-zero).
+ struct PendingUpdate {
+ gfx::Point scroll_delta;
+ gfx::Rect scroll_rect;
+ gfx::Rect paint_rect;
+
+ // Returns the rect damaged by scrolling within |scroll_rect| by
+ // |scroll_delta|. This rect must be repainted.
+ gfx::Rect GetScrollDamage() const;
+ };
+
+ // There is a PendingUpdate if InvalidateRect or ScrollRect were called and
+ // ClearPendingUpdate was not called.
+ bool HasPendingUpdate() const;
+ void ClearPendingUpdate();
+
+ const PendingUpdate& GetPendingUpdate() const { return update_; }
+
+ // The given rect should be repainted.
+ void InvalidateRect(const gfx::Rect& rect);
+
+ // The given rect should be scrolled by the given amounts.
+ void ScrollRect(int dx, int dy, const gfx::Rect& clip_rect);
+
+ private:
+ PendingUpdate update_;
+};
+
+#endif // CHROME_RENDERER_PAINT_AGGREGATOR_H_
diff --git a/chrome/renderer/paint_aggregator_unittest.cc b/chrome/renderer/paint_aggregator_unittest.cc
new file mode 100644
index 0000000..f490a2d
--- /dev/null
+++ b/chrome/renderer/paint_aggregator_unittest.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/paint_aggregator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PaintAggregator, InitialState) {
+ PaintAggregator greg;
+ EXPECT_FALSE(greg.HasPendingUpdate());
+}
+
+TEST(PaintAggregator, SingleInvalidation) {
+ PaintAggregator greg;
+
+ gfx::Rect rect(2, 4, 10, 16);
+ greg.InvalidateRect(rect);
+
+ EXPECT_TRUE(greg.HasPendingUpdate());
+ EXPECT_TRUE(greg.GetPendingUpdate().scroll_rect.IsEmpty());
+ EXPECT_FALSE(greg.GetPendingUpdate().paint_rect.IsEmpty());
+
+ EXPECT_EQ(rect.x(), greg.GetPendingUpdate().paint_rect.x());
+ EXPECT_EQ(rect.y(), greg.GetPendingUpdate().paint_rect.y());
+ EXPECT_EQ(rect.width(), greg.GetPendingUpdate().paint_rect.width());
+ EXPECT_EQ(rect.height(), greg.GetPendingUpdate().paint_rect.height());
+}
+
+TEST(PaintAggregator, DoubleDisjointInvalidation) {
+ PaintAggregator greg;
+
+ gfx::Rect r1(2, 4, 2, 4);
+ gfx::Rect r2(4, 2, 4, 2);
+
+ greg.InvalidateRect(r1);
+ greg.InvalidateRect(r2);
+
+ gfx::Rect expected = r1.Union(r2);
+
+ EXPECT_TRUE(greg.HasPendingUpdate());
+ EXPECT_TRUE(greg.GetPendingUpdate().scroll_rect.IsEmpty());
+ EXPECT_FALSE(greg.GetPendingUpdate().paint_rect.IsEmpty());
+
+ EXPECT_EQ(expected.x(), greg.GetPendingUpdate().paint_rect.x());
+ EXPECT_EQ(expected.y(), greg.GetPendingUpdate().paint_rect.y());
+ EXPECT_EQ(expected.width(), greg.GetPendingUpdate().paint_rect.width());
+ EXPECT_EQ(expected.height(), greg.GetPendingUpdate().paint_rect.height());
+}
+
+TEST(PaintAggregator, SingleScroll) {
+ PaintAggregator greg;
+
+ gfx::Rect rect(1, 2, 3, 4);
+ gfx::Point delta(1, 0);
+ greg.ScrollRect(delta.x(), delta.y(), rect);
+
+ EXPECT_TRUE(greg.HasPendingUpdate());
+ EXPECT_TRUE(greg.GetPendingUpdate().paint_rect.IsEmpty());
+ EXPECT_FALSE(greg.GetPendingUpdate().scroll_rect.IsEmpty());
+
+ EXPECT_EQ(rect.x(), greg.GetPendingUpdate().scroll_rect.x());
+ EXPECT_EQ(rect.y(), greg.GetPendingUpdate().scroll_rect.y());
+ EXPECT_EQ(rect.width(), greg.GetPendingUpdate().scroll_rect.width());
+ EXPECT_EQ(rect.height(), greg.GetPendingUpdate().scroll_rect.height());
+
+ EXPECT_EQ(delta.x(), greg.GetPendingUpdate().scroll_delta.x());
+ EXPECT_EQ(delta.y(), greg.GetPendingUpdate().scroll_delta.y());
+
+ gfx::Rect resulting_damage = greg.GetPendingUpdate().GetScrollDamage();
+ gfx::Rect expected_damage(1, 2, 1, 4);
+ EXPECT_EQ(expected_damage.x(), resulting_damage.x());
+ EXPECT_EQ(expected_damage.y(), resulting_damage.y());
+ EXPECT_EQ(expected_damage.width(), resulting_damage.width());
+ EXPECT_EQ(expected_damage.height(), resulting_damage.height());
+}
+
+TEST(PaintAggregator, DoubleOverlappingScroll) {
+ PaintAggregator greg;
+
+ gfx::Rect rect(1, 2, 3, 4);
+ gfx::Point delta1(1, 0);
+ gfx::Point delta2(1, 0);
+ greg.ScrollRect(delta1.x(), delta1.y(), rect);
+ greg.ScrollRect(delta2.x(), delta2.y(), rect);
+
+ EXPECT_TRUE(greg.HasPendingUpdate());
+ EXPECT_TRUE(greg.GetPendingUpdate().paint_rect.IsEmpty());
+ EXPECT_FALSE(greg.GetPendingUpdate().scroll_rect.IsEmpty());
+
+ EXPECT_EQ(rect.x(), greg.GetPendingUpdate().scroll_rect.x());
+ EXPECT_EQ(rect.y(), greg.GetPendingUpdate().scroll_rect.y());
+ EXPECT_EQ(rect.width(), greg.GetPendingUpdate().scroll_rect.width());
+ EXPECT_EQ(rect.height(), greg.GetPendingUpdate().scroll_rect.height());
+
+ gfx::Point expected_delta(delta1.x() + delta2.x(),
+ delta1.y() + delta2.y());
+ EXPECT_EQ(expected_delta.x(), greg.GetPendingUpdate().scroll_delta.x());
+ EXPECT_EQ(expected_delta.y(), greg.GetPendingUpdate().scroll_delta.y());
+
+ gfx::Rect resulting_damage = greg.GetPendingUpdate().GetScrollDamage();
+ gfx::Rect expected_damage(1, 2, 2, 4);
+ EXPECT_EQ(expected_damage.x(), resulting_damage.x());
+ EXPECT_EQ(expected_damage.y(), resulting_damage.y());
+ EXPECT_EQ(expected_damage.width(), resulting_damage.width());
+ EXPECT_EQ(expected_damage.height(), resulting_damage.height());
+}
+
+TEST(PaintAggregator, DiagonalScroll) {
+ PaintAggregator greg;
+
+ // We don't support optimized diagonal scrolling, so this should result in
+ // repainting.
+
+ gfx::Rect rect(1, 2, 3, 4);
+ gfx::Point delta(1, 1);
+ greg.ScrollRect(delta.x(), delta.y(), rect);
+
+ EXPECT_TRUE(greg.HasPendingUpdate());
+ EXPECT_TRUE(greg.GetPendingUpdate().scroll_rect.IsEmpty());
+ EXPECT_FALSE(greg.GetPendingUpdate().paint_rect.IsEmpty());
+
+ EXPECT_EQ(rect.x(), greg.GetPendingUpdate().paint_rect.x());
+ EXPECT_EQ(rect.y(), greg.GetPendingUpdate().paint_rect.y());
+ EXPECT_EQ(rect.width(), greg.GetPendingUpdate().paint_rect.width());
+ EXPECT_EQ(rect.height(), greg.GetPendingUpdate().paint_rect.height());
+}
+
+// TODO(darin): Add tests for mixed scrolling and invalidation
diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc
index 85ecf77..34dee52 100644
--- a/chrome/renderer/render_widget.cc
+++ b/chrome/renderer/render_widget.cc
@@ -215,17 +215,15 @@ void RenderWidget::OnResize(const gfx::Size& new_size,
// We should not be sent a Resize message if we have not ACK'd the previous
DCHECK(!next_paint_is_resize_ack());
+ paint_aggregator_.ClearPendingUpdate();
+
// When resizing, we want to wait to paint before ACK'ing the resize. This
// ensures that we only resize as fast as we can paint. We only need to send
// an ACK if we are resized to a non-empty rect.
webwidget_->resize(new_size);
if (!new_size.IsEmpty()) {
- DCHECK(!paint_rect_.IsEmpty());
-
- // This should have caused an invalidation of the entire view. The damaged
- // rect could be larger than new_size if we are being made smaller.
- DCHECK_GE(paint_rect_.width(), new_size.width());
- DCHECK_GE(paint_rect_.height(), new_size.height());
+ // Resize should have caused an invalidation of the entire view.
+ DCHECK(paint_aggregator_.HasPendingUpdate());
// We will send the Resize_ACK flag once we paint again.
set_next_paint_is_resize_ack();
@@ -271,7 +269,7 @@ void RenderWidget::OnPaintRectAck() {
DidPaint();
// Continue painting if necessary...
- CallDoDeferredPaint();
+ CallDoDeferredUpdate();
}
void RenderWidget::OnRequestMoveAck() {
@@ -288,16 +286,7 @@ void RenderWidget::OnScrollRectAck() {
}
// Continue scrolling if necessary...
- CallDoDeferredScroll();
-}
-
-void RenderWidget::CallDoDeferredScroll() {
- DoDeferredScroll();
-
- if (pending_input_event_ack_.get()) {
- Send(pending_input_event_ack_.get());
- pending_input_event_ack_.release();
- }
+ CallDoDeferredUpdate();
}
void RenderWidget::OnHandleInputEvent(const IPC::Message& message) {
@@ -322,7 +311,7 @@ void RenderWidget::OnHandleInputEvent(const IPC::Message& message) {
response->WriteBool(processed);
if (input_event->type == WebInputEvent::MouseMove &&
- (!paint_rect_.IsEmpty() || !scroll_rect_.IsEmpty())) {
+ paint_aggregator_.HasPendingUpdate()) {
// We want to rate limit the input events in this case, so we'll wait for
// painting to finish before ACKing this message.
pending_input_event_ack_.reset(response);
@@ -387,8 +376,8 @@ void RenderWidget::PaintRect(const gfx::Rect& rect,
canvas->getTopPlatformDevice().accessBitmap(false);
}
-void RenderWidget::CallDoDeferredPaint() {
- DoDeferredPaint();
+void RenderWidget::CallDoDeferredUpdate() {
+ DoDeferredUpdate();
if (pending_input_event_ack_.get()) {
Send(pending_input_event_ack_.get());
@@ -396,140 +385,96 @@ void RenderWidget::CallDoDeferredPaint() {
}
}
-void RenderWidget::DoDeferredPaint() {
- if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty())
+void RenderWidget::DoDeferredUpdate() {
+ if (!webwidget_ || !paint_aggregator_.HasPendingUpdate() ||
+ paint_reply_pending() || scroll_reply_pending())
return;
- // When we are hidden, we want to suppress painting, but we still need to
- // mark this DoDeferredPaint as complete.
+ // Suppress updating when we are hidden.
if (is_hidden_ || size_.IsEmpty()) {
- paint_rect_ = gfx::Rect();
+ paint_aggregator_.ClearPendingUpdate();
needs_repainting_on_restore_ = true;
return;
}
- // Layout may generate more invalidation...
+ // Layout may generate more invalidation.
webwidget_->layout();
- // OK, save the current paint_rect to a local since painting may cause more
+ // OK, save the pending update to a local since painting may cause more
// invalidation. Some WebCore rendering objects only layout when painted.
- gfx::Rect damaged_rect = paint_rect_;
- paint_rect_ = gfx::Rect();
-
- // Compute a buffer for painting and cache it.
- skia::PlatformCanvas* canvas =
- RenderProcess::current()->GetDrawingCanvas(&current_paint_buf_,
- damaged_rect);
- if (!canvas) {
- NOTREACHED();
- return;
- }
-
- // We may get back a smaller canvas than we asked for.
- damaged_rect.set_width(canvas->getDevice()->width());
- damaged_rect.set_height(canvas->getDevice()->height());
-
- PaintRect(damaged_rect, canvas);
+ PaintAggregator::PendingUpdate update = paint_aggregator_.GetPendingUpdate();
+ paint_aggregator_.ClearPendingUpdate();
+
+ if (!update.scroll_rect.IsEmpty()) {
+ // Optmized scrolling
+
+ // Compute the region we will expose by scrolling, and paint that into a
+ // shared memory section.
+ gfx::Rect damaged_rect = update.GetScrollDamage();
+
+ scoped_ptr<skia::PlatformCanvas> canvas(
+ RenderProcess::current()->GetDrawingCanvas(&current_scroll_buf_,
+ damaged_rect));
+ if (!canvas.get()) {
+ NOTREACHED();
+ return;
+ }
- ViewHostMsg_PaintRect_Params params;
- params.bitmap_rect = damaged_rect;
- params.view_size = size_;
- params.plugin_window_moves = plugin_window_moves_;
- params.flags = next_paint_flags_;
- params.bitmap = current_paint_buf_->id();
+ // We may get back a smaller canvas than we asked for.
+ damaged_rect.set_width(canvas->getDevice()->width());
+ damaged_rect.set_height(canvas->getDevice()->height());
- delete canvas;
+ // Set these parameters before calling Paint, since that could result in
+ // further invalidates (uncommon).
+ ViewHostMsg_ScrollRect_Params params;
+ params.bitmap_rect = damaged_rect;
+ params.dx = update.scroll_delta.x();
+ params.dy = update.scroll_delta.y();
+ params.clip_rect = update.scroll_rect;
+ params.view_size = size_;
+ params.plugin_window_moves = plugin_window_moves_;
+ params.bitmap = current_scroll_buf_->id();
- plugin_window_moves_.clear();
+ plugin_window_moves_.clear();
- paint_reply_pending_ = true;
- Send(new ViewHostMsg_PaintRect(routing_id_, params));
- next_paint_flags_ = 0;
+ PaintRect(damaged_rect, canvas.get());
+ Send(new ViewHostMsg_ScrollRect(routing_id_, params));
+ }
- UpdateIME();
-}
+ if (!update.paint_rect.IsEmpty()) {
+ // Normal painting
-void RenderWidget::DoDeferredScroll() {
- if (!webwidget_ || scroll_reply_pending() || scroll_rect_.IsEmpty())
- return;
+ gfx::Rect damaged_rect = update.paint_rect;
- // When we are hidden, we want to suppress scrolling, but we still need to
- // mark this DoDeferredScroll as complete.
- if (is_hidden_ || size_.IsEmpty()) {
- scroll_rect_ = gfx::Rect();
- needs_repainting_on_restore_ = true;
- return;
- }
+ // Compute a buffer for painting and cache it.
+ scoped_ptr<skia::PlatformCanvas> canvas(
+ RenderProcess::current()->GetDrawingCanvas(&current_paint_buf_,
+ damaged_rect));
+ if (!canvas.get()) {
+ NOTREACHED();
+ return;
+ }
- // Layout may generate more invalidation, so we might have to bail on
- // optimized scrolling...
- webwidget_->layout();
+ // We may get back a smaller canvas than we asked for.
+ damaged_rect.set_width(canvas->getDevice()->width());
+ damaged_rect.set_height(canvas->getDevice()->height());
- if (scroll_rect_.IsEmpty())
- return;
+ PaintRect(damaged_rect, canvas.get());
- gfx::Rect damaged_rect;
-
- // Compute the region we will expose by scrolling, and paint that into a
- // shared memory section.
- if (scroll_delta_.x()) {
- int dx = scroll_delta_.x();
- damaged_rect.set_y(scroll_rect_.y());
- damaged_rect.set_height(scroll_rect_.height());
- if (dx > 0) {
- damaged_rect.set_x(scroll_rect_.x());
- damaged_rect.set_width(dx);
- } else {
- damaged_rect.set_x(scroll_rect_.right() + dx);
- damaged_rect.set_width(-dx);
- }
- } else {
- int dy = scroll_delta_.y();
- damaged_rect.set_x(scroll_rect_.x());
- damaged_rect.set_width(scroll_rect_.width());
- if (dy > 0) {
- damaged_rect.set_y(scroll_rect_.y());
- damaged_rect.set_height(dy);
- } else {
- damaged_rect.set_y(scroll_rect_.bottom() + dy);
- damaged_rect.set_height(-dy);
- }
- }
+ ViewHostMsg_PaintRect_Params params;
+ params.bitmap_rect = damaged_rect;
+ params.view_size = size_;
+ params.plugin_window_moves = plugin_window_moves_;
+ params.flags = next_paint_flags_;
+ params.bitmap = current_paint_buf_->id();
- // In case the scroll offset exceeds the width/height of the scroll rect
- damaged_rect = scroll_rect_.Intersect(damaged_rect);
+ plugin_window_moves_.clear();
- skia::PlatformCanvas* canvas =
- RenderProcess::current()->GetDrawingCanvas(&current_scroll_buf_,
- damaged_rect);
- if (!canvas) {
- NOTREACHED();
- return;
+ paint_reply_pending_ = true;
+ Send(new ViewHostMsg_PaintRect(routing_id_, params));
+ next_paint_flags_ = 0;
}
- // We may get back a smaller canvas than we asked for.
- damaged_rect.set_width(canvas->getDevice()->width());
- damaged_rect.set_height(canvas->getDevice()->height());
-
- // Set these parameters before calling Paint, since that could result in
- // further invalidates (uncommon).
- ViewHostMsg_ScrollRect_Params params;
- params.bitmap_rect = damaged_rect;
- params.dx = scroll_delta_.x();
- params.dy = scroll_delta_.y();
- params.clip_rect = scroll_rect_;
- params.view_size = size_;
- params.plugin_window_moves = plugin_window_moves_;
- params.bitmap = current_scroll_buf_->id();
-
- plugin_window_moves_.clear();
-
- // Mark the scroll operation as no longer pending.
- scroll_rect_ = gfx::Rect();
-
- PaintRect(damaged_rect, canvas);
- Send(new ViewHostMsg_ScrollRect(routing_id_, params));
- delete canvas;
UpdateIME();
}
@@ -537,77 +482,61 @@ void RenderWidget::DoDeferredScroll() {
// WebWidgetDelegate
void RenderWidget::didInvalidateRect(const WebRect& rect) {
- // We only want one pending DoDeferredPaint call at any time...
- bool paint_pending = !paint_rect_.IsEmpty();
-
- // If this invalidate overlaps with a pending scroll, then we have to
- // downgrade to invalidating the scroll rect.
- if (gfx::Rect(rect).Intersects(scroll_rect_)) {
- paint_rect_ = paint_rect_.Union(scroll_rect_);
- scroll_rect_ = gfx::Rect();
- }
+ // We only want one pending DoDeferredUpdate call at any time...
+ bool update_pending = paint_aggregator_.HasPendingUpdate();
+ // The invalidated rect might be outside the bounds of the view.
gfx::Rect view_rect(0, 0, size_.width(), size_.height());
- // TODO(iyengar) Investigate why we have painting issues when
- // we ignore invalid regions outside the view.
- // Ignore invalidates that occur outside the bounds of the view
- // TODO(darin): maybe this should move into the paint code?
- // paint_rect_ = view_rect.Intersect(paint_rect_.Union(rect));
- paint_rect_ = paint_rect_.Union(view_rect.Intersect(rect));
-
- if (paint_rect_.IsEmpty() || paint_reply_pending() || paint_pending)
+ gfx::Rect damaged_rect = view_rect.Intersect(rect);
+ if (damaged_rect.IsEmpty())
return;
- // Perform painting asynchronously. This serves two purposes:
+ paint_aggregator_.InvalidateRect(damaged_rect);
+
+ // We may not need to schedule another call to DoDeferredUpdate.
+ if (update_pending)
+ return;
+ if (!paint_aggregator_.HasPendingUpdate())
+ return;
+ if (paint_reply_pending() || scroll_reply_pending())
+ return;
+
+ // Perform updating asynchronously. This serves two purposes:
// 1) Ensures that we call WebView::Paint without a bunch of other junk
// on the call stack.
// 2) Allows us to collect more damage rects before painting to help coalesce
// the work that we will need to do.
MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &RenderWidget::CallDoDeferredPaint));
+ this, &RenderWidget::CallDoDeferredUpdate));
}
void RenderWidget::didScrollRect(int dx, int dy, const WebRect& clip_rect) {
- if (dx != 0 && dy != 0) {
- // We only support scrolling along one axis at a time.
- didScrollRect(0, dy, clip_rect);
- dy = 0;
- }
+ // We only want one pending DoDeferredUpdate call at any time...
+ bool update_pending = paint_aggregator_.HasPendingUpdate();
- bool intersects_with_painting = paint_rect_.Intersects(clip_rect);
-
- // If we already have a pending scroll operation or if this scroll operation
- // intersects the existing paint region, then just failover to invalidating.
- if (!scroll_rect_.IsEmpty() || intersects_with_painting) {
- if (!intersects_with_painting && scroll_rect_ == gfx::Rect(clip_rect)) {
- // OK, we can just update the scroll delta (requires same scrolling axis)
- if (!dx && !scroll_delta_.x()) {
- scroll_delta_.set_y(scroll_delta_.y() + dy);
- return;
- }
- if (!dy && !scroll_delta_.y()) {
- scroll_delta_.set_x(scroll_delta_.x() + dx);
- return;
- }
- }
- didInvalidateRect(scroll_rect_);
- DCHECK(scroll_rect_.IsEmpty());
- didInvalidateRect(clip_rect);
+ // The scrolled rect might be outside the bounds of the view.
+ gfx::Rect view_rect(0, 0, size_.width(), size_.height());
+ gfx::Rect damaged_rect = view_rect.Intersect(clip_rect);
+ if (damaged_rect.IsEmpty())
return;
- }
-
- // We only want one pending DoDeferredScroll call at any time...
- bool scroll_pending = !scroll_rect_.IsEmpty();
- scroll_rect_ = clip_rect;
- scroll_delta_.SetPoint(dx, dy);
+ paint_aggregator_.ScrollRect(dx, dy, damaged_rect);
- if (scroll_pending)
+ // We may not need to schedule another call to DoDeferredUpdate.
+ if (update_pending)
+ return;
+ if (!paint_aggregator_.HasPendingUpdate())
+ return;
+ if (paint_reply_pending() || scroll_reply_pending())
return;
- // Perform scrolling asynchronously since we need to call WebView::Paint
+ // Perform updating asynchronously. This serves two purposes:
+ // 1) Ensures that we call WebView::Paint without a bunch of other junk
+ // on the call stack.
+ // 2) Allows us to collect more damage rects before painting to help coalesce
+ // the work that we will need to do.
MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &RenderWidget::CallDoDeferredScroll));
+ this, &RenderWidget::CallDoDeferredUpdate));
}
void RenderWidget::didChangeCursor(const WebCursorInfo& cursor_info) {
diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h
index df17bd1..31eee61 100644
--- a/chrome/renderer/render_widget.h
+++ b/chrome/renderer/render_widget.h
@@ -14,6 +14,7 @@
#include "base/gfx/size.h"
#include "base/ref_counted.h"
#include "base/shared_memory.h"
+#include "chrome/renderer/paint_aggregator.h"
#include "chrome/renderer/render_process.h"
#include "ipc/ipc_channel.h"
#include "skia/ext/platform_canvas.h"
@@ -120,10 +121,8 @@ class RenderWidget : public IPC::Channel::Listener,
// must ensure that the given rect fits within the bounds of the WebWidget.
void PaintRect(const gfx::Rect& rect, skia::PlatformCanvas* canvas);
- void CallDoDeferredPaint();
- void DoDeferredPaint();
- void CallDoDeferredScroll();
- void DoDeferredScroll();
+ void CallDoDeferredUpdate();
+ void DoDeferredUpdate();
void DoDeferredClose();
void DoDeferredSetWindowRect(const WebKit::WebRect& pos);
@@ -240,20 +239,11 @@ class RenderWidget : public IPC::Channel::Listener,
TransportDIB* current_paint_buf_;
TransportDIB* current_scroll_buf_;
- // The smallest bounding rectangle that needs to be re-painted. This is non-
- // empty if a paint event is pending.
- gfx::Rect paint_rect_;
-
- // The clip rect for the pending scroll event. This is non-empty if a
- // scroll event is pending.
- gfx::Rect scroll_rect_;
+ PaintAggregator paint_aggregator_;
// The area that must be reserved for drawing the resize corner.
gfx::Rect resizer_rect_;
- // The scroll delta for a pending scroll event.
- gfx::Point scroll_delta_;
-
// Flags for the next ViewHostMsg_PaintRect message.
int next_paint_flags_;