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
13namespace flutter {
14namespace testing {
15
16using ImageFilterLayerTest = LayerTest;
17
18#ifndef NDEBUG
19TEST_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
31TEST_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
44TEST_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
72TEST_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
105TEST_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
138TEST_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
183TEST_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
243TEST_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
263TEST_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
292TEST_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