1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "pdf/paint_manager.h"
9 #include "base/files/file_path.h"
10 #include "base/run_loop.h"
11 #include "base/strings/string_piece.h"
12 #include "cc/test/pixel_comparator.h"
13 #include "cc/test/pixel_test_utils.h"
14 #include "pdf/paint_ready_rect.h"
15 #include "pdf/test/test_helpers.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "third_party/skia/include/core/SkCanvas.h"
20 #include "third_party/skia/include/core/SkColor.h"
21 #include "third_party/skia/include/core/SkImage.h"
22 #include "third_party/skia/include/core/SkRect.h"
23 #include "third_party/skia/include/core/SkRefCnt.h"
24 #include "third_party/skia/include/core/SkSurface.h"
25 #include "ui/gfx/geometry/rect.h"
26 #include "ui/gfx/geometry/size.h"
27 #include "ui/gfx/geometry/skia_conversions.h"
29 namespace chrome_pdf {
34 using ::testing::NiceMock;
36 base::FilePath GetTestDataFilePath(base::StringPiece filename) {
37 return base::FilePath(FILE_PATH_LITERAL("paint_manager"))
38 .AppendASCII(filename);
41 class FakeClient : public PaintManager::Client {
43 MOCK_METHOD(void, InvalidatePluginContainer, (), (override));
46 (const std::vector<gfx::Rect>& paint_rects,
47 std::vector<PaintReadyRect>& ready,
48 std::vector<gfx::Rect>& pending),
50 MOCK_METHOD(void, UpdateSnapshot, (sk_sp<SkImage> snapshot), (override));
51 MOCK_METHOD(void, UpdateScale, (float scale), (override));
54 (float scale, const gfx::Vector2dF& translate),
58 class PaintManagerTest : public testing::Test {
60 void WaitForOnPaint() {
61 base::RunLoop run_loop;
62 EXPECT_CALL(client_, OnPaint).WillOnce([&run_loop] { run_loop.Quit(); });
66 sk_sp<SkImage> WaitForFlush(
67 const std::vector<gfx::Rect>& expected_paint_rects,
68 std::vector<PaintReadyRect> fake_ready,
69 std::vector<gfx::Rect> fake_pending) {
70 EXPECT_CALL(client_, OnPaint(expected_paint_rects, _, _))
71 .WillOnce([&fake_ready, &fake_pending](
72 const std::vector<gfx::Rect>& paint_rects,
73 std::vector<PaintReadyRect>& ready,
74 std::vector<gfx::Rect>& pending) {
75 ready = std::move(fake_ready);
76 pending = std::move(fake_pending);
79 sk_sp<SkImage> saved_snapshot;
80 base::RunLoop run_loop;
81 EXPECT_CALL(client_, UpdateSnapshot)
82 .WillOnce([&saved_snapshot, &run_loop](sk_sp<SkImage> snapshot) {
83 saved_snapshot = std::move(snapshot);
88 return saved_snapshot;
91 void TestPaintImage(const gfx::Size& plugin_size,
92 const gfx::Size& source_size,
93 const gfx::Rect& paint_rect,
94 const gfx::Rect& overlapped_rect) {
95 // Paint `paint_rect` from `source_size` image over a magenta background.
96 paint_manager_.SetSize(plugin_size, 1.0f);
97 sk_sp<SkImage> snapshot = WaitForFlush(
98 /*expected_paint_rects=*/{{gfx::Rect(plugin_size)}},
101 {gfx::Rect(plugin_size),
102 CreateSkiaImageForTesting(plugin_size, SK_ColorMAGENTA)},
103 {paint_rect, CreateSkiaImageForTesting(source_size, SK_ColorRED)},
105 /*fake_pending=*/{});
106 ASSERT_TRUE(snapshot);
108 // Check if snapshot has `overlapped_rect` painted red.
109 snapshot = snapshot->makeSubset(
110 static_cast<GrDirectContext*>(nullptr),
111 SkIRect::MakeWH(plugin_size.width(), plugin_size.height()));
112 ASSERT_TRUE(snapshot);
114 SkBitmap snapshot_bitmap;
115 ASSERT_TRUE(snapshot->asLegacyBitmap(&snapshot_bitmap));
117 sk_sp<SkSurface> expected_surface =
118 CreateSkiaSurfaceForTesting(plugin_size, SK_ColorMAGENTA);
119 expected_surface->getCanvas()->clipIRect(
120 gfx::RectToSkIRect(overlapped_rect));
121 expected_surface->getCanvas()->clear(SK_ColorRED);
123 SkBitmap expected_bitmap;
124 ASSERT_TRUE(expected_surface->makeImageSnapshot()->asLegacyBitmap(
127 EXPECT_TRUE(cc::MatchesBitmap(snapshot_bitmap, expected_bitmap,
128 cc::ExactPixelComparator()));
131 void TestScroll(const gfx::Vector2d& scroll_amount,
132 const gfx::Rect& expected_paint_rect,
133 base::StringPiece expected_png) {
134 // Paint non-uniform initial image.
135 gfx::Size plugin_size = paint_manager_.GetEffectiveSize();
136 ASSERT_GE(plugin_size.width(), 4);
137 ASSERT_GE(plugin_size.height(), 4);
139 sk_sp<SkSurface> initial_surface =
140 CreateSkiaSurfaceForTesting(plugin_size, SK_ColorRED);
141 initial_surface->getCanvas()->clipIRect(SkIRect::MakeLTRB(
142 1, 1, plugin_size.width() - 1, plugin_size.height() - 2));
143 initial_surface->getCanvas()->clear(SK_ColorGREEN);
145 paint_manager_.Invalidate();
146 ASSERT_TRUE(WaitForFlush(
147 /*expected_paint_rects=*/{gfx::Rect(plugin_size)},
149 {{gfx::Rect(plugin_size), initial_surface->makeImageSnapshot()}},
150 /*fake_pending=*/{}));
152 // Scroll by `scroll_amount`, painting `expected_paint_rect` magenta.
153 paint_manager_.ScrollRect(gfx::Rect(plugin_size), scroll_amount);
154 sk_sp<SkImage> snapshot = WaitForFlush(
155 /*expected_paint_rects=*/{expected_paint_rect},
157 {{expected_paint_rect,
158 CreateSkiaImageForTesting(plugin_size, SK_ColorMAGENTA)}},
159 /*fake_pending=*/{});
160 ASSERT_TRUE(snapshot);
162 // Compare snapshot to `expected_png`.
163 snapshot = snapshot->makeSubset(
164 static_cast<GrDirectContext*>(nullptr),
165 SkIRect::MakeWH(plugin_size.width(), plugin_size.height()));
166 ASSERT_TRUE(snapshot);
169 MatchesPngFile(snapshot.get(), GetTestDataFilePath(expected_png)));
172 NiceMock<FakeClient> client_;
173 PaintManager paint_manager_{&client_};
176 TEST_F(PaintManagerTest, GetNewContextSizeWhenGrowingBelowMaximum) {
177 EXPECT_EQ(gfx::Size(450, 350),
178 PaintManager::GetNewContextSize({450, 350}, {450, 349}));
179 EXPECT_EQ(gfx::Size(450, 350),
180 PaintManager::GetNewContextSize({450, 350}, {449, 350}));
183 TEST_F(PaintManagerTest, GetNewContextSizeWhenGrowingAboveMaximum) {
184 EXPECT_EQ(gfx::Size(501, 400),
185 PaintManager::GetNewContextSize({450, 350}, {451, 350}));
186 EXPECT_EQ(gfx::Size(500, 401),
187 PaintManager::GetNewContextSize({450, 350}, {450, 351}));
190 TEST_F(PaintManagerTest, GetNewContextSizeWhenShrinkingAboveMinimum) {
191 EXPECT_EQ(gfx::Size(450, 350),
192 PaintManager::GetNewContextSize({450, 350}, {350, 251}));
193 EXPECT_EQ(gfx::Size(450, 350),
194 PaintManager::GetNewContextSize({450, 350}, {351, 250}));
197 TEST_F(PaintManagerTest, GetNewContextSizeWhenShrinkingBelowMinimum) {
198 EXPECT_EQ(gfx::Size(399, 300),
199 PaintManager::GetNewContextSize({450, 350}, {349, 250}));
200 EXPECT_EQ(gfx::Size(400, 299),
201 PaintManager::GetNewContextSize({450, 350}, {350, 249}));
204 TEST_F(PaintManagerTest, Create) {
205 EXPECT_EQ(gfx::Size(0, 0), paint_manager_.GetEffectiveSize());
206 EXPECT_EQ(1.0f, paint_manager_.GetEffectiveDeviceScale());
209 TEST_F(PaintManagerTest, SetSizeWithoutPaint) {
210 EXPECT_CALL(client_, InvalidatePluginContainer).Times(0);
211 paint_manager_.SetSize({400, 300}, 2.0f);
213 EXPECT_EQ(gfx::Size(400, 300), paint_manager_.GetEffectiveSize());
214 EXPECT_EQ(2.0f, paint_manager_.GetEffectiveDeviceScale());
217 TEST_F(PaintManagerTest, SetSizeWithPaint) {
218 paint_manager_.SetSize({400, 300}, 2.0f);
220 EXPECT_CALL(client_, InvalidatePluginContainer);
221 EXPECT_CALL(client_, UpdateScale(0.5f));
225 TEST_F(PaintManagerTest, SetTransformWithoutSurface) {
226 EXPECT_CALL(client_, UpdateLayerTransform).Times(0);
227 paint_manager_.SetTransform(0.25f, {150, 50}, {-4, 8},
228 /*schedule_flush=*/true);
231 TEST_F(PaintManagerTest, SetTransformWithSurface) {
232 paint_manager_.SetSize({400, 300}, 2.0f);
236 UpdateLayerTransform(0.25f, gfx::Vector2dF(116.5f, 29.5f)));
237 paint_manager_.SetTransform(0.25f, {150, 50}, {-4, 8},
238 /*schedule_flush=*/true);
242 TEST_F(PaintManagerTest, ClearTransform) {
243 paint_manager_.SetSize({400, 300}, 2.0f);
246 EXPECT_CALL(client_, UpdateLayerTransform(1.0f, gfx::Vector2dF()));
247 paint_manager_.ClearTransform();
250 TEST_F(PaintManagerTest, DoPaintFirst) {
251 paint_manager_.SetSize({400, 300}, 2.0f);
253 sk_sp<SkImage> snapshot =
254 WaitForFlush(/*expected_paint_rects=*/{{0, 0, 400, 300}},
256 {{{25, 50, 200, 100},
257 CreateSkiaImageForTesting({200, 100}, SK_ColorGRAY)}},
258 /*fake_pending=*/{});
260 EXPECT_TRUE(MatchesPngFile(snapshot.get(),
261 GetTestDataFilePath("do_paint_first.png")));
264 TEST_F(PaintManagerTest, PaintImage) {
265 // Painted area is within the plugin area and the source image.
266 TestPaintImage(/*plugin_size=*/{20, 20}, /*source_size=*/{15, 15},
267 /*paint_rect=*/{0, 0, 10, 10},
268 /*overlapped_rect=*/{0, 0, 10, 10});
270 // Painted area straddles the plugin area and the source image.
271 TestPaintImage(/*plugin_size=*/{50, 30}, /*source_size=*/{30, 50},
272 /*paint_rect=*/{10, 10, 30, 30},
273 /*overlapped_rect=*/{10, 10, 20, 20});
275 // Painted area is outside the plugin area.
276 TestPaintImage(/*plugin_size=*/{10, 10}, /*source_size=*/{30, 30},
277 /*paint_rect=*/{10, 10, 10, 10},
278 /*overlapped_rect=*/{0, 0, 0, 0});
280 // Painted area is outside the source image.
281 TestPaintImage(/*plugin_size=*/{15, 15}, /*source_size=*/{5, 5},
282 /*paint_rect=*/{10, 10, 5, 5},
283 /*overlapped_rect=*/{0, 0, 0, 0});
286 TEST_F(PaintManagerTest, Scroll) {
287 paint_manager_.SetSize({4, 5}, 1.0f);
289 TestScroll(/*scroll_amount=*/{1, 0}, /*expected_paint_rect=*/{0, 0, 1, 5},
291 TestScroll(/*scroll_amount=*/{-2, 0}, /*expected_paint_rect=*/{2, 0, 2, 5},
293 TestScroll(/*scroll_amount=*/{0, 3}, /*expected_paint_rect=*/{0, 0, 4, 3},
295 TestScroll(/*scroll_amount=*/{0, -3}, /*expected_paint_rect=*/{0, 2, 4, 3},
299 TEST_F(PaintManagerTest, ScrollIgnored) {
300 paint_manager_.SetSize({4, 5}, 1.0f);
302 // Scroll to the edge of the plugin area.
303 TestScroll(/*scroll_amount=*/{4, 0}, /*expected_paint_rect=*/{0, 0, 4, 5},
304 "scroll_ignored.png");
305 TestScroll(/*scroll_amount=*/{-4, 0}, /*expected_paint_rect=*/{0, 0, 4, 5},
306 "scroll_ignored.png");
307 TestScroll(/*scroll_amount=*/{0, 5}, /*expected_paint_rect=*/{0, 0, 4, 5},
308 "scroll_ignored.png");
309 TestScroll(/*scroll_amount=*/{0, -5}, /*expected_paint_rect=*/{0, 0, 4, 5},
310 "scroll_ignored.png");
312 // Scroll outside of the plugin area.
313 TestScroll(/*scroll_amount=*/{5, 0}, /*expected_paint_rect=*/{0, 0, 4, 5},
314 "scroll_ignored.png");
315 TestScroll(/*scroll_amount=*/{-7, 0}, /*expected_paint_rect=*/{0, 0, 4, 5},
316 "scroll_ignored.png");
317 TestScroll(/*scroll_amount=*/{0, 8}, /*expected_paint_rect=*/{0, 0, 4, 5},
318 "scroll_ignored.png");
319 TestScroll(/*scroll_amount=*/{0, -9}, /*expected_paint_rect=*/{0, 0, 4, 5},
320 "scroll_ignored.png");
325 } // namespace chrome_pdf