From c051d5a56b838d1a1ac552dffa93f52e24ab1fd3 Mon Sep 17 00:00:00 2001 From: "enne@chromium.org" Date: Wed, 8 Jan 2014 23:54:51 +0000 Subject: Add GrabWindowSnapshotAsync aura tests This adds a version of GrabWindowSnapshotAsync that behaves identically to GrabWindowSnapshot except with a callback. The snapshot aura unit tests are updated to use this new async function instead of the sync one. In order to make the snapshot tests work properly, they also now have to use a real gl context so that real drawing and readback can occur through the compositor. BUG=331410, 332167 Review URL: https://codereview.chromium.org/119753007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243700 0039d316-1c4b-4281-b951-d872f2087c98 --- ui/snapshot/snapshot.h | 13 ++- ui/snapshot/snapshot_android.cc | 10 ++- ui/snapshot/snapshot_aura.cc | 156 ++++++++++++++++++++++++---------- ui/snapshot/snapshot_aura_unittest.cc | 84 +++++++++++++++--- ui/snapshot/snapshot_gtk.cc | 10 ++- ui/snapshot/snapshot_ios.mm | 10 ++- ui/snapshot/snapshot_mac.mm | 10 ++- ui/snapshot/snapshot_win.cc | 10 ++- 8 files changed, 235 insertions(+), 68 deletions(-) (limited to 'ui/snapshot') diff --git a/ui/snapshot/snapshot.h b/ui/snapshot/snapshot.h index d6fb6b8..7ccd8b4 100644 --- a/ui/snapshot/snapshot.h +++ b/ui/snapshot/snapshot.h @@ -28,7 +28,7 @@ namespace ui { // intended to be used for debugging purposes where no BrowserProcess instance // is available (ie. tests). This function is synchronous, so it should NOT be // used in a result of user action. Use asynchronous GrabWindowSnapshotAsync() -// instead. +// instead on supported platforms. SNAPSHOT_EXPORT bool GrabWindowSnapshot( gfx::NativeWindow window, std::vector* png_representation, @@ -39,16 +39,21 @@ SNAPSHOT_EXPORT bool GrabViewSnapshot( std::vector* png_representation, const gfx::Rect& snapshot_bounds); -// GrabWindowSnapshotAsync() copies snapshot of |source_rect| from window and -// scales it to |target_size| asynchronously. typedef base::Callback GrabWindowSnapshotAsyncCallback; -SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( +// GrabWindowSnapshotAndScaleAsync() copies snapshot of |source_rect| from +// window and scales it to |target_size| asynchronously. +SNAPSHOT_EXPORT void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, scoped_refptr background_task_runner, const GrabWindowSnapshotAsyncCallback& callback); +SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback); } // namespace ui diff --git a/ui/snapshot/snapshot_android.cc b/ui/snapshot/snapshot_android.cc index 3ae0be7..83c5efe 100644 --- a/ui/snapshot/snapshot_android.cc +++ b/ui/snapshot/snapshot_android.cc @@ -33,7 +33,7 @@ bool GrabWindowSnapshot(gfx::NativeWindow window, scaled_bounds.height(), png_representation); } -SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( +void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& snapshot_bounds, const gfx::Size& target_size, @@ -42,4 +42,12 @@ SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( NOTIMPLEMENTED(); } +void GrabWindowSnapshotAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback) { + NOTIMPLEMENTED(); +} + } // namespace ui diff --git a/ui/snapshot/snapshot_aura.cc b/ui/snapshot/snapshot_aura.cc index 5fdd4dc..620d312 100644 --- a/ui/snapshot/snapshot_aura.cc +++ b/ui/snapshot/snapshot_aura.cc @@ -40,9 +40,43 @@ void OnFrameScalingFinished( callback.Run(gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(scaled_bitmap))); } -void ScaleCopyOutputResult( +void RotateBitmap(SkBitmap* bitmap, gfx::Display::Rotation rotation) { + switch (rotation) { + case gfx::Display::ROTATE_0: + break; + case gfx::Display::ROTATE_90: + *bitmap = SkBitmapOperations::Rotate(*bitmap, + SkBitmapOperations::ROTATION_270_CW); + break; + case gfx::Display::ROTATE_180: + *bitmap = SkBitmapOperations::Rotate(*bitmap, + SkBitmapOperations::ROTATION_180_CW); + break; + case gfx::Display::ROTATE_270: + *bitmap = SkBitmapOperations::Rotate(*bitmap, + SkBitmapOperations::ROTATION_90_CW); + break; + } +} + +SkBitmap ScaleAndRotateBitmap(const SkBitmap& input_bitmap, + gfx::Size target_size_pre_rotation, + gfx::Display::Rotation rotation) { + SkBitmap bitmap; + bitmap = + skia::ImageOperations::Resize(input_bitmap, + skia::ImageOperations::RESIZE_GOOD, + target_size_pre_rotation.width(), + target_size_pre_rotation.height(), + static_cast(NULL)); + RotateBitmap(&bitmap, rotation); + return bitmap; +} + +void ScaleAndRotateCopyOutputResult( const GrabWindowSnapshotAsyncCallback& callback, const gfx::Size& target_size, + gfx::Display::Rotation rotation, scoped_refptr background_task_runner, scoped_ptr result) { if (result->IsEmpty()) { @@ -50,40 +84,20 @@ void ScaleCopyOutputResult( return; } - // There are two overrides for skia::ImageOperations::Resize(), so we need get - // pointer to the right override explicitly (otherwise the base::Bind() call - // below won't compile). - SkBitmap (*resize_function)(const SkBitmap&, - skia::ImageOperations::ResizeMethod, int, int, - SkBitmap::Allocator* allocator) = - &skia::ImageOperations::Resize; - // TODO(sergeyu): Potentially images can be scaled on GPU before reading it // from GPU. Image scaling is implemented in content::GlHelper, but it's can't // be used here because it's not in content/public. Move the scaling code // somewhere so that it can be reused here. base::PostTaskAndReplyWithResult( - background_task_runner, FROM_HERE, - base::Bind(resize_function, *result->TakeBitmap(), - skia::ImageOperations::RESIZE_GOOD, - target_size.width(), target_size.height(), - static_cast(NULL)), + background_task_runner, + FROM_HERE, + base::Bind( + ScaleAndRotateBitmap, *result->TakeBitmap(), target_size, rotation), base::Bind(&OnFrameScalingFinished, callback)); } -} // namespace - -bool GrabViewSnapshot(gfx::NativeView view, - std::vector* png_representation, - const gfx::Rect& snapshot_bounds) { - return GrabWindowSnapshot(view, png_representation, snapshot_bounds); -} - -bool GrabWindowSnapshot(gfx::NativeWindow window, - std::vector* png_representation, - const gfx::Rect& snapshot_bounds) { - ui::Compositor* compositor = window->layer()->GetCompositor(); - +gfx::Rect GetTargetBoundsFromWindow(gfx::NativeWindow window, + gfx::Rect snapshot_bounds) { gfx::RectF read_pixels_bounds = snapshot_bounds; // We must take into account the window's position on the desktop. @@ -98,33 +112,37 @@ bool GrabWindowSnapshot(gfx::NativeWindow window, // Sometimes (i.e. when using Aero on Windows) the compositor's size is // smaller than the window bounds. So trim appropriately. + ui::Compositor* compositor = window->layer()->GetCompositor(); read_pixels_bounds_in_pixel.Intersect(gfx::Rect(compositor->size())); DCHECK_LE(0, read_pixels_bounds.x()); DCHECK_LE(0, read_pixels_bounds.y()); + return read_pixels_bounds_in_pixel; +} + +} // namespace + +bool GrabViewSnapshot(gfx::NativeView view, + std::vector* png_representation, + const gfx::Rect& snapshot_bounds) { + return GrabWindowSnapshot(view, png_representation, snapshot_bounds); +} + +bool GrabWindowSnapshot(gfx::NativeWindow window, + std::vector* png_representation, + const gfx::Rect& snapshot_bounds) { + gfx::Rect read_pixels_bounds_in_pixel = + GetTargetBoundsFromWindow(window, snapshot_bounds); + + ui::Compositor* compositor = window->layer()->GetCompositor(); SkBitmap bitmap; if (!compositor->ReadPixels(&bitmap, read_pixels_bounds_in_pixel)) return false; gfx::Display display = gfx::Screen::GetScreenFor(window)->GetDisplayNearestWindow(window); - switch (display.rotation()) { - case gfx::Display::ROTATE_0: - break; - case gfx::Display::ROTATE_90: - bitmap = SkBitmapOperations::Rotate( - bitmap, SkBitmapOperations::ROTATION_270_CW); - break; - case gfx::Display::ROTATE_180: - bitmap = SkBitmapOperations::Rotate( - bitmap, SkBitmapOperations::ROTATION_180_CW); - break; - case gfx::Display::ROTATE_270: - bitmap = SkBitmapOperations::Rotate( - bitmap, SkBitmapOperations::ROTATION_90_CW); - break; - } + RotateBitmap(&bitmap, display.rotation()); unsigned char* pixels = reinterpret_cast( bitmap.pixelRef()->pixels()); @@ -136,18 +154,66 @@ bool GrabWindowSnapshot(gfx::NativeWindow window, png_representation); } -SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( +void MakeAsyncCopyRequest( gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, scoped_refptr background_task_runner, const GrabWindowSnapshotAsyncCallback& callback) { + gfx::Display::Rotation rotation = gfx::Screen::GetScreenFor(window) + ->GetDisplayNearestWindow(window) + .rotation(); scoped_ptr request = cc::CopyOutputRequest::CreateBitmapRequest( - base::Bind(&ScaleCopyOutputResult, callback, target_size, + base::Bind(&ScaleAndRotateCopyOutputResult, + callback, + target_size, + rotation, background_task_runner)); request->set_area(ui::ConvertRectToPixel(window->layer(), source_rect)); window->layer()->RequestCopyOfOutput(request.Pass()); } +void GrabWindowSnapshotAndScaleAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + const gfx::Size& target_size, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback) { + // target_size is post-rotation, and so logically this is a rotate and then + // scale operation. However, it will usually be more efficient to scale first + // (given that this is mostly used for thumbnails) and then rotate. + gfx::Display::Rotation rotation = gfx::Screen::GetScreenFor(window) + ->GetDisplayNearestWindow(window) + .rotation(); + gfx::Size rotated_target_size; + switch (rotation) { + case gfx::Display::ROTATE_0: + case gfx::Display::ROTATE_180: + rotated_target_size = target_size; + break; + case gfx::Display::ROTATE_90: + case gfx::Display::ROTATE_270: + rotated_target_size = + gfx::Size(target_size.height(), target_size.width()); + break; + }; + + MakeAsyncCopyRequest(window, + source_rect, + rotated_target_size, + background_task_runner, + callback); +} + +void GrabWindowSnapshotAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback) { + gfx::Size target_size = GetTargetBoundsFromWindow(window, source_rect).size(); + MakeAsyncCopyRequest( + window, source_rect, target_size, background_task_runner, callback); +} + } // namespace ui diff --git a/ui/snapshot/snapshot_aura_unittest.cc b/ui/snapshot/snapshot_aura_unittest.cc index 856be55..6e5705a 100644 --- a/ui/snapshot/snapshot_aura_unittest.cc +++ b/ui/snapshot/snapshot_aura_unittest.cc @@ -4,6 +4,8 @@ #include "ui/snapshot/snapshot.h" +#include "base/bind.h" +#include "base/test/test_simple_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/root_window.h" #include "ui/aura/test/aura_test_helper.h" @@ -58,7 +60,7 @@ size_t GetFailedPixelsCount(const gfx::Image& image) { } // namespace -class SnapshotAuraTest : public testing::Test { +class SnapshotAuraTest : public testing::TestWithParam { public: SnapshotAuraTest() {} virtual ~SnapshotAuraTest() {} @@ -67,7 +69,9 @@ class SnapshotAuraTest : public testing::Test { testing::Test::SetUp(); helper_.reset( new aura::test::AuraTestHelper(base::MessageLoopForUI::current())); - helper_->SetUp(); + // Snapshot test tests real drawing and readback, so needs a real context. + bool allow_test_contexts = false; + helper_->SetUp(allow_test_contexts); } virtual void TearDown() OVERRIDE { @@ -95,15 +99,65 @@ class SnapshotAuraTest : public testing::Test { delegate_.get(), 0, window_bounds, root_window())); } + bool is_async_test() const { return GetParam(); } + gfx::Image GrabSnapshotForTestWindow() { - std::vector png_representation; - gfx::Rect local_bounds(test_window_->bounds().size()); - ui::GrabWindowSnapshot(test_window(), &png_representation, local_bounds); - return gfx::Image::CreateFrom1xPNGBytes( - base::RefCountedBytes::TakeVector(&png_representation)); + gfx::Rect source_rect(test_window_->bounds().size()); + if (!is_async_test()) { + std::vector png_representation; + ui::GrabWindowSnapshot(test_window(), &png_representation, source_rect); + return gfx::Image::CreateFrom1xPNGBytes(&(png_representation[0]), + png_representation.size()); + } + + scoped_refptr task_runner( + new base::TestSimpleTaskRunner()); + scoped_refptr holder(new SnapshotHolder); + ui::GrabWindowSnapshotAsync( + test_window(), + source_rect, + task_runner, + base::Bind(&SnapshotHolder::SnapshotCallback, holder)); + + // Wait for copy response. + WaitForDraw(); + // Run internal snapshot callback to scale/rotate response image. + task_runner->RunUntilIdle(); + // Run SnapshotHolder callback. + helper_->RunAllPendingInMessageLoop(); + + if (holder->completed()) + return holder->image(); + + // Callback never called. + NOTREACHED(); + return gfx::Image(); } private: + class SnapshotHolder : public base::RefCountedThreadSafe { + public: + SnapshotHolder() : completed_(false) {} + + void SnapshotCallback(const gfx::Image& image) { + DCHECK(!completed_); + image_ = image; + completed_ = true; + } + bool completed() const { + return completed_; + }; + const gfx::Image& image() const { return image_; } + + private: + friend class base::RefCountedThreadSafe; + + virtual ~SnapshotHolder() {} + + gfx::Image image_; + bool completed_; + }; + scoped_ptr helper_; scoped_ptr test_window_; scoped_ptr delegate_; @@ -112,7 +166,9 @@ class SnapshotAuraTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(SnapshotAuraTest); }; -TEST_F(SnapshotAuraTest, FullScreenWindow) { +INSTANTIATE_TEST_CASE_P(SnapshotAuraTest, SnapshotAuraTest, ::testing::Bool()); + +TEST_P(SnapshotAuraTest, FullScreenWindow) { SetupTestWindow(root_window()->bounds()); WaitForDraw(); @@ -122,7 +178,7 @@ TEST_F(SnapshotAuraTest, FullScreenWindow) { EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } -TEST_F(SnapshotAuraTest, PartialBounds) { +TEST_P(SnapshotAuraTest, PartialBounds) { gfx::Rect test_bounds(100, 100, 300, 200); SetupTestWindow(test_bounds); WaitForDraw(); @@ -133,7 +189,7 @@ TEST_F(SnapshotAuraTest, PartialBounds) { EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } -TEST_F(SnapshotAuraTest, Rotated) { +TEST_P(SnapshotAuraTest, Rotated) { test_screen()->SetDisplayRotation(gfx::Display::ROTATE_90); gfx::Rect test_bounds(100, 100, 300, 200); @@ -146,7 +202,7 @@ TEST_F(SnapshotAuraTest, Rotated) { EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } -TEST_F(SnapshotAuraTest, UIScale) { +TEST_P(SnapshotAuraTest, UIScale) { const float kUIScale = 1.25f; test_screen()->SetUIScale(kUIScale); @@ -164,7 +220,7 @@ TEST_F(SnapshotAuraTest, UIScale) { EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } -TEST_F(SnapshotAuraTest, DeviceScaleFactor) { +TEST_P(SnapshotAuraTest, DeviceScaleFactor) { test_screen()->SetDeviceScaleFactor(2.0f); gfx::Rect test_bounds(100, 100, 150, 100); @@ -181,7 +237,7 @@ TEST_F(SnapshotAuraTest, DeviceScaleFactor) { EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } -TEST_F(SnapshotAuraTest, RotateAndUIScale) { +TEST_P(SnapshotAuraTest, RotateAndUIScale) { const float kUIScale = 1.25f; test_screen()->SetUIScale(kUIScale); test_screen()->SetDisplayRotation(gfx::Display::ROTATE_90); @@ -200,7 +256,7 @@ TEST_F(SnapshotAuraTest, RotateAndUIScale) { EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); } -TEST_F(SnapshotAuraTest, RotateAndUIScaleAndScaleFactor) { +TEST_P(SnapshotAuraTest, RotateAndUIScaleAndScaleFactor) { test_screen()->SetDeviceScaleFactor(2.0f); const float kUIScale = 1.25f; test_screen()->SetUIScale(kUIScale); diff --git a/ui/snapshot/snapshot_gtk.cc b/ui/snapshot/snapshot_gtk.cc index bf05a33..c904500 100644 --- a/ui/snapshot/snapshot_gtk.cc +++ b/ui/snapshot/snapshot_gtk.cc @@ -83,7 +83,7 @@ bool GrabWindowSnapshot(gfx::NativeWindow window_handle, snapshot_bounds); } -SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( +void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& snapshot_bounds, const gfx::Size& target_size, @@ -92,4 +92,12 @@ SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( NOTIMPLEMENTED(); } +void GrabWindowSnapshotAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback) { + NOTIMPLEMENTED(); +} + } // namespace ui diff --git a/ui/snapshot/snapshot_ios.mm b/ui/snapshot/snapshot_ios.mm index b121d42..425192f 100644 --- a/ui/snapshot/snapshot_ios.mm +++ b/ui/snapshot/snapshot_ios.mm @@ -23,7 +23,7 @@ bool GrabWindowSnapshot(gfx::NativeWindow window, return false; } -SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( +void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& snapshot_bounds, const gfx::Size& target_size, @@ -32,4 +32,12 @@ SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( NOTIMPLEMENTED(); } +void GrabWindowSnapshotAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback) { + NOTIMPLEMENTED(); +} + } // namespace ui diff --git a/ui/snapshot/snapshot_mac.mm b/ui/snapshot/snapshot_mac.mm index 9057e66..2ebdf54 100644 --- a/ui/snapshot/snapshot_mac.mm +++ b/ui/snapshot/snapshot_mac.mm @@ -73,7 +73,7 @@ bool GrabWindowSnapshot(gfx::NativeWindow window, snapshot_bounds); } -SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( +void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& snapshot_bounds, const gfx::Size& target_size, @@ -82,4 +82,12 @@ SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( NOTIMPLEMENTED(); } +void GrabWindowSnapshotAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback) { + NOTIMPLEMENTED(); +} + } // namespace ui diff --git a/ui/snapshot/snapshot_win.cc b/ui/snapshot/snapshot_win.cc index fc157be..da9f1cc 100644 --- a/ui/snapshot/snapshot_win.cc +++ b/ui/snapshot/snapshot_win.cc @@ -119,7 +119,7 @@ bool GrabWindowSnapshot(gfx::NativeWindow window_handle, png_representation); } -SNAPSHOT_EXPORT void GrapWindowSnapshotAsync( +void GrapWindowSnapshotAsync( gfx::NativeWindow window, const gfx::Rect& snapshot_bounds, const gfx::Size& target_size, @@ -128,6 +128,14 @@ SNAPSHOT_EXPORT void GrapWindowSnapshotAsync( NOTIMPLEMENTED(); } +void GrabWindowSnapshotAsync( + gfx::NativeWindow window, + const gfx::Rect& source_rect, + scoped_refptr background_task_runner, + const GrabWindowSnapshotAsyncCallback& callback) { + NOTIMPLEMENTED(); +} + #endif // !defined(USE_AURA) } // namespace ui -- cgit v1.1