1 | // Copyright 2013 The Flutter 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 "flutter/flow/layers/image_filter_layer.h" |
6 | |
7 | #include "flutter/flow/testing/layer_test.h" |
8 | #include "flutter/flow/testing/mock_layer.h" |
9 | #include "flutter/fml/macros.h" |
10 | #include "flutter/testing/mock_canvas.h" |
11 | #include "third_party/skia/include/core/SkImageFilter.h" |
12 | |
13 | namespace flutter { |
14 | namespace testing { |
15 | |
16 | using ImageFilterLayerTest = LayerTest; |
17 | |
18 | #ifndef NDEBUG |
19 | TEST_F(ImageFilterLayerTest, PaintingEmptyLayerDies) { |
20 | auto layer = std::make_shared<ImageFilterLayer>(sk_sp<SkImageFilter>()); |
21 | |
22 | layer->Preroll(preroll_context(), SkMatrix()); |
23 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
24 | EXPECT_FALSE(layer->needs_painting()); |
25 | EXPECT_FALSE(layer->needs_system_composite()); |
26 | |
27 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
28 | "needs_painting\\(\\)" ); |
29 | } |
30 | |
31 | TEST_F(ImageFilterLayerTest, PaintBeforePrerollDies) { |
32 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
33 | const SkPath child_path = SkPath().addRect(child_bounds); |
34 | auto mock_layer = std::make_shared<MockLayer>(child_path); |
35 | auto layer = std::make_shared<ImageFilterLayer>(sk_sp<SkImageFilter>()); |
36 | layer->Add(mock_layer); |
37 | |
38 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
39 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
40 | "needs_painting\\(\\)" ); |
41 | } |
42 | #endif |
43 | |
44 | TEST_F(ImageFilterLayerTest, EmptyFilter) { |
45 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
46 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
47 | const SkPath child_path = SkPath().addRect(child_bounds); |
48 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
49 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
50 | auto layer = std::make_shared<ImageFilterLayer>(nullptr); |
51 | layer->Add(mock_layer); |
52 | |
53 | layer->Preroll(preroll_context(), initial_transform); |
54 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
55 | EXPECT_TRUE(layer->needs_painting()); |
56 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
57 | |
58 | SkPaint filter_paint; |
59 | filter_paint.setImageFilter(nullptr); |
60 | layer->Paint(paint_context()); |
61 | EXPECT_EQ(mock_canvas().draw_calls(), |
62 | std::vector({ |
63 | MockCanvas::DrawCall{ |
64 | 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, |
65 | nullptr, 1}}, |
66 | MockCanvas::DrawCall{ |
67 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
68 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, |
69 | })); |
70 | } |
71 | |
72 | TEST_F(ImageFilterLayerTest, SimpleFilter) { |
73 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
74 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
75 | const SkPath child_path = SkPath().addRect(child_bounds); |
76 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
77 | auto layer_filter = SkImageFilter::MakeMatrixFilter( |
78 | SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
79 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
80 | auto layer = std::make_shared<ImageFilterLayer>(layer_filter); |
81 | layer->Add(mock_layer); |
82 | |
83 | const SkRect child_rounded_bounds = |
84 | SkRect::MakeLTRB(5.0f, 6.0f, 21.0f, 22.0f); |
85 | |
86 | layer->Preroll(preroll_context(), initial_transform); |
87 | EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds); |
88 | EXPECT_TRUE(layer->needs_painting()); |
89 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
90 | |
91 | SkPaint filter_paint; |
92 | filter_paint.setImageFilter(layer_filter); |
93 | layer->Paint(paint_context()); |
94 | EXPECT_EQ(mock_canvas().draw_calls(), |
95 | std::vector({ |
96 | MockCanvas::DrawCall{ |
97 | 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, |
98 | nullptr, 1}}, |
99 | MockCanvas::DrawCall{ |
100 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
101 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, |
102 | })); |
103 | } |
104 | |
105 | TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { |
106 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
107 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
108 | const SkPath child_path = SkPath().addRect(child_bounds); |
109 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
110 | const SkMatrix filter_transform = SkMatrix::Scale(2.0, 2.0); |
111 | auto layer_filter = SkImageFilter::MakeMatrixFilter( |
112 | filter_transform, SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
113 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
114 | auto layer = std::make_shared<ImageFilterLayer>(layer_filter); |
115 | layer->Add(mock_layer); |
116 | |
117 | const SkRect filter_bounds = SkRect::MakeLTRB(10.0f, 12.0f, 42.0f, 44.0f); |
118 | |
119 | layer->Preroll(preroll_context(), initial_transform); |
120 | EXPECT_EQ(layer->paint_bounds(), filter_bounds); |
121 | EXPECT_TRUE(layer->needs_painting()); |
122 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
123 | |
124 | SkPaint filter_paint; |
125 | filter_paint.setImageFilter(layer_filter); |
126 | layer->Paint(paint_context()); |
127 | EXPECT_EQ(mock_canvas().draw_calls(), |
128 | std::vector({ |
129 | MockCanvas::DrawCall{ |
130 | 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, |
131 | nullptr, 1}}, |
132 | MockCanvas::DrawCall{ |
133 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
134 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, |
135 | })); |
136 | } |
137 | |
138 | TEST_F(ImageFilterLayerTest, MultipleChildren) { |
139 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
140 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); |
141 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
142 | const SkPath child_path2 = |
143 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
144 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
145 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
146 | auto layer_filter = SkImageFilter::MakeMatrixFilter( |
147 | SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
148 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
149 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
150 | auto layer = std::make_shared<ImageFilterLayer>(layer_filter); |
151 | layer->Add(mock_layer1); |
152 | layer->Add(mock_layer2); |
153 | |
154 | SkRect children_bounds = child_path1.getBounds(); |
155 | children_bounds.join(child_path2.getBounds()); |
156 | SkRect children_rounded_bounds = SkRect::Make(children_bounds.roundOut()); |
157 | |
158 | layer->Preroll(preroll_context(), initial_transform); |
159 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
160 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
161 | EXPECT_EQ(layer->paint_bounds(), children_rounded_bounds); |
162 | EXPECT_TRUE(mock_layer1->needs_painting()); |
163 | EXPECT_TRUE(mock_layer2->needs_painting()); |
164 | EXPECT_TRUE(layer->needs_painting()); |
165 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
166 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
167 | |
168 | SkPaint filter_paint; |
169 | filter_paint.setImageFilter(layer_filter); |
170 | layer->Paint(paint_context()); |
171 | EXPECT_EQ( |
172 | mock_canvas().draw_calls(), |
173 | std::vector({MockCanvas::DrawCall{ |
174 | 0, MockCanvas::SaveLayerData{children_bounds, |
175 | filter_paint, nullptr, 1}}, |
176 | MockCanvas::DrawCall{ |
177 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
178 | MockCanvas::DrawCall{ |
179 | 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
180 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
181 | } |
182 | |
183 | TEST_F(ImageFilterLayerTest, Nested) { |
184 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
185 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); |
186 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
187 | const SkPath child_path2 = |
188 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
189 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
190 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
191 | auto layer_filter1 = SkImageFilter::MakeMatrixFilter( |
192 | SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
193 | auto layer_filter2 = SkImageFilter::MakeMatrixFilter( |
194 | SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
195 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
196 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
197 | auto layer1 = std::make_shared<ImageFilterLayer>(layer_filter1); |
198 | auto layer2 = std::make_shared<ImageFilterLayer>(layer_filter2); |
199 | layer2->Add(mock_layer2); |
200 | layer1->Add(mock_layer1); |
201 | layer1->Add(layer2); |
202 | |
203 | SkRect children_bounds = child_path1.getBounds(); |
204 | children_bounds.join(SkRect::Make(child_path2.getBounds().roundOut())); |
205 | const SkRect children_rounded_bounds = |
206 | SkRect::Make(children_bounds.roundOut()); |
207 | const SkRect mock_layer2_rounded_bounds = |
208 | SkRect::Make(child_path2.getBounds().roundOut()); |
209 | |
210 | layer1->Preroll(preroll_context(), initial_transform); |
211 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
212 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
213 | EXPECT_EQ(layer1->paint_bounds(), children_rounded_bounds); |
214 | EXPECT_EQ(layer2->paint_bounds(), mock_layer2_rounded_bounds); |
215 | EXPECT_TRUE(mock_layer1->needs_painting()); |
216 | EXPECT_TRUE(mock_layer2->needs_painting()); |
217 | EXPECT_TRUE(layer1->needs_painting()); |
218 | EXPECT_TRUE(layer2->needs_painting()); |
219 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
220 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
221 | |
222 | SkPaint filter_paint1, filter_paint2; |
223 | filter_paint1.setImageFilter(layer_filter1); |
224 | filter_paint2.setImageFilter(layer_filter2); |
225 | layer1->Paint(paint_context()); |
226 | EXPECT_EQ(mock_canvas().draw_calls(), |
227 | std::vector({ |
228 | MockCanvas::DrawCall{ |
229 | 0, MockCanvas::SaveLayerData{children_bounds, filter_paint1, |
230 | nullptr, 1}}, |
231 | MockCanvas::DrawCall{ |
232 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
233 | MockCanvas::DrawCall{ |
234 | 1, MockCanvas::SaveLayerData{child_path2.getBounds(), |
235 | filter_paint2, nullptr, 2}}, |
236 | MockCanvas::DrawCall{ |
237 | 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
238 | MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, |
239 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, |
240 | })); |
241 | } |
242 | |
243 | TEST_F(ImageFilterLayerTest, Readback) { |
244 | auto layer_filter = SkImageFilter::MakeMatrixFilter( |
245 | SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
246 | auto initial_transform = SkMatrix(); |
247 | |
248 | // ImageFilterLayer does not read from surface |
249 | auto layer = std::make_shared<ImageFilterLayer>(layer_filter); |
250 | preroll_context()->surface_needs_readback = false; |
251 | layer->Preroll(preroll_context(), initial_transform); |
252 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
253 | |
254 | // ImageFilterLayer blocks child with readback |
255 | auto mock_layer = |
256 | std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true); |
257 | layer->Add(mock_layer); |
258 | preroll_context()->surface_needs_readback = false; |
259 | layer->Preroll(preroll_context(), initial_transform); |
260 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
261 | } |
262 | |
263 | TEST_F(ImageFilterLayerTest, ChildIsCached) { |
264 | auto layer_filter = SkImageFilter::MakeMatrixFilter( |
265 | SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
266 | auto initial_transform = SkMatrix::Translate(50.0, 25.5); |
267 | auto other_transform = SkMatrix::Scale(1.0, 2.0); |
268 | const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); |
269 | auto mock_layer = std::make_shared<MockLayer>(child_path); |
270 | auto layer = std::make_shared<ImageFilterLayer>(layer_filter); |
271 | layer->Add(mock_layer); |
272 | |
273 | SkMatrix cache_ctm = initial_transform; |
274 | SkCanvas cache_canvas; |
275 | cache_canvas.setMatrix(cache_ctm); |
276 | SkCanvas other_canvas; |
277 | other_canvas.setMatrix(other_transform); |
278 | |
279 | use_mock_raster_cache(); |
280 | |
281 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); |
282 | EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); |
283 | EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); |
284 | |
285 | layer->Preroll(preroll_context(), initial_transform); |
286 | |
287 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); |
288 | EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas)); |
289 | EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas)); |
290 | } |
291 | |
292 | TEST_F(ImageFilterLayerTest, ChildrenNotCached) { |
293 | auto layer_filter = SkImageFilter::MakeMatrixFilter( |
294 | SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); |
295 | auto initial_transform = SkMatrix::Translate(50.0, 25.5); |
296 | auto other_transform = SkMatrix::Scale(1.0, 2.0); |
297 | const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); |
298 | const SkPath child_path2 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); |
299 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1); |
300 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2); |
301 | auto layer = std::make_shared<ImageFilterLayer>(layer_filter); |
302 | layer->Add(mock_layer1); |
303 | layer->Add(mock_layer2); |
304 | |
305 | SkMatrix cache_ctm = initial_transform; |
306 | SkCanvas cache_canvas; |
307 | cache_canvas.setMatrix(cache_ctm); |
308 | SkCanvas other_canvas; |
309 | other_canvas.setMatrix(other_transform); |
310 | |
311 | use_mock_raster_cache(); |
312 | |
313 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); |
314 | EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); |
315 | EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); |
316 | EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); |
317 | EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); |
318 | |
319 | layer->Preroll(preroll_context(), initial_transform); |
320 | |
321 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); |
322 | EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas)); |
323 | EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas)); |
324 | EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas)); |
325 | EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); |
326 | } |
327 | |
328 | } // namespace testing |
329 | } // namespace flutter |
330 | |