1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/snapshot/snapshot.h" 6 7 #include "base/bind.h" 8 #include "base/test/test_simple_task_runner.h" 9 #include "testing/gtest/include/gtest/gtest.h" 10 #include "ui/aura/test/aura_test_helper.h" 11 #include "ui/aura/test/test_screen.h" 12 #include "ui/aura/test/test_window_delegate.h" 13 #include "ui/aura/test/test_windows.h" 14 #include "ui/aura/window.h" 15 #include "ui/aura/window_event_dispatcher.h" 16 #include "ui/compositor/compositor.h" 17 #include "ui/compositor/layer.h" 18 #include "ui/compositor/test/context_factories_for_test.h" 19 #include "ui/compositor/test/draw_waiter_for_test.h" 20 #include "ui/gfx/canvas.h" 21 #include "ui/gfx/gfx_paths.h" 22 #include "ui/gfx/image/image.h" 23 #include "ui/gfx/rect.h" 24 #include "ui/gfx/size_conversions.h" 25 #include "ui/gfx/transform.h" 26 #include "ui/gl/gl_implementation.h" 27 #include "ui/wm/core/default_activation_client.h" 28 29 namespace ui { 30 namespace { 31 32 SkColor GetExpectedColorForPoint(int x, int y) { 33 return SkColorSetRGB(std::min(x, 255), std::min(y, 255), 0); 34 } 35 36 // Paint simple rectangle on the specified aura window. 37 class TestPaintingWindowDelegate : public aura::test::TestWindowDelegate { 38 public: 39 explicit TestPaintingWindowDelegate(const gfx::Size& window_size) 40 : window_size_(window_size) { 41 } 42 43 virtual ~TestPaintingWindowDelegate() { 44 } 45 46 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 47 for (int y = 0; y < window_size_.height(); ++y) { 48 for (int x = 0; x < window_size_.width(); ++x) 49 canvas->FillRect(gfx::Rect(x, y, 1, 1), GetExpectedColorForPoint(x, y)); 50 } 51 } 52 53 private: 54 gfx::Size window_size_; 55 56 DISALLOW_COPY_AND_ASSIGN(TestPaintingWindowDelegate); 57 }; 58 59 size_t GetFailedPixelsCountWithScaleFactor(const gfx::Image& image, 60 int scale_factor) { 61 const SkBitmap* bitmap = image.ToSkBitmap(); 62 uint32* bitmap_data = reinterpret_cast<uint32*>( 63 bitmap->pixelRef()->pixels()); 64 size_t result = 0; 65 for (int y = 0; y < bitmap->height(); y += scale_factor) { 66 for (int x = 0; x < bitmap->width(); x += scale_factor) { 67 if (static_cast<SkColor>(bitmap_data[x + y * bitmap->width()]) != 68 GetExpectedColorForPoint(x / scale_factor, y / scale_factor)) { 69 ++result; 70 } 71 } 72 } 73 return result; 74 } 75 76 size_t GetFailedPixelsCount(const gfx::Image& image) { 77 return GetFailedPixelsCountWithScaleFactor(image, 1); 78 } 79 80 } // namespace 81 82 class SnapshotAuraTest : public testing::Test { 83 public: 84 SnapshotAuraTest() {} 85 virtual ~SnapshotAuraTest() {} 86 87 virtual void SetUp() OVERRIDE { 88 testing::Test::SetUp(); 89 90 // The ContextFactory must exist before any Compositors are created. 91 // Snapshot test tests real drawing and readback, so needs pixel output. 92 bool enable_pixel_output = true; 93 ui::ContextFactory* context_factory = 94 ui::InitializeContextFactoryForTests(enable_pixel_output); 95 96 helper_.reset( 97 new aura::test::AuraTestHelper(base::MessageLoopForUI::current())); 98 helper_->SetUp(context_factory); 99 new ::wm::DefaultActivationClient(helper_->root_window()); 100 } 101 102 virtual void TearDown() OVERRIDE { 103 test_window_.reset(); 104 delegate_.reset(); 105 helper_->RunAllPendingInMessageLoop(); 106 helper_->TearDown(); 107 ui::TerminateContextFactoryForTests(); 108 testing::Test::TearDown(); 109 } 110 111 protected: 112 aura::Window* test_window() { return test_window_.get(); } 113 aura::Window* root_window() { return helper_->root_window(); } 114 aura::TestScreen* test_screen() { return helper_->test_screen(); } 115 116 void WaitForDraw() { 117 helper_->host()->compositor()->ScheduleDraw(); 118 ui::DrawWaiterForTest::Wait(helper_->host()->compositor()); 119 } 120 121 void SetupTestWindow(const gfx::Rect& window_bounds) { 122 delegate_.reset(new TestPaintingWindowDelegate(window_bounds.size())); 123 test_window_.reset(aura::test::CreateTestWindowWithDelegate( 124 delegate_.get(), 0, window_bounds, root_window())); 125 } 126 127 gfx::Image GrabSnapshotForTestWindow() { 128 gfx::Rect source_rect(test_window_->bounds().size()); 129 aura::Window::ConvertRectToTarget( 130 test_window(), root_window(), &source_rect); 131 132 scoped_refptr<base::TestSimpleTaskRunner> task_runner( 133 new base::TestSimpleTaskRunner()); 134 scoped_refptr<SnapshotHolder> holder(new SnapshotHolder); 135 ui::GrabWindowSnapshotAsync( 136 root_window(), 137 source_rect, 138 task_runner, 139 base::Bind(&SnapshotHolder::SnapshotCallback, holder)); 140 141 // Wait for copy response. 142 WaitForDraw(); 143 // Run internal snapshot callback to scale/rotate response image. 144 task_runner->RunUntilIdle(); 145 // Run SnapshotHolder callback. 146 helper_->RunAllPendingInMessageLoop(); 147 148 if (holder->completed()) 149 return holder->image(); 150 151 // Callback never called. 152 NOTREACHED(); 153 return gfx::Image(); 154 } 155 156 private: 157 class SnapshotHolder : public base::RefCountedThreadSafe<SnapshotHolder> { 158 public: 159 SnapshotHolder() : completed_(false) {} 160 161 void SnapshotCallback(scoped_refptr<base::RefCountedBytes> png_data) { 162 DCHECK(!completed_); 163 image_ = gfx::Image::CreateFrom1xPNGBytes(&(png_data->data()[0]), 164 png_data->size()); 165 completed_ = true; 166 } 167 bool completed() const { 168 return completed_; 169 }; 170 const gfx::Image& image() const { return image_; } 171 172 private: 173 friend class base::RefCountedThreadSafe<SnapshotHolder>; 174 175 virtual ~SnapshotHolder() {} 176 177 gfx::Image image_; 178 bool completed_; 179 }; 180 181 scoped_ptr<aura::test::AuraTestHelper> helper_; 182 scoped_ptr<aura::Window> test_window_; 183 scoped_ptr<TestPaintingWindowDelegate> delegate_; 184 std::vector<unsigned char> png_representation_; 185 186 DISALLOW_COPY_AND_ASSIGN(SnapshotAuraTest); 187 }; 188 189 TEST_F(SnapshotAuraTest, FullScreenWindow) { 190 SetupTestWindow(root_window()->bounds()); 191 WaitForDraw(); 192 193 gfx::Image snapshot = GrabSnapshotForTestWindow(); 194 EXPECT_EQ(test_window()->bounds().size().ToString(), 195 snapshot.Size().ToString()); 196 EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); 197 } 198 199 TEST_F(SnapshotAuraTest, PartialBounds) { 200 gfx::Rect test_bounds(100, 100, 300, 200); 201 SetupTestWindow(test_bounds); 202 WaitForDraw(); 203 204 gfx::Image snapshot = GrabSnapshotForTestWindow(); 205 EXPECT_EQ(test_bounds.size().ToString(), snapshot.Size().ToString()); 206 EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); 207 } 208 209 TEST_F(SnapshotAuraTest, Rotated) { 210 test_screen()->SetDisplayRotation(gfx::Display::ROTATE_90); 211 212 gfx::Rect test_bounds(100, 100, 300, 200); 213 SetupTestWindow(test_bounds); 214 WaitForDraw(); 215 216 gfx::Image snapshot = GrabSnapshotForTestWindow(); 217 EXPECT_EQ(test_bounds.size().ToString(), snapshot.Size().ToString()); 218 EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); 219 } 220 221 TEST_F(SnapshotAuraTest, UIScale) { 222 const float kUIScale = 1.25f; 223 test_screen()->SetUIScale(kUIScale); 224 225 gfx::Rect test_bounds(100, 100, 300, 200); 226 SetupTestWindow(test_bounds); 227 WaitForDraw(); 228 229 // Snapshot always captures the physical pixels. 230 gfx::SizeF snapshot_size(test_bounds.size()); 231 232 gfx::Image snapshot = GrabSnapshotForTestWindow(); 233 EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), 234 snapshot.Size().ToString()); 235 EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); 236 } 237 238 TEST_F(SnapshotAuraTest, DeviceScaleFactor) { 239 test_screen()->SetDeviceScaleFactor(2.0f); 240 241 gfx::Rect test_bounds(100, 100, 150, 100); 242 SetupTestWindow(test_bounds); 243 WaitForDraw(); 244 245 // Snapshot always captures the physical pixels. 246 gfx::SizeF snapshot_size(test_bounds.size()); 247 snapshot_size.Scale(2.0f); 248 249 gfx::Image snapshot = GrabSnapshotForTestWindow(); 250 EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), 251 snapshot.Size().ToString()); 252 EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 2)); 253 } 254 255 TEST_F(SnapshotAuraTest, RotateAndUIScale) { 256 const float kUIScale = 1.25f; 257 test_screen()->SetUIScale(kUIScale); 258 test_screen()->SetDisplayRotation(gfx::Display::ROTATE_90); 259 260 gfx::Rect test_bounds(100, 100, 300, 200); 261 SetupTestWindow(test_bounds); 262 WaitForDraw(); 263 264 // Snapshot always captures the physical pixels. 265 gfx::SizeF snapshot_size(test_bounds.size()); 266 267 gfx::Image snapshot = GrabSnapshotForTestWindow(); 268 EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), 269 snapshot.Size().ToString()); 270 EXPECT_EQ(0u, GetFailedPixelsCount(snapshot)); 271 } 272 273 TEST_F(SnapshotAuraTest, RotateAndUIScaleAndScaleFactor) { 274 test_screen()->SetDeviceScaleFactor(2.0f); 275 const float kUIScale = 1.25f; 276 test_screen()->SetUIScale(kUIScale); 277 test_screen()->SetDisplayRotation(gfx::Display::ROTATE_90); 278 279 gfx::Rect test_bounds(20, 30, 150, 100); 280 SetupTestWindow(test_bounds); 281 WaitForDraw(); 282 283 // Snapshot always captures the physical pixels. 284 gfx::SizeF snapshot_size(test_bounds.size()); 285 snapshot_size.Scale(2.0f); 286 287 gfx::Image snapshot = GrabSnapshotForTestWindow(); 288 EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(), 289 snapshot.Size().ToString()); 290 EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 2)); 291 } 292 293 } // namespace ui 294