diff options
19 files changed, 281 insertions, 10 deletions
diff --git a/chrome/browser/aeropeek_manager.cc b/chrome/browser/aeropeek_manager.cc index 77360cf..8c65ecb 100644 --- a/chrome/browser/aeropeek_manager.cc +++ b/chrome/browser/aeropeek_manager.cc @@ -998,7 +998,7 @@ bool AeroPeekManager::GetTabPreview(int tab_id, SkBitmap* preview) { // Create a copy of this BackingStore image. // This code is just copied from "thumbnail_generator.cc". skia::PlatformCanvas canvas; - if (!render_view_host->CopyFromBackingStore(&canvas)) + if (!render_view_host->CopyFromBackingStore(gfx::Size(), &canvas)) return false; const SkBitmap& bitmap = skia::GetTopDevice(canvas)->accessBitmap(false); diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc index dbf198b..67d5916 100644 --- a/chrome/browser/extensions/extension_tabs_module.cc +++ b/chrome/browser/extensions/extension_tabs_module.cc @@ -1653,7 +1653,7 @@ bool CaptureVisibleTabFunction::RunImpl() { // For example, some uncommon X11 visual modes are not supported by // CopyFromBackingStore(). skia::PlatformCanvas temp_canvas; - if (render_view_host->CopyFromBackingStore(&temp_canvas)) { + if (render_view_host->CopyFromBackingStore(gfx::Size(), &temp_canvas)) { VLOG(1) << "captureVisibleTab() got image from backing store."; SendResultFromBitmap(skia::GetTopDevice(temp_canvas)->accessBitmap(false)); return true; diff --git a/chrome/browser/tab_contents/thumbnail_generator.cc b/chrome/browser/tab_contents/thumbnail_generator.cc index 79d8f10..398c2af 100644 --- a/chrome/browser/tab_contents/thumbnail_generator.cc +++ b/chrome/browser/tab_contents/thumbnail_generator.cc @@ -77,8 +77,9 @@ SkBitmap GetBitmapForRenderWidgetHost( // Get the bitmap as a Skia object so we can resample it. This is a large // allocation and we can tolerate failure here, so give up if the allocation // fails. + // TODO(mazda): Copy a shrinked size of image instead of the whole view size. skia::PlatformCanvas temp_canvas; - if (!render_widget_host->CopyFromBackingStore(&temp_canvas)) + if (!render_widget_host->CopyFromBackingStore(gfx::Size(), &temp_canvas)) return result; const SkBitmap& bmp_with_scrollbars = skia::GetTopDevice(temp_canvas)->accessBitmap(false); diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index 26dff6c..e797f92 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -34,8 +34,11 @@ #include "content/public/browser/user_metrics.h" #include "content/public/common/content_switches.h" #include "content/public/common/result_codes.h" +#include "skia/ext/image_operations.h" +#include "skia/ext/platform_canvas.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderline.h" #include "ui/base/keycodes/keyboard_codes.h" +#include "ui/gfx/skbitmap_operations.h" #include "webkit/glue/webcursor.h" #include "webkit/glue/webpreferences.h" #include "webkit/plugins/npapi/webplugin.h" @@ -501,13 +504,54 @@ void RenderWidgetHostImpl::SetIsLoading(bool is_loading) { view_->SetIsLoading(is_loading); } -bool RenderWidgetHostImpl::CopyFromBackingStore(skia::PlatformCanvas* output) { +bool RenderWidgetHostImpl::CopyFromBackingStore(const gfx::Size& dest_size, + skia::PlatformCanvas* output) { + if (view_ && is_accelerated_compositing_active_) { + TRACE_EVENT0("browser", + "RenderWidgetHostImpl::CopyFromBackingStore::FromCompositingSurface"); + gfx::Size result_size = view_->GetViewBounds().size(); + if (!dest_size.IsEmpty()) { + result_size.SetSize(std::min(result_size.width(), dest_size.width()), + std::min(result_size.height(), dest_size.height())); + } + return view_->CopyFromCompositingSurface(result_size, output); + } + BackingStore* backing_store = GetBackingStore(false); if (!backing_store) return false; - return backing_store->CopyFromBackingStore( - gfx::Rect(backing_store->size()), output); + TRACE_EVENT0("browser", + "RenderWidgetHostImpl::CopyFromBackingStore::FromBackingStore"); + const gfx::Size backing_store_size = backing_store->size(); + gfx::Size result_size = backing_store->size(); + if (!dest_size.IsEmpty()) { + result_size.SetSize(std::min(result_size.width(), dest_size.width()), + std::min(result_size.height(), dest_size.height())); + } + // When the result size is equal to the backing store size, copy from the + // backing store directly to the output canvas. + if (result_size == backing_store_size) + return backing_store->CopyFromBackingStore(gfx::Rect(result_size), output); + + // Otherwise, first copy from the backing store to a temporary canvas, then + // resize it into the result size. + skia::PlatformCanvas temp_canvas; + if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store_size), + &temp_canvas)) + return false; + + SkBitmap downsampled_bitmap = + SkBitmapOperations::DownsampleByTwoUntilSize( + skia::GetTopDevice(temp_canvas)->accessBitmap(false), + result_size.width(), result_size.height()); + SkBitmap result_bitmap = skia::ImageOperations::Resize( + downsampled_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, + dest_size.width(), dest_size.height()); + if (!output->initialize(result_size.width(), result_size.height(), true)) + return false; + output->writePixels(result_bitmap, 0, 0); + return true; } #if defined(TOOLKIT_GTK) diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index 5942911..207a37b 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h @@ -77,7 +77,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, virtual void UpdateTextDirection(WebKit::WebTextDirection direction) OVERRIDE; virtual void NotifyTextDirection() OVERRIDE; virtual void Blur() OVERRIDE; - virtual bool CopyFromBackingStore(skia::PlatformCanvas* output) OVERRIDE; + virtual bool CopyFromBackingStore(const gfx::Size& dest_size, + skia::PlatformCanvas* output) OVERRIDE; #if defined(TOOLKIT_GTK) virtual bool CopyFromBackingStoreToGtkWindow(const gfx::Rect& dest_rect, GdkWindow* target) OVERRIDE; diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 6a5c159..a0e2ebd 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -525,6 +525,14 @@ void RenderWidgetHostViewAura::SetBackground(const SkBitmap& background) { window_->layer()->SetFillsBoundsOpaquely(background.isOpaque()); } +bool RenderWidgetHostViewAura::CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) { + // TODO(mazda): Implement this. + NOTIMPLEMENTED(); + return false; +} + void RenderWidgetHostViewAura::GetScreenInfo(WebKit::WebScreenInfo* results) { GetDefaultScreenInfo(results); } diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index 3123c56..60257b4 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h @@ -67,6 +67,9 @@ class RenderWidgetHostViewAura virtual void UnhandledWheelEvent( const WebKit::WebMouseWheelEvent& event) OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; + virtual bool CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) OVERRIDE; // Overridden from RenderWidgetHostViewPort: virtual void InitAsPopup(content::RenderWidgetHostView* parent_host_view, diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.cc b/content/browser/renderer_host/render_widget_host_view_gtk.cc index a2cf64e..fa4b879 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/content/browser/renderer_host/render_widget_host_view_gtk.cc @@ -1047,6 +1047,14 @@ void RenderWidgetHostViewGtk::SetBackground(const SkBitmap& background) { host_->Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background)); } +bool RenderWidgetHostViewGtk::CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) { + // TODO(mazda): Implement this. + NOTIMPLEMENTED(); + return false; +} + void RenderWidgetHostViewGtk::ModifyEventForEdgeDragging( GtkWidget* widget, GdkEventMotion* event) { // If the widget is aligned with an edge of the monitor its on and the user diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.h b/content/browser/renderer_host/render_widget_host_view_gtk.h index 5b60ec3..f322d04 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.h +++ b/content/browser/renderer_host/render_widget_host_view_gtk.h @@ -72,6 +72,9 @@ class RenderWidgetHostViewGtk : public content::RenderWidgetHostViewBase { virtual void UnhandledWheelEvent( const WebKit::WebMouseWheelEvent& event) OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; + virtual bool CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) OVERRIDE; // RenderWidgetHostViewPort implementation. virtual void InitAsPopup(content::RenderWidgetHostView* parent_host_view, diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index 5e2a054..c8f5fd56 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -193,6 +193,9 @@ class RenderWidgetHostViewMac : public content::RenderWidgetHostViewBase { virtual void UnhandledWheelEvent( const WebKit::WebMouseWheelEvent& event) OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; + virtual bool CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) OVERRIDE; // Implementation of RenderWidgetHostViewPort. virtual void InitAsPopup(content::RenderWidgetHostView* parent_host_view, diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 2f1e2da..9ace1d3 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -1126,6 +1126,14 @@ void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) { render_widget_host_->GetRoutingID(), background)); } +bool RenderWidgetHostViewMac::CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) { + // TODO(mazda): Implement this. + NOTIMPLEMENTED(); + return false; +} + void RenderWidgetHostViewMac::OnAccessibilityNotifications( const std::vector<AccessibilityHostMsg_NotificationParams>& params) { if (!GetBrowserAccessibilityManager()) { diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc index 5be7dfa..33539a1 100644 --- a/content/browser/renderer_host/render_widget_host_view_win.cc +++ b/content/browser/renderer_host/render_widget_host_view_win.cc @@ -907,6 +907,22 @@ void RenderWidgetHostViewWin::SetBackground(const SkBitmap& background) { render_widget_host_->SetBackground(background); } +bool RenderWidgetHostViewWin::CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) { + if (!accelerated_surface_.get()) + return false; + + if (size.IsEmpty()) + return false; + + if (!output->initialize(size.width(), size.height(), true)) + return false; + + return accelerated_surface_->CopyTo( + size, output->getTopDevice()->accessBitmap(true).getPixels()); +} + void RenderWidgetHostViewWin::UnhandledWheelEvent( const WebKit::WebMouseWheelEvent& event) { } diff --git a/content/browser/renderer_host/render_widget_host_view_win.h b/content/browser/renderer_host/render_widget_host_view_win.h index 670c9f4d..4dc81b4 100644 --- a/content/browser/renderer_host/render_widget_host_view_win.h +++ b/content/browser/renderer_host/render_widget_host_view_win.h @@ -169,6 +169,9 @@ class RenderWidgetHostViewWin virtual void UnhandledWheelEvent( const WebKit::WebMouseWheelEvent& event) OVERRIDE; virtual void SetBackground(const SkBitmap& background) OVERRIDE; + virtual bool CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) OVERRIDE; // Implementation of RenderWidgetHostViewPort. virtual void InitAsPopup(content::RenderWidgetHostView* parent_host_view, diff --git a/content/browser/renderer_host/test_render_view_host.cc b/content/browser/renderer_host/test_render_view_host.cc index ad23b47..e61edd1 100644 --- a/content/browser/renderer_host/test_render_view_host.cc +++ b/content/browser/renderer_host/test_render_view_host.cc @@ -181,6 +181,12 @@ gfx::NativeView TestRenderWidgetHostView::BuildInputMethodsGtkMenu() { #endif // !defined(TOOLKIT_VIEWS) #endif // defined(TOOLKIT_USES_GTK) +bool TestRenderWidgetHostView::CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) { + return false; +} + gfx::GLSurfaceHandle TestRenderWidgetHostView::GetCompositingSurface() { return gfx::GLSurfaceHandle(); } diff --git a/content/browser/renderer_host/test_render_view_host.h b/content/browser/renderer_host/test_render_view_host.h index 354b0fe..298761e 100644 --- a/content/browser/renderer_host/test_render_view_host.h +++ b/content/browser/renderer_host/test_render_view_host.h @@ -76,6 +76,9 @@ class TestRenderWidgetHostView : public RenderWidgetHostViewBase { #endif // defined(TOOLKIT_USES_GTK) virtual void UnhandledWheelEvent( const WebKit::WebMouseWheelEvent& event) OVERRIDE {} + virtual bool CopyFromCompositingSurface( + const gfx::Size& size, + skia::PlatformCanvas* output) OVERRIDE; // RenderWidgetHostViewPort implementation. virtual void InitAsPopup(RenderWidgetHostView* parent_host_view, diff --git a/content/port/browser/render_widget_host_view_port.h b/content/port/browser/render_widget_host_view_port.h index 471c427..7d84000 100644 --- a/content/port/browser/render_widget_host_view_port.h +++ b/content/port/browser/render_widget_host_view_port.h @@ -138,6 +138,12 @@ class CONTENT_EXPORT RenderWidgetHostViewPort : public RenderWidgetHostView { // Allocate a backing store for this view. virtual BackingStore* AllocBackingStore(const gfx::Size& size) = 0; + // Copies the contents of the compositing surface into the given + // (uninitialized) PlatformCanvas if any. Returns true on success, false + // otherwise. + virtual bool CopyFromCompositingSurface(const gfx::Size& size, + skia::PlatformCanvas* output) = 0; + // Called when accelerated compositing state changes. virtual void OnAcceleratedCompositingStateChange() = 0; // |params.window| and |params.surface_id| indicate which accelerated diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h index e95d325..b9f5204 100644 --- a/content/public/browser/render_widget_host.h +++ b/content/public/browser/render_widget_host.h @@ -177,8 +177,14 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Sender { virtual void Blur() = 0; // Copies the contents of the backing store into the given (uninitialized) - // PlatformCanvas. Returns true on success, false otherwise. - virtual bool CopyFromBackingStore(skia::PlatformCanvas* output) = 0; + // PlatformCanvas. Returns true on success, false otherwise. When accelerated + // compositing is active, the contents is copied from the compositing surface. + // If non empty |dest_size| is given, the content is shrinked so that it fits + // in |dest_size|. If |dest_size| is larger than the contens size, the + // content is not resized. If |dest_size| is empty, the original size of the + // contents is copied. + virtual bool CopyFromBackingStore(const gfx::Size& dest_size, + skia::PlatformCanvas* output) = 0; #if defined(TOOLKIT_GTK) // Paint the backing store into the target's |dest_rect|. diff --git a/ui/gfx/surface/accelerated_surface_win.cc b/ui/gfx/surface/accelerated_surface_win.cc index adff364..bf24711 100644 --- a/ui/gfx/surface/accelerated_surface_win.cc +++ b/ui/gfx/surface/accelerated_surface_win.cc @@ -5,6 +5,7 @@ #include "ui/gfx/surface/accelerated_surface_win.h" #include <windows.h> +#include <algorithm> #include "base/bind.h" #include "base/bind_helpers.h" @@ -38,6 +39,47 @@ UINT GetPresentationInterval() { return D3DPRESENT_INTERVAL_ONE; } +// Calculate the number necessary to transform |source_size| into |dest_size| +// by repeating downsampling of the image of |source_size| by a factor no more +// than 2. +int GetResampleCount(const gfx::Size& source_size, const gfx::Size& dest_size) { + int width_count = 0; + int width = source_size.width(); + while (width > dest_size.width()) { + ++width_count; + width >>= 1; + } + int height_count = 0; + int height = source_size.height(); + while (height > dest_size.height()) { + ++height_count; + height >>= 1; + } + return std::max(width_count, height_count); +} + +// Returns half the size of |size| no smaller than |min_size|. +gfx::Size GetHalfSizeNoLessThan(const gfx::Size& size, + const gfx::Size& min_size) { + return gfx::Size(std::max(min_size.width(), size.width() / 2), + std::max(min_size.height(), size.height() / 2)); +} + +bool CreateTemporarySurface(IDirect3DDevice9* device, + const gfx::Size& size, + IDirect3DSurface9** surface) { + HRESULT hr = device->CreateRenderTarget( + size.width(), + size.height(), + D3DFMT_A8R8G8B8, + D3DMULTISAMPLE_NONE, + 0, + TRUE, + surface, + NULL); + return SUCCEEDED(hr); +} + } // namespace anonymous // A PresentThread is a thread that is dedicated to presenting surfaces to a @@ -240,6 +282,106 @@ bool AcceleratedPresenter::Present(gfx::NativeWindow window) { return true; } +bool AcceleratedPresenter::CopyTo(const gfx::Size& size, void* buf) { + base::AutoLock locked(lock_); + + if (!swap_chain_) + return false; + + base::win::ScopedComPtr<IDirect3DSurface9> back_buffer; + HRESULT hr = swap_chain_->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + back_buffer.Receive()); + if (FAILED(hr)) + return false; + + D3DSURFACE_DESC desc; + hr = back_buffer->GetDesc(&desc); + if (FAILED(hr)) + return false; + + const gfx::Size back_buffer_size(desc.Width, desc.Height); + if (back_buffer_size.IsEmpty()) + return false; + + // Set up intermediate buffers needed for downsampling. + const int resample_count = + GetResampleCount(gfx::Size(desc.Width, desc.Height), size); + base::win::ScopedComPtr<IDirect3DSurface9> final_surface; + base::win::ScopedComPtr<IDirect3DSurface9> temp_buffer[2]; + if (resample_count == 0) + final_surface = back_buffer; + if (resample_count > 0) { + if (!CreateTemporarySurface(present_thread_->device(), + size, + final_surface.Receive())) + return false; + } + const gfx::Size half_size = GetHalfSizeNoLessThan(back_buffer_size, size); + if (resample_count > 1) { + if (!CreateTemporarySurface(present_thread_->device(), + half_size, + temp_buffer[0].Receive())) + return false; + } + if (resample_count > 2) { + const gfx::Size quarter_size = GetHalfSizeNoLessThan(half_size, size); + if (!CreateTemporarySurface(present_thread_->device(), + quarter_size, + temp_buffer[1].Receive())) + return false; + } + + // Repeat downsampling the surface until its size becomes identical to + // |size|. We keep the factor of each downsampling no more than two because + // using a factor more than two can introduce aliasing. + gfx::Size read_size = back_buffer_size; + gfx::Size write_size = half_size; + int read_buffer_index = 1; + int write_buffer_index = 0; + for (int i = 0; i < resample_count; ++i) { + base::win::ScopedComPtr<IDirect3DSurface9> read_buffer = + (i == 0) ? back_buffer : temp_buffer[read_buffer_index]; + base::win::ScopedComPtr<IDirect3DSurface9> write_buffer = + (i == resample_count - 1) ? final_surface : + temp_buffer[write_buffer_index]; + RECT read_rect = {0, 0, read_size.width(), read_size.height()}; + RECT write_rect = {0, 0, write_size.width(), write_size.height()}; + hr = present_thread_->device()->StretchRect(read_buffer, + &read_rect, + write_buffer, + &write_rect, + D3DTEXF_LINEAR); + if (FAILED(hr)) + return false; + read_size = write_size; + write_size = GetHalfSizeNoLessThan(write_size, size); + std::swap(read_buffer_index, write_buffer_index); + } + + DCHECK(size == read_size); + + base::win::ScopedComPtr<IDirect3DSurface9> temp_surface; + HANDLE handle = reinterpret_cast<HANDLE>(buf); + hr = present_thread_->device()->CreateOffscreenPlainSurface( + size.width(), + size.height(), + D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM, + temp_surface.Receive(), + &handle); + if (FAILED(hr)) + return false; + + // Copy the data in the temporary buffer to the surface backed by |buf|. + hr = present_thread_->device()->GetRenderTargetData(final_surface, + temp_surface); + if (FAILED(hr)) + return false; + + return true; +} + void AcceleratedPresenter::Suspend() { present_thread_->message_loop()->PostTask( FROM_HERE, @@ -444,7 +586,10 @@ bool AcceleratedSurface::Present(HWND window) { return presenter_->Present(window); } +bool AcceleratedSurface::CopyTo(const gfx::Size& size, void* buf) { + return presenter_->CopyTo(size, buf); +} + void AcceleratedSurface::Suspend() { presenter_->Suspend(); } - diff --git a/ui/gfx/surface/accelerated_surface_win.h b/ui/gfx/surface/accelerated_surface_win.h index 9b48b36..52208d3 100644 --- a/ui/gfx/surface/accelerated_surface_win.h +++ b/ui/gfx/surface/accelerated_surface_win.h @@ -32,6 +32,7 @@ class AcceleratedPresenter int64 surface_id, const base::Callback<void(bool)>& completion_task); bool Present(gfx::NativeWindow window); + bool CopyTo(const gfx::Size& size, void* buf); void Suspend(); void WaitForPendingTasks(); @@ -85,6 +86,12 @@ class SURFACE_EXPORT AcceleratedSurface { // Synchronously present a frame with no acknowledgement. bool Present(gfx::NativeWindow window); + // Copies the surface data to |buf|. The image data is transformed so that it + // fits in |size|. + // Caller must ensure that |buf| is allocated with the size no less than + // |4 * size.width() * size.height()| bytes. + bool CopyTo(const gfx::Size& size, void* buf); + // Temporarily release resources until a new surface is asynchronously // presented. Present will not be able to represent the last surface after // calling this and will return false. |