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/shader_mask_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/SkShader.h" |
12 | #include "third_party/skia/include/effects/SkPerlinNoiseShader.h" |
13 | |
14 | namespace flutter { |
15 | namespace testing { |
16 | |
17 | using ShaderMaskLayerTest = LayerTest; |
18 | |
19 | #ifndef NDEBUG |
20 | TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) { |
21 | auto layer = |
22 | std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, SkBlendMode::kSrc); |
23 | |
24 | layer->Preroll(preroll_context(), SkMatrix()); |
25 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
26 | EXPECT_FALSE(layer->needs_painting()); |
27 | |
28 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
29 | "needs_painting\\(\\)" ); |
30 | } |
31 | |
32 | TEST_F(ShaderMaskLayerTest, PaintBeforePreollDies) { |
33 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
34 | const SkPath child_path = SkPath().addRect(child_bounds); |
35 | auto mock_layer = std::make_shared<MockLayer>(child_path); |
36 | auto layer = |
37 | std::make_shared<ShaderMaskLayer>(nullptr, kEmptyRect, SkBlendMode::kSrc); |
38 | layer->Add(mock_layer); |
39 | |
40 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
41 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
42 | "needs_painting\\(\\)" ); |
43 | } |
44 | #endif |
45 | |
46 | TEST_F(ShaderMaskLayerTest, EmptyFilter) { |
47 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
48 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
49 | const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); |
50 | const SkPath child_path = SkPath().addRect(child_bounds); |
51 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
52 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
53 | auto layer = std::make_shared<ShaderMaskLayer>(nullptr, layer_bounds, |
54 | SkBlendMode::kSrc); |
55 | layer->Add(mock_layer); |
56 | |
57 | layer->Preroll(preroll_context(), initial_transform); |
58 | EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); |
59 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
60 | EXPECT_TRUE(mock_layer->needs_painting()); |
61 | EXPECT_TRUE(layer->needs_painting()); |
62 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
63 | |
64 | SkPaint filter_paint; |
65 | filter_paint.setBlendMode(SkBlendMode::kSrc); |
66 | filter_paint.setShader(nullptr); |
67 | layer->Paint(paint_context()); |
68 | EXPECT_EQ( |
69 | mock_canvas().draw_calls(), |
70 | std::vector({MockCanvas::DrawCall{ |
71 | 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), |
72 | nullptr, 1}}, |
73 | MockCanvas::DrawCall{ |
74 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
75 | MockCanvas::DrawCall{ |
76 | 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( |
77 | layer_bounds.fLeft, layer_bounds.fTop)}}, |
78 | MockCanvas::DrawCall{ |
79 | 1, MockCanvas::DrawRectData{SkRect::MakeWH( |
80 | layer_bounds.width(), |
81 | layer_bounds.height()), |
82 | filter_paint}}, |
83 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
84 | } |
85 | |
86 | TEST_F(ShaderMaskLayerTest, SimpleFilter) { |
87 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
88 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
89 | const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); |
90 | const SkPath child_path = SkPath().addRect(child_bounds); |
91 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
92 | auto layer_filter = |
93 | SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); |
94 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
95 | auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds, |
96 | SkBlendMode::kSrc); |
97 | layer->Add(mock_layer); |
98 | |
99 | layer->Preroll(preroll_context(), initial_transform); |
100 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
101 | EXPECT_TRUE(layer->needs_painting()); |
102 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
103 | |
104 | SkPaint filter_paint; |
105 | filter_paint.setBlendMode(SkBlendMode::kSrc); |
106 | filter_paint.setShader(layer_filter); |
107 | layer->Paint(paint_context()); |
108 | EXPECT_EQ( |
109 | mock_canvas().draw_calls(), |
110 | std::vector({MockCanvas::DrawCall{ |
111 | 0, MockCanvas::SaveLayerData{child_bounds, SkPaint(), |
112 | nullptr, 1}}, |
113 | MockCanvas::DrawCall{ |
114 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
115 | MockCanvas::DrawCall{ |
116 | 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( |
117 | layer_bounds.fLeft, layer_bounds.fTop)}}, |
118 | MockCanvas::DrawCall{ |
119 | 1, MockCanvas::DrawRectData{SkRect::MakeWH( |
120 | layer_bounds.width(), |
121 | layer_bounds.height()), |
122 | filter_paint}}, |
123 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
124 | } |
125 | |
126 | TEST_F(ShaderMaskLayerTest, MultipleChildren) { |
127 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
128 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); |
129 | const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 6.5f, 6.5f); |
130 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
131 | const SkPath child_path2 = |
132 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
133 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
134 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
135 | auto layer_filter = |
136 | SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); |
137 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
138 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
139 | auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds, |
140 | SkBlendMode::kSrc); |
141 | layer->Add(mock_layer1); |
142 | layer->Add(mock_layer2); |
143 | |
144 | SkRect children_bounds = child_path1.getBounds(); |
145 | children_bounds.join(child_path2.getBounds()); |
146 | layer->Preroll(preroll_context(), initial_transform); |
147 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
148 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
149 | EXPECT_EQ(layer->paint_bounds(), children_bounds); |
150 | EXPECT_TRUE(mock_layer1->needs_painting()); |
151 | EXPECT_TRUE(mock_layer2->needs_painting()); |
152 | EXPECT_TRUE(layer->needs_painting()); |
153 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
154 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
155 | |
156 | SkPaint filter_paint; |
157 | filter_paint.setBlendMode(SkBlendMode::kSrc); |
158 | filter_paint.setShader(layer_filter); |
159 | layer->Paint(paint_context()); |
160 | EXPECT_EQ( |
161 | mock_canvas().draw_calls(), |
162 | std::vector({MockCanvas::DrawCall{ |
163 | 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), |
164 | nullptr, 1}}, |
165 | MockCanvas::DrawCall{ |
166 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
167 | MockCanvas::DrawCall{ |
168 | 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
169 | MockCanvas::DrawCall{ |
170 | 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( |
171 | layer_bounds.fLeft, layer_bounds.fTop)}}, |
172 | MockCanvas::DrawCall{ |
173 | 1, MockCanvas::DrawRectData{SkRect::MakeWH( |
174 | layer_bounds.width(), |
175 | layer_bounds.height()), |
176 | filter_paint}}, |
177 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
178 | } |
179 | |
180 | TEST_F(ShaderMaskLayerTest, Nested) { |
181 | const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); |
182 | const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 7.5f, 8.5f); |
183 | const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); |
184 | const SkPath child_path1 = SkPath().addRect(child_bounds); |
185 | const SkPath child_path2 = |
186 | SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); |
187 | const SkPaint child_paint1 = SkPaint(SkColors::kYellow); |
188 | const SkPaint child_paint2 = SkPaint(SkColors::kCyan); |
189 | auto layer_filter1 = |
190 | SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); |
191 | auto layer_filter2 = |
192 | SkPerlinNoiseShader::MakeImprovedNoise(2.0f, 2.0f, 2, 2.0f); |
193 | auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
194 | auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
195 | auto layer1 = std::make_shared<ShaderMaskLayer>(layer_filter1, layer_bounds, |
196 | SkBlendMode::kSrc); |
197 | auto layer2 = std::make_shared<ShaderMaskLayer>(layer_filter2, layer_bounds, |
198 | SkBlendMode::kSrc); |
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(child_path2.getBounds()); |
205 | layer1->Preroll(preroll_context(), initial_transform); |
206 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
207 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
208 | EXPECT_EQ(layer1->paint_bounds(), children_bounds); |
209 | EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); |
210 | EXPECT_TRUE(mock_layer1->needs_painting()); |
211 | EXPECT_TRUE(mock_layer2->needs_painting()); |
212 | EXPECT_TRUE(layer1->needs_painting()); |
213 | EXPECT_TRUE(layer2->needs_painting()); |
214 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
215 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
216 | |
217 | SkPaint filter_paint1, filter_paint2; |
218 | filter_paint1.setBlendMode(SkBlendMode::kSrc); |
219 | filter_paint2.setBlendMode(SkBlendMode::kSrc); |
220 | filter_paint1.setShader(layer_filter1); |
221 | filter_paint2.setShader(layer_filter2); |
222 | layer1->Paint(paint_context()); |
223 | EXPECT_EQ( |
224 | mock_canvas().draw_calls(), |
225 | std::vector( |
226 | {MockCanvas::DrawCall{ |
227 | 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), nullptr, |
228 | 1}}, |
229 | MockCanvas::DrawCall{ |
230 | 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
231 | MockCanvas::DrawCall{ |
232 | 1, MockCanvas::SaveLayerData{child_path2.getBounds(), SkPaint(), |
233 | nullptr, 2}}, |
234 | MockCanvas::DrawCall{ |
235 | 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, |
236 | MockCanvas::DrawCall{ |
237 | 2, MockCanvas::ConcatMatrixData{SkMatrix::Translate( |
238 | layer_bounds.fLeft, layer_bounds.fTop)}}, |
239 | MockCanvas::DrawCall{ |
240 | 2, |
241 | MockCanvas::DrawRectData{ |
242 | SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), |
243 | filter_paint2}}, |
244 | MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, |
245 | MockCanvas::DrawCall{ |
246 | 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( |
247 | layer_bounds.fLeft, layer_bounds.fTop)}}, |
248 | MockCanvas::DrawCall{ |
249 | 1, |
250 | MockCanvas::DrawRectData{ |
251 | SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), |
252 | filter_paint1}}, |
253 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
254 | } |
255 | |
256 | TEST_F(ShaderMaskLayerTest, Readback) { |
257 | auto initial_transform = SkMatrix(); |
258 | const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); |
259 | auto layer_filter = |
260 | SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); |
261 | auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds, |
262 | SkBlendMode::kSrc); |
263 | |
264 | // ShaderMaskLayer does not read from surface |
265 | preroll_context()->surface_needs_readback = false; |
266 | layer->Preroll(preroll_context(), initial_transform); |
267 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
268 | |
269 | // ShaderMaskLayer blocks child with readback |
270 | auto mock_layer = |
271 | std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true); |
272 | layer->Add(mock_layer); |
273 | preroll_context()->surface_needs_readback = false; |
274 | layer->Preroll(preroll_context(), initial_transform); |
275 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
276 | } |
277 | |
278 | } // namespace testing |
279 | } // namespace flutter |
280 | |