From 587eb7871de2181db94244aaf6df088a81680553 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Wed, 9 Nov 2011 20:09:24 +0000 Subject: [PATCH] - Win: Improve redraw and scrolling performance (issue #360). - Win: Fix CefBrowser::GetImage() and add "Get Image" example to cefclient (issue #377). - Pass the list of dirty rectangles to CefRenderHandler::Paint(). (issue #415). git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@368 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- include/cef.h | 9 +- include/cef_capi.h | 9 +- libcef/browser_impl.cc | 20 ++- libcef/browser_impl.h | 4 +- libcef/webwidget_host.cc | 20 ++- libcef/webwidget_host.h | 31 ++-- libcef/webwidget_host_gtk.cc | 2 - libcef/webwidget_host_win.cc | 198 ++++++++++++--------- libcef_dll/cpptoc/render_handler_cpptoc.cc | 14 +- libcef_dll/ctocpp/render_handler_ctocpp.cc | 18 +- libcef_dll/ctocpp/render_handler_ctocpp.h | 2 +- tests/cefclient/cefclient.h | 1 + tests/cefclient/cefclient.rc | 1 + tests/cefclient/cefclient_win.cpp | 115 ++++++++++++ tests/cefclient/osrplugin.cpp | 2 +- tests/cefclient/resource.h | 1 + tools/cef_parser.py | 5 +- 17 files changed, 313 insertions(+), 139 deletions(-) diff --git a/include/cef.h b/include/cef.h index 890321d6a..e2092db01 100644 --- a/include/cef.h +++ b/include/cef.h @@ -1602,6 +1602,7 @@ class CefRenderHandler : public virtual CefBase { public: typedef cef_paint_element_type_t PaintElementType; + typedef std::vector RectList; /// // Called to retrieve the view rectangle which is relative to screen @@ -1649,14 +1650,14 @@ public: /// // Called when an element should be painted. |type| indicates whether the // element is the view or the popup widget. |buffer| contains the pixel data - // for the whole image. |dirtyRect| indicates the portion of the image that - // has been repainted. On Windows |buffer| will be width*height*4 bytes in - // size and represents a BGRA image with an upper-left origin. + // for the whole image. |dirtyRects| contains the set of rectangles that need + // to be repainted. On Windows |buffer| will be width*height*4 bytes in size + // and represents a BGRA image with an upper-left origin. /// /*--cef()--*/ virtual void OnPaint(CefRefPtr browser, PaintElementType type, - const CefRect& dirtyRect, + const RectList& dirtyRects, const void* buffer) {} /// diff --git a/include/cef_capi.h b/include/cef_capi.h index a4866706d..e865fb731 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -1428,13 +1428,14 @@ typedef struct _cef_render_handler_t /// // Called when an element should be painted. |type| indicates whether the // element is the view or the popup widget. |buffer| contains the pixel data - // for the whole image. |dirtyRect| indicates the portion of the image that - // has been repainted. On Windows |buffer| will be width*height*4 bytes in - // size and represents a BGRA image with an upper-left origin. + // for the whole image. |dirtyRects| contains the set of rectangles that need + // to be repainted. On Windows |buffer| will be width*height*4 bytes in size + // and represents a BGRA image with an upper-left origin. /// void (CEF_CALLBACK *on_paint)(struct _cef_render_handler_t* self, struct _cef_browser_t* browser, enum cef_paint_element_type_t type, - const cef_rect_t* dirtyRect, const void* buffer); + size_t dirtyRectCount, cef_rect_t const* dirtyRects, + const void* buffer); /// // Called when the browser window's cursor has changed. diff --git a/libcef/browser_impl.cc b/libcef/browser_impl.cc index 46d4ba382..34708948a 100644 --- a/libcef/browser_impl.cc +++ b/libcef/browser_impl.cc @@ -86,9 +86,8 @@ CefBrowserImpl::PaintDelegate::~PaintDelegate() { } -void CefBrowserImpl::PaintDelegate::Paint(bool popup, - const gfx::Rect& dirtyRect, - const void* buffer) +void CefBrowserImpl::PaintDelegate::Paint( + bool popup, const std::vector& dirtyRects, const void* buffer) { CefRefPtr client = browser_->GetClient(); if (!client.get()) @@ -97,9 +96,7 @@ void CefBrowserImpl::PaintDelegate::Paint(bool popup, if (!handler.get()) return; - CefRect rect(dirtyRect.x(), dirtyRect.y(), dirtyRect.width(), - dirtyRect.height()); - handler->OnPaint(browser_, (popup?PET_POPUP:PET_VIEW), rect, buffer); + handler->OnPaint(browser_, (popup?PET_POPUP:PET_VIEW), dirtyRects, buffer); } @@ -1024,8 +1021,15 @@ void CefBrowserImpl::UIT_Invalidate(const CefRect& dirtyRect) REQUIRE_UIT(); WebViewHost* host = UIT_GetWebViewHost(); if (host) { - host->InvalidateRect(gfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, - dirtyRect.height)); + gfx::Rect rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, + dirtyRect.height); + + // Used when window rendering is disabled to send the specified region to + // the paint delegate when WebWidget::Paint() is next called. + host->UpdateRedrawRect(rect); + + // Cause WebWidget::Paint() to be called when next appropriate. + host->InvalidateRect(rect); } } diff --git a/libcef/browser_impl.h b/libcef/browser_impl.h index c58fd2122..74b925827 100644 --- a/libcef/browser_impl.h +++ b/libcef/browser_impl.h @@ -42,8 +42,8 @@ public: PaintDelegate(CefBrowserImpl* browser); virtual ~PaintDelegate(); - virtual void Paint(bool popup, const gfx::Rect& dirtyRect, - const void* buffer); + virtual void Paint(bool popup, const std::vector& dirtyRects, + const void* buffer) OVERRIDE; protected: CefBrowserImpl* browser_; diff --git a/libcef/webwidget_host.cc b/libcef/webwidget_host.cc index 0a332bbf4..d3876b01d 100644 --- a/libcef/webwidget_host.cc +++ b/libcef/webwidget_host.cc @@ -21,7 +21,18 @@ void WebWidgetHost::ScheduleAnimation() { } void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) { +#if defined(OS_WIN) + paint_rgn_.op(rect.x(), rect.y(), rect.right(), rect.bottom(), + SkRegion::kUnion_Op); +#else + // TODO: Update all ports to use regions instead of rectangles. paint_rect_ = paint_rect_.Union(rect); +#endif +} + +void WebWidgetHost::UpdateRedrawRect(const gfx::Rect& rect) { + if (!view_) + redraw_rect_ = redraw_rect_.Union(rect); } void WebWidgetHost::SetSize(int width, int height) { @@ -80,19 +91,14 @@ gfx::PluginWindowHandle WebWidgetHost::GetWindowedPluginAt(int x, int y) void WebWidgetHost::DoPaint() { has_update_task_ = false; - if (update_rect_.IsEmpty()) - return; - // TODO(cef): The below code is cross-platform but the IsIdle() method // currently requires patches to Chromium. Since this code is only executed // on Windows it's been stuck behind an #ifdef for now to avoid having to // patch Chromium code on other platforms. #if defined(OS_WIN) if (MessageLoop::current()->IsIdle()) { - // Perform the paint. - UpdatePaintRect(update_rect_); - update_rect_ = gfx::Rect(); - Paint(update_rect_); + // Paint to the delegate. The rect argument is unused. + Paint(gfx::Rect()); } else { // Try again later. has_update_task_ = true; diff --git a/libcef/webwidget_host.h b/libcef/webwidget_host.h index 969e6cc8c..2c8aa5c71 100644 --- a/libcef/webwidget_host.h +++ b/libcef/webwidget_host.h @@ -7,6 +7,7 @@ #include "include/internal/cef_string.h" #include "include/internal/cef_types.h" +#include "include/internal/cef_types_wrappers.h" #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -15,6 +16,7 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebTextInputType.h" +#include "third_party/skia/include/core/SkRegion.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "webkit/plugins/npapi/webplugin.h" @@ -53,7 +55,7 @@ class WebWidgetHost { public: class PaintDelegate { public: - virtual void Paint(bool popup, const gfx::Rect& dirtyRect, + virtual void Paint(bool popup, const std::vector& dirtyRects, const void* buffer) =0; }; @@ -77,10 +79,14 @@ class WebWidgetHost { void SetCursor(HCURSOR cursor); #endif - // Allow clients to update the paint rect. For example, if we get a gdk - // expose or WM_PAINT event, we need to update the paint rect. + // Update the region that will be painted to the canvas by WebKit the next + // time that Paint() is called. void UpdatePaintRect(const gfx::Rect& rect); + // Update the region that will be drawn to the device the next time Paint() + // is called. This is only used when window rendering is disabled. + void UpdateRedrawRect(const gfx::Rect& rect); + void Paint(const gfx::Rect& dirty_rect); void InvalidateRect(const gfx::Rect& rect); @@ -183,8 +189,6 @@ class WebWidgetHost { void TrackMouseLeave(bool enable); #endif - void ResetScrollRect(); - void set_painting(bool value) { painting_ = value; } @@ -207,18 +211,19 @@ class WebWidgetHost { bool popup_; // Specifies the portion of the webwidget that needs painting. + // TODO: Update all ports to use regions instead of rectangles. +#if defined(OS_WIN) + SkRegion paint_rgn_; +#else gfx::Rect paint_rect_; +#endif - // Specifies the portion of the webwidget that needs scrolling. - gfx::Rect scroll_rect_; - int scroll_dx_; - int scroll_dy_; - - // Specifies the portion of the webwidget that has been invalidated when - // window rendering is disabled. - gfx::Rect update_rect_; + // True if an update task is pending when window rendering is disabled. bool has_update_task_; + // Redraw rectangle requested by an explicit call to CefBrowser::Invalidate(). + gfx::Rect redraw_rect_; + // The map of windowed plugins that need to be drawn when window rendering is // disabled. typedef std::map diff --git a/libcef/webwidget_host_gtk.cc b/libcef/webwidget_host_gtk.cc index 9589de0a7..277857f7b 100644 --- a/libcef/webwidget_host_gtk.cc +++ b/libcef/webwidget_host_gtk.cc @@ -322,8 +322,6 @@ WebWidgetHost::WebWidgetHost() canvas_w_(0), canvas_h_(0), popup_(false), - scroll_dx_(0), - scroll_dy_(0), has_update_task_(false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { set_painting(false); diff --git a/libcef/webwidget_host_win.cc b/libcef/webwidget_host_win.cc index 713bbc4f0..bfa9fa77d 100644 --- a/libcef/webwidget_host_win.cc +++ b/libcef/webwidget_host_win.cc @@ -19,6 +19,7 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "third_party/WebKit/Source/WebKit/chromium/public/win/WebInputEventFactory.h" #include "third_party/WebKit/Source/WebKit/chromium/public/win/WebScreenInfoFactory.h" +#include "third_party/skia/include/core/SkRegion.h" #include "ui/base/ime/composition_text.h" #include "ui/base/range/range.h" #include "ui/base/win/hwnd_util.h" @@ -66,6 +67,11 @@ void SendMessageToPlugin(HWND hwnd, UINT message, WPARAM wParam, EnumChildWindows(hwnd, SendMessageFunc, reinterpret_cast(&info)); } +inline SkIRect convertToSkiaRect(const gfx::Rect& r) +{ + return SkIRect::MakeLTRB(r.x(), r.y(), r.right(), r.bottom()); +} + } // namespace /*static*/ @@ -117,11 +123,8 @@ LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam, if (host && !host->WndProc(message, wparam, lparam)) { switch (message) { case WM_PAINT: { - // Paint to the window. - RECT rect; - if (GetUpdateRect(hwnd, &rect, FALSE)) - host->UpdatePaintRect(gfx::Rect(rect)); - host->Paint(gfx::Rect(rect)); + // Paint to the window. The rect argument is unused. + host->Paint(gfx::Rect()); return 0; } @@ -265,15 +268,10 @@ LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam, void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting"; - // If this invalidate overlaps with a pending scroll, then we have to - // downgrade to invalidating the scroll rect. - if (damaged_rect.Intersects(scroll_rect_)) { - paint_rect_ = paint_rect_.Union(scroll_rect_); - ResetScrollRect(); - } - paint_rect_ = paint_rect_.Union(damaged_rect); - - InvalidateRect(gfx::Rect(damaged_rect)); + // If this invalidate overlaps with a pending scroll then we have to downgrade + // to invalidating the scroll rect. + UpdatePaintRect(damaged_rect); + InvalidateRect(damaged_rect); if (!popup_ && view_) { CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableFunction( @@ -282,25 +280,31 @@ void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { } void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { - if (dx != 0 && dy != 0) { - // We only support uni-directional scroll - DidScrollRect(0, dy, clip_rect); - dy = 0; + DCHECK(dx || dy); + + // Invalidate and re-paint the entire scroll rect if: + // 1. We're in a state where we cannot draw into the view right now, or + // 2. The rect is being scrolled by more than the size of the view, or + // 3. The scroll rect intersects the current paint region. + if (!canvas_.get() || layouting_ || painting_ || + abs(dx) >= clip_rect.width() || abs(dy) >= clip_rect.height() || + paint_rgn_.intersects(convertToSkiaRect(clip_rect))) { + DidInvalidateRect(clip_rect); + return; } - // 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() || paint_rect_.Intersects(clip_rect)) { - paint_rect_ = paint_rect_.Union(scroll_rect_); - ResetScrollRect(); - paint_rect_ = paint_rect_.Union(clip_rect); + // Scroll the canvas bitmap. + { + skia::ScopedPlatformPaint scoped_platform_paint(canvas_.get()); + HDC hdc = scoped_platform_paint.GetPlatformSurface(); + RECT clip_rect_win32 = clip_rect.ToRECT(), uncovered_rect = {0,0,0,0}; + ScrollDC(hdc, dx, dy, NULL, &clip_rect_win32, NULL, &uncovered_rect); + + UpdatePaintRect(gfx::Rect(uncovered_rect)); } - // We will perform scrolling lazily, when requested to actually paint. - scroll_rect_ = clip_rect; - scroll_dx_ = dx; - scroll_dy_ = dy; - + // Invalidate the scroll rect. It will be drawn from the canvas bitmap on the + // next WM_PAINT call. InvalidateRect(clip_rect); } @@ -326,13 +330,12 @@ WebWidgetHost::WebWidgetHost() canvas_h_(0), popup_(false), track_mouse_leave_(false), - scroll_dx_(0), - scroll_dy_(0), has_update_task_(false), tooltip_view_(NULL), tooltip_showing_(false), ime_notification_(false), input_method_is_active_(false), + layouting_(false), text_input_type_(WebKit::WebTextInputTypeNone), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { set_painting(false); @@ -359,14 +362,19 @@ bool WebWidgetHost::WndProc(UINT message, WPARAM wparam, LPARAM lparam) { return false; } -void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { - if (canvas_.get() && paint_rect_.IsEmpty()) - return; - +void WebWidgetHost::Paint(const gfx::Rect& /*dirty_rect*/) { int width, height; GetSize(width, height); gfx::Rect client_rect(width, height); - gfx::Rect damaged_rect; + + // Damaged rectangle used for drawing when window rendering is disabled. + SkRegion damaged_rgn; + if (!view_ && !redraw_rect_.IsEmpty()) { + // At a minimum we need to send the delegate the rectangle that was + // requested by calling CefBrowser::InvalidateRect(). + damaged_rgn.setRect(convertToSkiaRect(redraw_rect_)); + redraw_rect_ = gfx::Rect(); + } if (view_ && !webwidget_->isAcceleratedCompositingActive()) { // Number of pixels that the canvas is allowed to differ from the client @@ -378,8 +386,7 @@ void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { canvas_h_ < client_rect.height() || canvas_w_ > client_rect.width() + kCanvasGrowSize * 2 || canvas_h_ > client_rect.height() + kCanvasGrowSize * 2) { - ResetScrollRect(); - paint_rect_ = client_rect; + paint_rgn_.setRect(convertToSkiaRect(client_rect)); // Resize the canvas to be within a reasonable size of the client area. canvas_w_ = client_rect.width() + kCanvasGrowSize; @@ -388,8 +395,7 @@ void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { } } else if(!canvas_.get() || canvas_w_ != client_rect.width() || canvas_h_ != client_rect.height()) { - ResetScrollRect(); - paint_rect_ = client_rect; + paint_rgn_.setRect(convertToSkiaRect(client_rect)); // The canvas must be the exact size of the client area. canvas_w_ = client_rect.width(); @@ -397,41 +403,40 @@ void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); } + layouting_ = true; webwidget_->animate(0.0); - // This may result in more invalidation + // This may result in more invalidation. webwidget_->layout(); - // Scroll the canvas if necessary - scroll_rect_ = client_rect.Intersect(scroll_rect_); - if (!scroll_rect_.IsEmpty()) { - skia::ScopedPlatformPaint scoped_platform_paint(canvas_.get()); - HDC hdc = scoped_platform_paint.GetPlatformSurface(); + layouting_ = false; - RECT damaged_scroll_rect, r = scroll_rect_.ToRECT(); - ScrollDC(hdc, scroll_dx_, scroll_dy_, NULL, &r, NULL, &damaged_scroll_rect); - - PaintRect(gfx::Rect(damaged_scroll_rect)); - } - ResetScrollRect(); - - // Paint the canvas if necessary. Allow painting to generate extra rects the - // first time we call it. This is necessary because some WebCore rendering + // 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_); - damaged_rect = damaged_rect.Union(paint_rect_); - if (!paint_rect_.IsEmpty()) { - gfx::Rect rect(paint_rect_); - paint_rect_ = gfx::Rect(); + SkRegion draw_rgn; + draw_rgn.swap(paint_rgn_); - DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations"; - PaintRect(rect); + // Draw each dirty rect in the region. + SkRegion::Cliperator iterator(draw_rgn, convertToSkiaRect(client_rect)); + for (; !iterator.done(); iterator.next()) { + const SkIRect& r = iterator.rect(); + gfx::Rect paint_rect = + gfx::Rect(r.left(), r.top(), r.width(), r.height()); + PaintRect(paint_rect); + + if (!view_) + damaged_rgn.op(convertToSkiaRect(paint_rect), SkRegion::kUnion_Op); } - } - DCHECK(paint_rect_.IsEmpty()); - if (plugin_map_.size() > 0) { + if (paint_rgn_.isEmpty()) + break; + } + + DCHECK(paint_rgn_.isEmpty()); + + if (!view_ && plugin_map_.size() > 0) { typedef std::list PluginList; PluginList visible_plugins; @@ -476,11 +481,14 @@ void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { SetViewportOrgEx(drawDC, oldViewport.x, oldViewport.y, NULL); SelectClipRgn(drawDC, oldRGN); - damaged_rect = damaged_rect.Union(geom->window_rect); - } + if (!view_) { + damaged_rgn.op(convertToSkiaRect(geom->window_rect), + SkRegion::kUnion_Op); + } - // Make sure the damaged rectangle is inside the client rectangle. - damaged_rect = damaged_rect.Intersect(client_rect); + DeleteObject(oldRGN); + DeleteObject(newRGN); + } } } @@ -494,13 +502,22 @@ void WebWidgetHost::Paint(const gfx::Rect& dirty_rect) { // Draw children UpdateWindow(view_); - } else { + } else if(!damaged_rgn.isEmpty()) { // Paint to the delegate. DCHECK(paint_delegate_); const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); const void* pixels = bitmap.getPixels(); - paint_delegate_->Paint(popup_, damaged_rect, pixels); + + std::vector damaged_rects; + SkRegion::Cliperator iterator(damaged_rgn, convertToSkiaRect(client_rect)); + for (; !iterator.done(); iterator.next()) { + const SkIRect& r = iterator.rect(); + damaged_rects.push_back( + CefRect(r.left(), r.top(), r.width(), r.height())); + } + + paint_delegate_->Paint(popup_, damaged_rects, pixels); } } @@ -511,12 +528,10 @@ void WebWidgetHost::InvalidateRect(const gfx::Rect& rect) if (view_) { // Let the window handle painting. - RECT r = {rect.x(), rect.y(), rect.x() + rect.width(), - rect.y() + rect.height()}; + RECT r = rect.ToRECT(); ::InvalidateRect(view_, &r, FALSE); } else { - // The update rectangle will be painted by DoPaint(). - update_rect_ = update_rect_.Union(rect); + // Paint() will be called by DoPaint(). if (!has_update_task_) { has_update_task_ = true; CefThread::PostTask(CefThread::UI, FROM_HERE, @@ -527,15 +542,28 @@ void WebWidgetHost::InvalidateRect(const gfx::Rect& rect) bool WebWidgetHost::GetImage(int width, int height, void* buffer) { - if (!canvas_.get()) - return false; - - DCHECK(width == canvas_->getDevice()->width()); - DCHECK(height == canvas_->getDevice()->height()); - const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); - const void* pixels = bitmap.getPixels(); + + if (width == canvas_->getDevice()->width() && + height == canvas_->getDevice()->height()) { + // The specified width and height values are the same as the canvas size. + // Return the existing canvas contents. + const void* pixels = bitmap.getPixels(); + memcpy(buffer, pixels, width * height * 4); + return true; + } + + // Create a new canvas of the requested size. + scoped_ptr new_canvas( + new skia::PlatformCanvas(width, height, true)); + + new_canvas->writePixels(bitmap, 0, 0); + const SkBitmap& new_bitmap = new_canvas->getDevice()->accessBitmap(false); + DCHECK(new_bitmap.config() == SkBitmap::kARGB_8888_Config); + + // Return the new canvas contents. + const void* pixels = new_bitmap.getPixels(); memcpy(buffer, pixels, width * height * 4); return true; } @@ -704,12 +732,6 @@ void WebWidgetHost::TrackMouseLeave(bool track) { TrackMouseEvent(&tme); } -void WebWidgetHost::ResetScrollRect() { - scroll_rect_ = gfx::Rect(); - scroll_dx_ = 0; - scroll_dy_ = 0; -} - void WebWidgetHost::PaintRect(const gfx::Rect& rect) { #ifndef NDEBUG DCHECK(!painting_); diff --git a/libcef_dll/cpptoc/render_handler_cpptoc.cc b/libcef_dll/cpptoc/render_handler_cpptoc.cc index f4b87594d..d3dcc31d5 100644 --- a/libcef_dll/cpptoc/render_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/render_handler_cpptoc.cc @@ -94,18 +94,22 @@ void CEF_CALLBACK render_handler_on_popup_size( void CEF_CALLBACK render_handler_on_paint(struct _cef_render_handler_t* self, cef_browser_t* browser, enum cef_paint_element_type_t type, - const cef_rect_t* dirtyRect, const void* buffer) + size_t dirtyRectCount, cef_rect_t const* dirtyRects, const void* buffer) { DCHECK(self); DCHECK(browser); - DCHECK(dirtyRect); + DCHECK(dirtyRectCount > 0); + DCHECK(dirtyRects); DCHECK(buffer); - if (!self || !browser || !dirtyRect || !buffer) + if (!self || !browser || dirtyRectCount == 0 || !dirtyRects || !buffer) return; - CefRect rect(*dirtyRect); + CefRenderHandler::RectList rectList; + for (size_t i = 0; i < dirtyRectCount; ++i) + rectList.push_back(dirtyRects[i]); + return CefRenderHandlerCppToC::Get(self)->OnPaint( - CefBrowserCToCpp::Wrap(browser), type, rect, buffer); + CefBrowserCToCpp::Wrap(browser), type, rectList, buffer); } void CEF_CALLBACK render_handler_on_cursor_change( diff --git a/libcef_dll/ctocpp/render_handler_ctocpp.cc b/libcef_dll/ctocpp/render_handler_ctocpp.cc index f1661afe2..b38db63f1 100644 --- a/libcef_dll/ctocpp/render_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/render_handler_ctocpp.cc @@ -66,13 +66,25 @@ void CefRenderHandlerCToCpp::OnPopupSize(CefRefPtr browser, } void CefRenderHandlerCToCpp::OnPaint(CefRefPtr browser, - PaintElementType type, const CefRect& dirtyRect, const void* buffer) + PaintElementType type, const RectList& dirtyRects, const void* buffer) { if (CEF_MEMBER_MISSING(struct_, on_paint)) return; - return struct_->on_paint(struct_, CefBrowserCppToC::Wrap(browser), type, - &dirtyRect, buffer); + const size_t rectCt = dirtyRects.size(); + DCHECK(rectCt > 0); + if (rectCt == 0) + return; + + cef_rect_t* rects = new cef_rect_t[rectCt]; + RectList::const_iterator it = dirtyRects.begin(); + for (size_t i = 0; it != dirtyRects.end(); ++it, ++i) + rects[i] = *it; + + struct_->on_paint(struct_, CefBrowserCppToC::Wrap(browser), type, rectCt, + rects, buffer); + + delete [] rects; } void CefRenderHandlerCToCpp::OnCursorChange(CefRefPtr browser, diff --git a/libcef_dll/ctocpp/render_handler_ctocpp.h b/libcef_dll/ctocpp/render_handler_ctocpp.h index 7375e06da..bc25a6683 100644 --- a/libcef_dll/ctocpp/render_handler_ctocpp.h +++ b/libcef_dll/ctocpp/render_handler_ctocpp.h @@ -43,7 +43,7 @@ public: virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) OVERRIDE; virtual void OnPaint(CefRefPtr browser, PaintElementType type, - const CefRect& dirtyRect, const void* buffer) OVERRIDE; + const RectList& dirtyRects, const void* buffer) OVERRIDE; virtual void OnCursorChange(CefRefPtr browser, CefCursorHandle cursor) OVERRIDE; }; diff --git a/tests/cefclient/cefclient.h b/tests/cefclient/cefclient.h index abdc97200..69137802d 100644 --- a/tests/cefclient/cefclient.h +++ b/tests/cefclient/cefclient.h @@ -36,6 +36,7 @@ void RunModalDialogTest(CefRefPtr browser); #if defined(OS_WIN) void RunTransparentPopupTest(CefRefPtr browser); +void RunGetImageTest(CefRefPtr browser); #endif #endif // _CEFCLIENT_H diff --git a/tests/cefclient/cefclient.rc b/tests/cefclient/cefclient.rc index e525fb965..a058a5686 100644 --- a/tests/cefclient/cefclient.rc +++ b/tests/cefclient/cefclient.rc @@ -101,6 +101,7 @@ BEGIN MENUITEM "Show Developer Tools", ID_TESTS_DEVTOOLS_SHOW MENUITEM "Close Developer Tools", ID_TESTS_DEVTOOLS_CLOSE MENUITEM "Modal Dialog", ID_TESTS_MODALDIALOG + MENUITEM "Get Image", ID_TESTS_GETIMAGE END END diff --git a/tests/cefclient/cefclient_win.cpp b/tests/cefclient/cefclient_win.cpp index 20cb01108..13230034c 100644 --- a/tests/cefclient/cefclient_win.cpp +++ b/tests/cefclient/cefclient_win.cpp @@ -3,6 +3,7 @@ // can be found in the LICENSE file. #include "include/cef.h" +#include "include/cef_runnable.h" #include "cefclient.h" #include "binding_test.h" #include "client_handler.h" @@ -17,6 +18,7 @@ #include #include +#include #define MAX_LOADSTRING 100 #define MAX_URL_LENGTH 255 @@ -583,6 +585,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if(browser.get()) RunModalDialogTest(browser); return 0; + case ID_TESTS_GETIMAGE: + if(browser.get()) + RunGetImageTest(browser); + return 0; } } break; @@ -694,3 +700,112 @@ void RunTransparentPopupTest(CefRefPtr browser) static_cast >(g_handler), "http://tests/transparency", settings); } + +namespace { + +// Determine a temporary path for the bitmap file. +bool GetBitmapTempPath(LPWSTR szTempName) +{ + DWORD dwRetVal; + DWORD dwBufSize = 512; + TCHAR lpPathBuffer[512]; + UINT uRetVal; + + dwRetVal = GetTempPath(dwBufSize, // length of the buffer + lpPathBuffer); // buffer for path + if (dwRetVal > dwBufSize || (dwRetVal == 0)) + return false; + + // Create a temporary file. + uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files + L"image", // temp file name prefix + 0, // create unique name + szTempName); // buffer for name + if (uRetVal == 0) + return false; + + size_t len = wcslen(szTempName); + wcscpy(szTempName + len - 3, L"bmp"); + return true; +} + +void UIT_RunGetImageTest(CefRefPtr browser) +{ + REQUIRE_UI_THREAD(); + + int width, height; + bool success = false; + + // Retrieve the image size. + if (browser->GetSize(PET_VIEW, width, height)) { + void* bits; + + // Populate the bitmap info header. + BITMAPINFOHEADER info; + info.biSize = sizeof(BITMAPINFOHEADER); + info.biWidth = width; + info.biHeight = -height; // minus means top-down bitmap + info.biPlanes = 1; + info.biBitCount = 32; + info.biCompression = BI_RGB; // no compression + info.biSizeImage = 0; + info.biXPelsPerMeter = 1; + info.biYPelsPerMeter = 1; + info.biClrUsed = 0; + info.biClrImportant = 0; + + // Create the bitmap and retrieve the bit buffer. + HDC screen_dc = GetDC(NULL); + HBITMAP bitmap = + CreateDIBSection(screen_dc, reinterpret_cast(&info), + DIB_RGB_COLORS, &bits, NULL, 0); + ReleaseDC(NULL, screen_dc); + + // Read the image into the bit buffer. + if (bitmap && browser->GetImage(PET_VIEW, width, height, bits)) { + // Populate the bitmap file header. + BITMAPFILEHEADER file; + file.bfType = 0x4d42; + file.bfSize = sizeof(BITMAPFILEHEADER); + file.bfReserved1 = 0; + file.bfReserved2 = 0; + file.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + + TCHAR temp_path[512]; + if (GetBitmapTempPath(temp_path)) { + // Write the bitmap to file. + HANDLE file_handle = + CreateFile(temp_path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); + if (file_handle != INVALID_HANDLE_VALUE) { + DWORD bytes_written = 0; + WriteFile(file_handle, &file, sizeof(file), &bytes_written, 0); + WriteFile(file_handle, &info, sizeof(info), &bytes_written, 0); + WriteFile(file_handle, bits, width * height * 4, &bytes_written, 0); + + CloseHandle(file_handle); + + // Open the bitmap in the default viewer. + ShellExecute(NULL, L"open", temp_path, NULL, NULL, SW_SHOWNORMAL); + success = true; + } + } + } + + DeleteObject(bitmap); + } + + if (!success) { + browser->GetMainFrame()->ExecuteJavaScript( + "alert('Failed to create image!');", + browser->GetMainFrame()->GetURL(), 0); + } +} + +} // namespace + +void RunGetImageTest(CefRefPtr browser) +{ + // Execute the test function on the UI thread. + CefPostTask(TID_UI, NewCefRunnableFunction(UIT_RunGetImageTest, browser)); +} diff --git a/tests/cefclient/osrplugin.cpp b/tests/cefclient/osrplugin.cpp index c16e48ed4..f349b71bf 100644 --- a/tests/cefclient/osrplugin.cpp +++ b/tests/cefclient/osrplugin.cpp @@ -280,7 +280,7 @@ public: virtual void OnPaint(CefRefPtr browser, PaintElementType type, - const CefRect& dirtyRect, + const RectList& dirtyRects, const void* buffer) OVERRIDE { REQUIRE_UI_THREAD(); diff --git a/tests/cefclient/resource.h b/tests/cefclient/resource.h index 80a0eb80d..7ca2da3bd 100644 --- a/tests/cefclient/resource.h +++ b/tests/cefclient/resource.h @@ -56,6 +56,7 @@ #define ID_TESTS_TRANSPARENT_POPUP 32796 #define ID_TESTS_TRANSPARENT_OSRAPP 32797 #define ID_TESTS_JAVASCRIPT_INVOKE 32798 +#define ID_TESTS_GETIMAGE 32799 #define IDC_STATIC -1 #define IDS_LOGO 1000 #define IDS_UIPLUGIN 1001 diff --git a/tools/cef_parser.py b/tools/cef_parser.py index 78255cf25..aad77cecd 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -1203,7 +1203,10 @@ class obj_analysis: return result if type == 'simple': - result['value'] = value + str = value + if self.is_const(): + str += ' const*' + result['value'] = str elif type == 'refptr': str = '' if not value[:-1] in defined_structs: