summaryrefslogtreecommitdiffstats
path: root/chrome/renderer/render_widget.cc
diff options
context:
space:
mode:
authormad@google.com <mad@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-11 01:08:27 +0000
committermad@google.com <mad@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-11 01:08:27 +0000
commit594321dd8d3c2b9cff721cd4caef682e5d7d65bd (patch)
tree494a29f6d56179cc9b041dedd2b457bfbfd7951e /chrome/renderer/render_widget.cc
parent935a4d59d9122f5c5aaa07b32aad4deccf90178f (diff)
downloadchromium_src-594321dd8d3c2b9cff721cd4caef682e5d7d65bd.zip
chromium_src-594321dd8d3c2b9cff721cd4caef682e5d7d65bd.tar.gz
chromium_src-594321dd8d3c2b9cff721cd4caef682e5d7d65bd.tar.bz2
Relanding reverted patch 18090.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18130 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer/render_widget.cc')
-rw-r--r--chrome/renderer/render_widget.cc170
1 files changed, 141 insertions, 29 deletions
diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc
index 5332d74..fd414ce 100644
--- a/chrome/renderer/render_widget.cc
+++ b/chrome/renderer/render_widget.cc
@@ -32,6 +32,92 @@ using WebKit::WebRect;
using WebKit::WebScreenInfo;
using WebKit::WebSize;
+namespace {
+
+// A helper class to manage access to the RenderWidget::paint_rects vector.
+class PaintRegion {
+ public:
+ explicit PaintRegion(std::vector<gfx::Rect>* rects)
+ : rects_(*rects),
+ coalesce_(false) {
+ }
+
+ gfx::Rect GetBoundingRect() const {
+ gfx::Rect bounding_rect;
+ for (size_t i = 0; i < rects_.size(); ++i)
+ bounding_rect = bounding_rect.Union(rects_[i]);
+ return bounding_rect;
+ }
+
+ void Add(gfx::Rect rect) {
+ if (rect.IsEmpty())
+ return;
+
+ if (rects_.size() >= kNumRectsThreshold) {
+ coalesce_ = true;
+ for (size_t i = rects_.size() - 1; i > 0; --i) {
+ rects_[0] = rects_[0].Union(rects_[i]);
+ rects_.pop_back();
+ }
+ }
+
+ if (coalesce_) {
+ DCHECK(rects_.size() == 1);
+ rects_[0] = rects_[0].Union(rect);
+ } else {
+ // Look for all rects that would intersect with this new
+ // rect, union them and remove them from the list so that we
+ // have only one rect for any pixel in the invalidation.
+ std::vector<gfx::Rect>::iterator it = rects_.begin();
+ while (it != rects_.end()) {
+ if (rect.Intersects(*it)) {
+ rect = rect.Union(*it);
+ rects_.erase(it);
+ // We must go back to the beginning since some rects we previously
+ // checked might now intersects with the union of the two rects,
+ // even though they didn't intersect with them individually.
+ it = rects_.begin();
+ } else {
+ ++it;
+ }
+ }
+ rects_.push_back(rect);
+ }
+ }
+
+ void Clip(const gfx::Rect& clip_rect) {
+ std::vector<gfx::Rect>::iterator it = rects_.begin();
+ while (it != rects_.end()) {
+ if (!clip_rect.Contains(*it))
+ *it = clip_rect.Intersect(*it);
+ ++it;
+ }
+ }
+
+ bool Intersects(const gfx::Rect& rect) const {
+ for (size_t i = 0; i < rects_.size(); ++i) {
+ if (rects_[i].Intersects(rect))
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::vector<gfx::Rect>& rects_;
+ bool coalesce_;
+ static const size_t kNumRectsThreshold;
+};
+// We use a threshold for the number of rects because in many cases as
+// described here:
+// http://my.opera.com/desktopteam/blog/2008/03/28/painting-performance-fixes
+// we get diminishing returns from using more sub-rectangles, it becomes more
+// efficient to Union the rects. The nubmer 25 is also used in WebKit for
+// deferred paints in the FrameView. It was chosen here based on some
+// manual experiments using the benchmark from the article sited above.
+const size_t PaintRegion::kNumRectsThreshold = 25;
+
+} // namespace
+
RenderWidget::RenderWidget(RenderThreadBase* render_thread, bool activatable)
: routing_id_(MSG_ROUTING_NONE),
webwidget_(NULL),
@@ -200,13 +286,14 @@ void RenderWidget::OnResize(const gfx::Size& new_size,
// an ACK if we are resized to a non-empty rect.
webwidget_->Resize(new_size);
if (!new_size.IsEmpty()) {
- DCHECK(!paint_rect_.IsEmpty());
+ DCHECK(!paint_rects_.empty());
// 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());
-
+ DCHECK_GE(PaintRegion(&paint_rects_).GetBoundingRect().width(),
+ new_size.width());
+ DCHECK_GE(PaintRegion(&paint_rects_).GetBoundingRect().height(),
+ new_size.height());
// We will send the Resize_ACK flag once we paint again.
set_next_paint_is_resize_ack();
}
@@ -314,12 +401,12 @@ void RenderWidget::ClearFocus() {
webwidget_->SetFocus(false);
}
-void RenderWidget::PaintRect(const gfx::Rect& rect,
- skia::PlatformCanvas* canvas) {
-
- // Bring the canvas into the coordinate system of the paint rect.
- canvas->translate(static_cast<SkScalar>(-rect.x()),
- static_cast<SkScalar>(-rect.y()));
+void RenderWidget::PaintThisRect(const gfx::Rect& rect,
+ skia::PlatformCanvas* canvas) {
+ // Make sure we don't erase previous rects in the canvas
+ SkRect clip_rect;
+ clip_rect.iset(rect.x(), rect.y(), rect.right(), rect.bottom());
+ canvas->clipRect(clip_rect, SkRegion::kReplace_Op);
// If there is a custom background, tile it.
if (!background_.empty()) {
@@ -333,19 +420,36 @@ void RenderWidget::PaintRect(const gfx::Rect& rect,
}
webwidget_->Paint(canvas, rect);
+}
+
+void RenderWidget::PaintRect(const gfx::Rect& rect,
+ skia::PlatformCanvas* canvas) {
+ // Bring the canvas into the coordinate system of the paint rect.
+ canvas->translate(static_cast<SkScalar>(-rect.x()),
+ static_cast<SkScalar>(-rect.y()));
+ PaintThisRect(rect, canvas);
+
+ // Flush to underlying bitmap. TODO(darin): is this needed?
+ canvas->getTopPlatformDevice().accessBitmap(false);
+}
+
+void RenderWidget::PaintRects(const std::vector<gfx::Rect>& rects,
+ skia::PlatformCanvas* canvas) {
+ for (size_t i = 0; i < rects.size(); ++i)
+ PaintThisRect(rects[i], canvas);
// Flush to underlying bitmap. TODO(darin): is this needed?
canvas->getTopPlatformDevice().accessBitmap(false);
}
void RenderWidget::DoDeferredPaint() {
- if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty())
+ if (!webwidget_ || paint_reply_pending() || paint_rects_.empty())
return;
// When we are hidden, we want to suppress painting, but we still need to
// mark this DoDeferredPaint as complete.
if (is_hidden_ || size_.IsEmpty()) {
- paint_rect_ = gfx::Rect();
+ paint_rects_.clear();
needs_repainting_on_restore_ = true;
return;
}
@@ -355,26 +459,36 @@ void RenderWidget::DoDeferredPaint() {
// OK, save the current paint_rect 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();
+ std::vector<gfx::Rect> paint_rects = paint_rects_;
+ paint_rects_.clear();
// Compute a buffer for painting and cache it.
+ DCHECK(!current_paint_buf_);
+
+ // we use the whole view size as opposed to damaged rect size we have a pool
+ // of paint buffers anyway, and this size has surely been used at least once
+ // to paint the whole view... And it also prevents, somehow, an intermittent
+ // bug we get when painting the sub-rectangles with StretchDIBits on Windows.
+ gfx::Rect bitmap_rect = gfx::Rect(gfx::Point(0, 0), size_);
skia::PlatformCanvas* canvas =
RenderProcess::current()->GetDrawingCanvas(&current_paint_buf_,
- damaged_rect);
+ bitmap_rect);
if (!canvas) {
NOTREACHED();
return;
}
- PaintRect(damaged_rect, canvas);
+ // We must make sure all our paint rects are within the bitmap bounds.
+ PaintRegion(&paint_rects).Clip(bitmap_rect);
+ PaintRects(paint_rects, canvas);
ViewHostMsg_PaintRect_Params params;
- params.bitmap_rect = damaged_rect;
+ params.bitmap = current_paint_buf_->id();
+ params.bitmap_rect = bitmap_rect;
+ params.paint_rects = paint_rects;
params.view_size = size_;
params.plugin_window_moves = plugin_window_moves_;
params.flags = next_paint_flags_;
- params.bitmap = current_paint_buf_->id();
delete canvas;
@@ -477,24 +591,21 @@ gfx::NativeViewId RenderWidget::GetContainingView(WebWidget* webwidget) {
void RenderWidget::DidInvalidateRect(WebWidget* webwidget,
const WebRect& rect) {
// We only want one pending DoDeferredPaint call at any time...
- bool paint_pending = !paint_rect_.IsEmpty();
+ bool paint_pending = !paint_rects_.empty();
// If this invalidate overlaps with a pending scroll, then we have to
// downgrade to invalidating the scroll rect.
+ PaintRegion rect_vector(&paint_rects_);
if (gfx::Rect(rect).Intersects(scroll_rect_)) {
- paint_rect_ = paint_rect_.Union(scroll_rect_);
+ rect_vector.Add(scroll_rect_);
scroll_rect_ = gfx::Rect();
}
- 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));
+ // We don't need to intersect with the view rect here since we have to
+ // do it later on in case the view rect changes between invalidations.
+ rect_vector.Add(rect);
- if (paint_rect_.IsEmpty() || paint_reply_pending() || paint_pending)
+ if (paint_rects_.empty() || paint_reply_pending() || paint_pending)
return;
// Perform painting asynchronously. This serves two purposes:
@@ -514,7 +625,8 @@ void RenderWidget::DidScrollRect(WebWidget* webwidget, int dx, int dy,
dy = 0;
}
- bool intersects_with_painting = paint_rect_.Intersects(clip_rect);
+ bool intersects_with_painting =
+ PaintRegion(&paint_rects_).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.