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
29namespace ui {
30namespace {
31
32SkColor 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.
37class 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
59size_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
76size_t GetFailedPixelsCount(const gfx::Image& image) {
77  return GetFailedPixelsCountWithScaleFactor(image, 1);
78}
79
80}  // namespace
81
82class 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
189TEST_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
199TEST_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
209TEST_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
221TEST_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
238TEST_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
255TEST_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
273TEST_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