diff --git a/libcef/browser_webview_mac.mm b/libcef/browser_webview_mac.mm index 5f98b8c4e..f90655696 100644 --- a/libcef/browser_webview_mac.mm +++ b/libcef/browser_webview_mac.mm @@ -49,20 +49,16 @@ - (void)drawRect:(NSRect)rect { #ifndef NDEBUG - CGContextRef context = - reinterpret_cast([[NSGraphicsContext currentContext] - graphicsPort]); - - // start by filling the rect with magenta, so that we can see what's drawn - CGContextSetRGBFillColor (context, 1, 0, 1, 1); + CGContextRef context = reinterpret_cast( + [[NSGraphicsContext currentContext] graphicsPort]); + CGContextSetRGBFillColor(context, 1, 0, 1, 1); CGContextFillRect(context, NSRectToCGRect(rect)); #endif if (browser_ && browser_->UIT_GetWebView()) { - gfx::Rect client_rect(NSRectToCGRect(rect)); - client_rect.set_y(NSHeight([self bounds]) - client_rect.bottom()); - browser_->UIT_GetWebViewHost()->UpdatePaintRect(client_rect); - browser_->UIT_GetWebViewHost()->Paint(); + gfx::Rect dirty_rect = gfx::Rect(NSRectToCGRect(rect)); + dirty_rect.set_y(NSHeight([self bounds]) - dirty_rect.bottom()); + browser_->UIT_GetWebViewHost()->Paint(dirty_rect); } } @@ -183,10 +179,9 @@ - (void)setFrame:(NSRect)frameRect { [super setFrame:frameRect]; if (browser_ && browser_->UIT_GetWebView()) { - browser_->UIT_GetWebViewHost()->Resize( - gfx::Rect(NSRectToCGRect(frameRect))); + const NSRect bounds = [self bounds]; + browser_->UIT_GetWebViewHost()->Resize(gfx::Rect(NSRectToCGRect(bounds))); } - [self setNeedsDisplay:YES]; } - (void)undo:(id)sender { diff --git a/libcef/webwidget_host.cc b/libcef/webwidget_host.cc index 9c9e44fdb..0a332bbf4 100644 --- a/libcef/webwidget_host.cc +++ b/libcef/webwidget_host.cc @@ -26,6 +26,7 @@ void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) { void WebWidgetHost::SetSize(int width, int height) { webwidget_->resize(WebSize(width, height)); + DidInvalidateRect(gfx::Rect(0, 0, width, height)); EnsureTooltip(); } @@ -91,7 +92,7 @@ void WebWidgetHost::DoPaint() { // Perform the paint. UpdatePaintRect(update_rect_); update_rect_ = gfx::Rect(); - Paint(); + Paint(update_rect_); } else { // Try again later. has_update_task_ = true; diff --git a/libcef/webwidget_host.h b/libcef/webwidget_host.h index 177330705..969e6cc8c 100644 --- a/libcef/webwidget_host.h +++ b/libcef/webwidget_host.h @@ -81,7 +81,7 @@ class WebWidgetHost { // expose or WM_PAINT event, we need to update the paint rect. void UpdatePaintRect(const gfx::Rect& rect); - void Paint(); + void Paint(const gfx::Rect& dirty_rect); void InvalidateRect(const gfx::Rect& rect); bool GetImage(int width, int height, void* buffer); @@ -140,12 +140,17 @@ class WebWidgetHost { void OnNotify(WPARAM wparam, NMHDR* header); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); - LRESULT OnImeSetContext(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); - LRESULT OnImeStartComposition(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); - LRESULT OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); - LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnImeSetContext(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + LRESULT OnImeStartComposition(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + LRESULT OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); void OnInputLangChange(DWORD character_set, HKL input_language_id); - void ImeUpdateTextInputState(WebKit::WebTextInputType type, const gfx::Rect& caret_rect); + void ImeUpdateTextInputState(WebKit::WebTextInputType type, + const gfx::Rect& caret_rect); static void UpdateInputMethod(HWND view); #elif defined(OS_MACOSX) // These need to be called from a non-subclass, so they need to be public. diff --git a/libcef/webwidget_host_gtk.cc b/libcef/webwidget_host_gtk.cc index 0211a013d..9589de0a7 100644 --- a/libcef/webwidget_host_gtk.cc +++ b/libcef/webwidget_host_gtk.cc @@ -149,7 +149,7 @@ class WebWidgetHostGtkWidget { g_handling_expose = true; gfx::Rect rect(expose->area); host->UpdatePaintRect(rect); - host->Paint(); + host->Paint(rect); g_handling_expose = false; return FALSE; } @@ -344,7 +344,7 @@ void WebWidgetHost::Resize(const gfx::Size &newsize) { SetSize(newsize.width(), newsize.height()); } -void WebWidgetHost::Paint() { +void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { int width = logical_size_.width(); int height = logical_size_.height(); gfx::Rect client_rect(width, height); diff --git a/libcef/webwidget_host_mac.mm b/libcef/webwidget_host_mac.mm index 1cc24c56d..3b5ec0d9f 100644 --- a/libcef/webwidget_host_mac.mm +++ b/libcef/webwidget_host_mac.mm @@ -46,11 +46,12 @@ WebWidgetHost* WebWidgetHost::Create(NSView* parent_view, void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { const gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); - paint_rect_ = paint_rect_.Union(client_rect.Intersect(damaged_rect)); + const gfx::Rect damaged_rect_in_client = client_rect.Intersect(damaged_rect); - if (!paint_rect_.IsEmpty()) { - NSRect r = NSRectFromCGRect(damaged_rect.ToCGRect()); - r.origin.y = NSHeight([view_ frame]) - NSMaxY(r); + if (!damaged_rect_in_client.IsEmpty()) { + paint_rect_ = paint_rect_.Union(damaged_rect_in_client); + NSRect r = NSRectFromCGRect(damaged_rect_in_client.ToCGRect()); + r.origin.y = NSHeight([view_ bounds]) - NSMaxY(r); [view_ setNeedsDisplayInRect:r]; } } @@ -60,27 +61,29 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { const gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); gfx::Rect rect = clip_rect.Intersect(client_rect); + const int x = rect.x(); const int y = rect.y(); const int r = rect.right(); const int b = rect.bottom(); const int w = rect.width(); const int h = rect.height(); + const int Dx = ABS(dx); + const int Dy = ABS(dy); - // If we're in a state right now in which we cannot draw into the view, just - // mark the scrolling rect as dirty, and it will be completely redrawn instead. - // The Paint() method can end up calling this method indirectly, if the view - // needs to be laid out. Calling scrollRect:by: in this situation leads to + // If we're in a state right now where we cannot draw into the view then just + // mark the scrolling rect as dirty and it will be completely redrawn instead. + // The Paint() method can end up calling this method indirectly if the view + // needs to be laid out; calling scrollRect:by: in this situation leads to // unwanted behavior. Finally, scrolling the rectangle by more than the size // of the view means we can just invalidate the entire scroll rect. - if (![view_ canDraw] || painting_ || layouting_ || ABS(dx) >= w || - ABS(dy) >= h) { + if (![view_ canDraw] || painting_ || layouting_ || Dx >= w || Dy >= h) { DidInvalidateRect(clip_rect); return; } - // The scrolling rect must not scroll outside the clip rect, because that will - // clobber the scrollbars; so the rectangle is shortened a bit from the + // The scrolling rect must not scroll outside the clip rect because that will + // clobber the scrollbars. As a result we shorten the rectangle a bit from the // leading side of the scroll (could be either horizontally or vertically). if (dx > 0) rect = gfx::Rect(x, y, w - dx, h); @@ -88,14 +91,15 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { rect = gfx::Rect(x - dx, y, w + dx, h); else if (dy > 0) rect = gfx::Rect(x, y, w, h - dy); - else if (dy < 0) + else if (dy < 0) rect = gfx::Rect(x, y - dy, w, h + dy); - // Convert scroll rectangle to the view's coordinate system, and perform the - // scroll directly, without invalidating the view. In theory this could cause - // some kind of performance issue, since we're not coalescing redraw events, - // but in practice we get much smoother scrolling of big views, since just - // copying the pixels within the window is much faster than redrawing them. + // Convert the scroll rectangle to the view's coordinate system and perform + // the scroll directly without invalidating the view. In theory this could + // cause some kind of performance issue since we're not coalescing redraw + // events. In practice, however, we get much smoother scrolling of big views + // since just copying the pixels within the window is much faster than + // redrawing them. NSRect cocoa_rect = NSRectFromCGRect(rect.ToCGRect()); cocoa_rect.origin.y = NSHeight([view_ bounds]) - NSMaxY(cocoa_rect); [view_ scrollRect:cocoa_rect by:NSMakeSize(dx, -dy)]; @@ -105,10 +109,10 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { return; // Repaint the rectangle that was revealed when scrolling the given rectangle. - // We don't want to invalidate the rectangle, because that would cause the - // invalidated area to be L-shaped, because of this narrow area, and the - // scrollbar area that also could be invalidated, and the union of those two - // rectangles is pretty much the entire view area, and we would not save any + // We don't want to invalidate this rectangle because that would cause the + // invalidated area to be L-shaped due to the combination of this narrow area + // and the scrollbar area that may also be invalidated. The union of these two + // rectangles is pretty much the entire view area and we would not save any // work by doing the scrollRect: call above. if (dx > 0) paint_rect_ = gfx::Rect(x, y, dx, h); @@ -121,9 +125,9 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { paint_rect_ = paint_rect_.Intersect(client_rect); if (!paint_rect_.IsEmpty()) - Paint(); + Paint(paint_rect_); - // If any part of the scrolled rect was marked as dirty, make sure to redraw + // If any part of the scrolled rect was marked as dirty make sure to redraw // it in the new scrolled-to location. Otherwise we can end up with artifacts // for overlapping elements. gfx::Rect moved_paint_rect = saved_paint_rect.Intersect(clip_rect); @@ -131,7 +135,8 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { moved_paint_rect.Offset(dx, dy); paint_rect_ = moved_paint_rect; paint_rect_ = paint_rect_.Intersect(client_rect); - if (!paint_rect_.IsEmpty()) Paint(); + if (!paint_rect_.IsEmpty()) + Paint(paint_rect_); } [view_ unlockFocus]; @@ -160,33 +165,37 @@ WebWidgetHost::WebWidgetHost() WebWidgetHost::~WebWidgetHost() { } -void WebWidgetHost::Paint() { +void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); - gfx::Rect update_rect; - - // When we are not using accelerated compositing, the canvas area is allowed - // to differ in size from the client with a certain number of pixels (128 in - // this case). When accelerated compositing is in effect, the size must match - // exactly. - if (!webwidget_->isAcceleratedCompositingActive()) { - const int kCanvasGrowSize = 128; + gfx::Rect copy_rect = dirty_rect; - if (!canvas_.get() || - canvas_w_ < client_rect.width() || - canvas_h_ < client_rect.height() || - canvas_w_ > client_rect.width() + kCanvasGrowSize * 2 || - canvas_h_ > client_rect.height() + kCanvasGrowSize * 2) { - canvas_w_ = client_rect.width() + kCanvasGrowSize; - canvas_h_ = client_rect.height() + kCanvasGrowSize; - canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); - paint_rect_ = client_rect; - } - } else if(!canvas_.get() || canvas_w_ != client_rect.width() || - canvas_h_ != client_rect.height()) { - canvas_w_ = client_rect.width(); - canvas_h_ = client_rect.height(); + // Union the rectangle that WebKit think needs repainting with the rectangle + // of the view that must be painted now. In most situations this will not + // affect the painted rectangle. In some situations we could end up re- + // painting areas that are already in the canvas and only dirty in the view + // itself. However, if we don't do this we can get artifacts when scrolling + // because contents of the canvas are no longer correct after scrolling only + // in the view. + paint_rect_ = paint_rect_.Union(dirty_rect); + + // When we are not using accelerated compositing the canvas area is allowed + // to differ in size from the client by a certain number of pixels (128 in + // this case). When accelerated compositing is in effect the size must match + // exactly. + const int extra_w = (webwidget_->isAcceleratedCompositingActive()? 0: 128); + const int extra_h = (webwidget_->isAcceleratedCompositingActive()? 0: 128); + const int min_w = client_rect.width(); + const int min_h = client_rect.height(); + const int max_w = client_rect.width() + extra_w * 2; + const int max_h = client_rect.height() + extra_h * 2; + + const bool too_small = (canvas_w_ < min_w || canvas_h_ < min_h); + const bool too_large = (canvas_w_ > max_w || canvas_h_ > max_h); + + if (!canvas_.get() || too_small || too_large) { + canvas_w_ = client_rect.width() + extra_w; + canvas_h_ = client_rect.height() + extra_h; canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); - paint_rect_ = client_rect; } // Animate the view and layout any views that have not been laid out yet. The @@ -199,35 +208,37 @@ void WebWidgetHost::Paint() { layouting_ = false; // Draw into the graphics context of the canvas instead of the view's context. - // The view's context is pushed onto the context stack, to be restored below. + // The view's context is pushed onto the context stack to be restored below. CGContextRef bitmap = skia::GetBitmapContext(skia::GetTopDevice(*canvas_)); NSGraphicsContext* paint_context = [NSGraphicsContext graphicsContextWithGraphicsPort:bitmap flipped:YES]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:paint_context]; - // Paint the canvas if necessary. Allow painting to generate extra rects the - // first time we call it. This is necessary because some WebCore rendering - // objects update their layout only when painted. - for (int i = 0; i < 2; ++i) { - paint_rect_ = client_rect.Intersect(paint_rect_); - if (!paint_rect_.IsEmpty()) { - gfx::Rect rect(paint_rect_); - update_rect = (i == 0? rect: update_rect.Union(rect)); - paint_rect_ = gfx::Rect(); - PaintRect(rect); - } + // Paint the canvas if necessary. The painting operation can cause additional + // rectangles to be invalidated because some elements are laid out only the + // first time they are painted. + paint_rect_ = client_rect.Intersect(paint_rect_); + const gfx::Rect paint_rect = paint_rect_; + paint_rect_ = gfx::Rect(); + PaintRect(paint_rect); + + if (!paint_rect_.IsEmpty()) { + copy_rect = copy_rect.Union(paint_rect_); + const gfx::Rect paint_rect = paint_rect_; + paint_rect_ = gfx::Rect(); + PaintRect(paint_rect); } // Set the context back to our view and copy the bitmap that we just painted - // into to the view. Only the region that was update is copied. + // into to the view. Only the region that was updated is copied. [NSGraphicsContext restoreGraphicsState]; NSGraphicsContext* view_context = [NSGraphicsContext currentContext]; CGContextRef context = static_cast([view_context graphicsPort]); - CGRect bitmap_rect = { { update_rect.x(), update_rect.y() }, - { update_rect.width(), update_rect.height() } }; - skia::DrawToNativeContext(canvas_.get(), context, update_rect.x(), - client_rect.height() - update_rect.bottom(), &bitmap_rect); + CGRect bitmap_rect = { { copy_rect.x(), copy_rect.y() }, + { copy_rect.width(), copy_rect.height() } }; + skia::DrawToNativeContext(canvas_.get(), context, copy_rect.x(), + client_rect.height() - copy_rect.bottom(), &bitmap_rect); } void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) @@ -296,9 +307,11 @@ void WebWidgetHost::PaintRect(const gfx::Rect& rect) { #endif DCHECK(canvas_.get()); - set_painting(true); - webwidget_->paint(webkit_glue::ToWebCanvas(canvas_.get()), rect); - set_painting(false); + if (!rect.IsEmpty()) { + set_painting(true); + webwidget_->paint(webkit_glue::ToWebCanvas(canvas_.get()), rect); + set_painting(false); + } } void WebWidgetHost::SendKeyEvent(cef_key_type_t type, int key, int modifiers, diff --git a/libcef/webwidget_host_win.cc b/libcef/webwidget_host_win.cc index a7521ae4f..713bbc4f0 100644 --- a/libcef/webwidget_host_win.cc +++ b/libcef/webwidget_host_win.cc @@ -119,10 +119,9 @@ LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam, case WM_PAINT: { // Paint to the window. RECT rect; - if (GetUpdateRect(hwnd, &rect, FALSE)) { + if (GetUpdateRect(hwnd, &rect, FALSE)) host->UpdatePaintRect(gfx::Rect(rect)); - } - host->Paint(); + host->Paint(gfx::Rect(rect)); return 0; } @@ -360,7 +359,7 @@ bool WebWidgetHost::WndProc(UINT message, WPARAM wparam, LPARAM lparam) { return false; } -void WebWidgetHost::Paint() { +void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { if (canvas_.get() && paint_rect_.IsEmpty()) return;