Mac: additional repaint fixes related to issue #360.

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@364 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2011-11-08 16:19:03 +00:00
parent 8257177763
commit 09bf0b039f
6 changed files with 108 additions and 95 deletions

View File

@ -49,20 +49,16 @@
- (void)drawRect:(NSRect)rect { - (void)drawRect:(NSRect)rect {
#ifndef NDEBUG #ifndef NDEBUG
CGContextRef context = CGContextRef context = reinterpret_cast<CGContextRef>(
reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] [[NSGraphicsContext currentContext] graphicsPort]);
graphicsPort]);
// start by filling the rect with magenta, so that we can see what's drawn
CGContextSetRGBFillColor(context, 1, 0, 1, 1); CGContextSetRGBFillColor(context, 1, 0, 1, 1);
CGContextFillRect(context, NSRectToCGRect(rect)); CGContextFillRect(context, NSRectToCGRect(rect));
#endif #endif
if (browser_ && browser_->UIT_GetWebView()) { if (browser_ && browser_->UIT_GetWebView()) {
gfx::Rect client_rect(NSRectToCGRect(rect)); gfx::Rect dirty_rect = gfx::Rect(NSRectToCGRect(rect));
client_rect.set_y(NSHeight([self bounds]) - client_rect.bottom()); dirty_rect.set_y(NSHeight([self bounds]) - dirty_rect.bottom());
browser_->UIT_GetWebViewHost()->UpdatePaintRect(client_rect); browser_->UIT_GetWebViewHost()->Paint(dirty_rect);
browser_->UIT_GetWebViewHost()->Paint();
} }
} }
@ -183,10 +179,9 @@
- (void)setFrame:(NSRect)frameRect { - (void)setFrame:(NSRect)frameRect {
[super setFrame:frameRect]; [super setFrame:frameRect];
if (browser_ && browser_->UIT_GetWebView()) { if (browser_ && browser_->UIT_GetWebView()) {
browser_->UIT_GetWebViewHost()->Resize( const NSRect bounds = [self bounds];
gfx::Rect(NSRectToCGRect(frameRect))); browser_->UIT_GetWebViewHost()->Resize(gfx::Rect(NSRectToCGRect(bounds)));
} }
[self setNeedsDisplay:YES];
} }
- (void)undo:(id)sender { - (void)undo:(id)sender {

View File

@ -26,6 +26,7 @@ void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) {
void WebWidgetHost::SetSize(int width, int height) { void WebWidgetHost::SetSize(int width, int height) {
webwidget_->resize(WebSize(width, height)); webwidget_->resize(WebSize(width, height));
DidInvalidateRect(gfx::Rect(0, 0, width, height));
EnsureTooltip(); EnsureTooltip();
} }
@ -91,7 +92,7 @@ void WebWidgetHost::DoPaint() {
// Perform the paint. // Perform the paint.
UpdatePaintRect(update_rect_); UpdatePaintRect(update_rect_);
update_rect_ = gfx::Rect(); update_rect_ = gfx::Rect();
Paint(); Paint(update_rect_);
} else { } else {
// Try again later. // Try again later.
has_update_task_ = true; has_update_task_ = true;

View File

@ -81,7 +81,7 @@ class WebWidgetHost {
// expose or WM_PAINT event, we need to update the paint rect. // expose or WM_PAINT event, we need to update the paint rect.
void UpdatePaintRect(const gfx::Rect& rect); void UpdatePaintRect(const gfx::Rect& rect);
void Paint(); void Paint(const gfx::Rect& dirty_rect);
void InvalidateRect(const gfx::Rect& rect); void InvalidateRect(const gfx::Rect& rect);
bool GetImage(int width, int height, void* buffer); bool GetImage(int width, int height, void* buffer);
@ -140,12 +140,17 @@ class WebWidgetHost {
void OnNotify(WPARAM wparam, NMHDR* header); void OnNotify(WPARAM wparam, NMHDR* header);
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT OnImeSetContext(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); LRESULT OnImeSetContext(UINT message, WPARAM wparam, LPARAM lparam,
LRESULT OnImeStartComposition(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); BOOL& handled);
LRESULT OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); LRESULT OnImeStartComposition(UINT message, WPARAM wparam, LPARAM lparam,
LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); 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 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); static void UpdateInputMethod(HWND view);
#elif defined(OS_MACOSX) #elif defined(OS_MACOSX)
// These need to be called from a non-subclass, so they need to be public. // These need to be called from a non-subclass, so they need to be public.

View File

@ -149,7 +149,7 @@ class WebWidgetHostGtkWidget {
g_handling_expose = true; g_handling_expose = true;
gfx::Rect rect(expose->area); gfx::Rect rect(expose->area);
host->UpdatePaintRect(rect); host->UpdatePaintRect(rect);
host->Paint(); host->Paint(rect);
g_handling_expose = false; g_handling_expose = false;
return FALSE; return FALSE;
} }
@ -344,7 +344,7 @@ void WebWidgetHost::Resize(const gfx::Size &newsize) {
SetSize(newsize.width(), newsize.height()); SetSize(newsize.width(), newsize.height());
} }
void WebWidgetHost::Paint() { void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) {
int width = logical_size_.width(); int width = logical_size_.width();
int height = logical_size_.height(); int height = logical_size_.height();
gfx::Rect client_rect(width, height); gfx::Rect client_rect(width, height);

View File

@ -46,11 +46,12 @@ WebWidgetHost* WebWidgetHost::Create(NSView* parent_view,
void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) {
const gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); 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()) { if (!damaged_rect_in_client.IsEmpty()) {
NSRect r = NSRectFromCGRect(damaged_rect.ToCGRect()); paint_rect_ = paint_rect_.Union(damaged_rect_in_client);
r.origin.y = NSHeight([view_ frame]) - NSMaxY(r); NSRect r = NSRectFromCGRect(damaged_rect_in_client.ToCGRect());
r.origin.y = NSHeight([view_ bounds]) - NSMaxY(r);
[view_ setNeedsDisplayInRect: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])); const gfx::Rect client_rect(NSRectToCGRect([view_ bounds]));
gfx::Rect rect = clip_rect.Intersect(client_rect); gfx::Rect rect = clip_rect.Intersect(client_rect);
const int x = rect.x(); const int x = rect.x();
const int y = rect.y(); const int y = rect.y();
const int r = rect.right(); const int r = rect.right();
const int b = rect.bottom(); const int b = rect.bottom();
const int w = rect.width(); const int w = rect.width();
const int h = rect.height(); 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 // 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. // 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 // 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 // needs to be laid out; calling scrollRect:by: in this situation leads to
// unwanted behavior. Finally, scrolling the rectangle by more than the size // unwanted behavior. Finally, scrolling the rectangle by more than the size
// of the view means we can just invalidate the entire scroll rect. // of the view means we can just invalidate the entire scroll rect.
if (![view_ canDraw] || painting_ || layouting_ || ABS(dx) >= w || if (![view_ canDraw] || painting_ || layouting_ || Dx >= w || Dy >= h) {
ABS(dy) >= h) {
DidInvalidateRect(clip_rect); DidInvalidateRect(clip_rect);
return; return;
} }
// The scrolling rect must not scroll outside the clip rect, because that will // 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 // 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). // leading side of the scroll (could be either horizontally or vertically).
if (dx > 0) if (dx > 0)
rect = gfx::Rect(x, y, w - dx, h); rect = gfx::Rect(x, y, w - dx, h);
@ -91,11 +94,12 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
else if (dy < 0) else if (dy < 0)
rect = gfx::Rect(x, y - dy, w, h + dy); rect = gfx::Rect(x, y - dy, w, h + dy);
// Convert scroll rectangle to the view's coordinate system, and perform the // Convert the scroll rectangle to the view's coordinate system and perform
// scroll directly, without invalidating the view. In theory this could cause // the scroll directly without invalidating the view. In theory this could
// some kind of performance issue, since we're not coalescing redraw events, // cause some kind of performance issue since we're not coalescing redraw
// but in practice we get much smoother scrolling of big views, since just // events. In practice, however, we get much smoother scrolling of big views
// copying the pixels within the window is much faster than redrawing them. // since just copying the pixels within the window is much faster than
// redrawing them.
NSRect cocoa_rect = NSRectFromCGRect(rect.ToCGRect()); NSRect cocoa_rect = NSRectFromCGRect(rect.ToCGRect());
cocoa_rect.origin.y = NSHeight([view_ bounds]) - NSMaxY(cocoa_rect); cocoa_rect.origin.y = NSHeight([view_ bounds]) - NSMaxY(cocoa_rect);
[view_ scrollRect:cocoa_rect by:NSMakeSize(dx, -dy)]; [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; return;
// Repaint the rectangle that was revealed when scrolling the given rectangle. // Repaint the rectangle that was revealed when scrolling the given rectangle.
// We don't want to invalidate the rectangle, because that would cause the // We don't want to invalidate this rectangle because that would cause the
// invalidated area to be L-shaped, because of this narrow area, and the // invalidated area to be L-shaped due to the combination of this narrow area
// scrollbar area that also could be invalidated, and the union of those two // 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 // rectangles is pretty much the entire view area and we would not save any
// work by doing the scrollRect: call above. // work by doing the scrollRect: call above.
if (dx > 0) if (dx > 0)
paint_rect_ = gfx::Rect(x, y, dx, h); 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); paint_rect_ = paint_rect_.Intersect(client_rect);
if (!paint_rect_.IsEmpty()) 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 // it in the new scrolled-to location. Otherwise we can end up with artifacts
// for overlapping elements. // for overlapping elements.
gfx::Rect moved_paint_rect = saved_paint_rect.Intersect(clip_rect); 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); moved_paint_rect.Offset(dx, dy);
paint_rect_ = moved_paint_rect; paint_rect_ = moved_paint_rect;
paint_rect_ = paint_rect_.Intersect(client_rect); paint_rect_ = paint_rect_.Intersect(client_rect);
if (!paint_rect_.IsEmpty()) Paint(); if (!paint_rect_.IsEmpty())
Paint(paint_rect_);
} }
[view_ unlockFocus]; [view_ unlockFocus];
@ -160,33 +165,37 @@ WebWidgetHost::WebWidgetHost()
WebWidgetHost::~WebWidgetHost() { WebWidgetHost::~WebWidgetHost() {
} }
void WebWidgetHost::Paint() { void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) {
gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); gfx::Rect client_rect(NSRectToCGRect([view_ bounds]));
gfx::Rect update_rect; gfx::Rect copy_rect = dirty_rect;
// When we are not using accelerated compositing, the canvas area is allowed // Union the rectangle that WebKit think needs repainting with the rectangle
// to differ in size from the client with a certain number of pixels (128 in // of the view that must be painted now. In most situations this will not
// this case). When accelerated compositing is in effect, the size must match // 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. // exactly.
if (!webwidget_->isAcceleratedCompositingActive()) { const int extra_w = (webwidget_->isAcceleratedCompositingActive()? 0: 128);
const int kCanvasGrowSize = 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;
if (!canvas_.get() || const bool too_small = (canvas_w_ < min_w || canvas_h_ < min_h);
canvas_w_ < client_rect.width() || const bool too_large = (canvas_w_ > max_w || canvas_h_ > max_h);
canvas_h_ < client_rect.height() ||
canvas_w_ > client_rect.width() + kCanvasGrowSize * 2 || if (!canvas_.get() || too_small || too_large) {
canvas_h_ > client_rect.height() + kCanvasGrowSize * 2) { canvas_w_ = client_rect.width() + extra_w;
canvas_w_ = client_rect.width() + kCanvasGrowSize; canvas_h_ = client_rect.height() + extra_h;
canvas_h_ = client_rect.height() + kCanvasGrowSize;
canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); 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();
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 // Animate the view and layout any views that have not been laid out yet. The
@ -199,35 +208,37 @@ void WebWidgetHost::Paint() {
layouting_ = false; layouting_ = false;
// Draw into the graphics context of the canvas instead of the view's context. // 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_)); CGContextRef bitmap = skia::GetBitmapContext(skia::GetTopDevice(*canvas_));
NSGraphicsContext* paint_context = NSGraphicsContext* paint_context =
[NSGraphicsContext graphicsContextWithGraphicsPort:bitmap flipped:YES]; [NSGraphicsContext graphicsContextWithGraphicsPort:bitmap flipped:YES];
[NSGraphicsContext saveGraphicsState]; [NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:paint_context]; [NSGraphicsContext setCurrentContext:paint_context];
// Paint the canvas if necessary. Allow painting to generate extra rects the // Paint the canvas if necessary. The painting operation can cause additional
// first time we call it. This is necessary because some WebCore rendering // rectangles to be invalidated because some elements are laid out only the
// objects update their layout only when painted. // first time they are painted.
for (int i = 0; i < 2; ++i) {
paint_rect_ = client_rect.Intersect(paint_rect_); paint_rect_ = client_rect.Intersect(paint_rect_);
if (!paint_rect_.IsEmpty()) { const gfx::Rect paint_rect = paint_rect_;
gfx::Rect rect(paint_rect_);
update_rect = (i == 0? rect: update_rect.Union(rect));
paint_rect_ = gfx::Rect(); paint_rect_ = gfx::Rect();
PaintRect(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 // 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 restoreGraphicsState];
NSGraphicsContext* view_context = [NSGraphicsContext currentContext]; NSGraphicsContext* view_context = [NSGraphicsContext currentContext];
CGContextRef context = static_cast<CGContextRef>([view_context graphicsPort]); CGContextRef context = static_cast<CGContextRef>([view_context graphicsPort]);
CGRect bitmap_rect = { { update_rect.x(), update_rect.y() }, CGRect bitmap_rect = { { copy_rect.x(), copy_rect.y() },
{ update_rect.width(), update_rect.height() } }; { copy_rect.width(), copy_rect.height() } };
skia::DrawToNativeContext(canvas_.get(), context, update_rect.x(), skia::DrawToNativeContext(canvas_.get(), context, copy_rect.x(),
client_rect.height() - update_rect.bottom(), &bitmap_rect); client_rect.height() - copy_rect.bottom(), &bitmap_rect);
} }
void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) void WebWidgetHost::SetTooltipText(const CefString& tooltip_text)
@ -296,10 +307,12 @@ void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
#endif #endif
DCHECK(canvas_.get()); DCHECK(canvas_.get());
if (!rect.IsEmpty()) {
set_painting(true); set_painting(true);
webwidget_->paint(webkit_glue::ToWebCanvas(canvas_.get()), rect); webwidget_->paint(webkit_glue::ToWebCanvas(canvas_.get()), rect);
set_painting(false); set_painting(false);
} }
}
void WebWidgetHost::SendKeyEvent(cef_key_type_t type, int key, int modifiers, void WebWidgetHost::SendKeyEvent(cef_key_type_t type, int key, int modifiers,
bool sysChar, bool imeChar) bool sysChar, bool imeChar)

View File

@ -119,10 +119,9 @@ LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam,
case WM_PAINT: { case WM_PAINT: {
// Paint to the window. // Paint to the window.
RECT rect; RECT rect;
if (GetUpdateRect(hwnd, &rect, FALSE)) { if (GetUpdateRect(hwnd, &rect, FALSE))
host->UpdatePaintRect(gfx::Rect(rect)); host->UpdatePaintRect(gfx::Rect(rect));
} host->Paint(gfx::Rect(rect));
host->Paint();
return 0; return 0;
} }
@ -360,7 +359,7 @@ bool WebWidgetHost::WndProc(UINT message, WPARAM wparam, LPARAM lparam) {
return false; return false;
} }
void WebWidgetHost::Paint() { void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) {
if (canvas_.get() && paint_rect_.IsEmpty()) if (canvas_.get() && paint_rect_.IsEmpty())
return; return;