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/layers/layer_tree.h" |
8 | #include "flutter/flow/layers/opacity_layer.h" |
9 | #include "flutter/flow/raster_cache.h" |
10 | #include "flutter/flow/raster_cache_util.h" |
11 | #include "flutter/flow/testing/layer_test.h" |
12 | #include "flutter/flow/testing/mock_layer.h" |
13 | #include "flutter/fml/macros.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | // TODO(zanderso): https://github.com/flutter/flutter/issues/127701 |
17 | // NOLINTBEGIN(bugprone-unchecked-optional-access) |
18 | |
19 | namespace flutter { |
20 | namespace testing { |
21 | |
22 | using ShaderMaskLayerTest = LayerTest; |
23 | |
24 | static std::shared_ptr<DlColorSource> MakeFilter(DlColor color) { |
25 | DlColor colors[] = { |
26 | color.withAlpha(alpha: 0x7f), |
27 | color, |
28 | }; |
29 | float stops[] = { |
30 | 0, |
31 | 1, |
32 | }; |
33 | return DlColorSource::MakeLinear(start_point: SkPoint::Make(x: 0, y: 0), end_point: SkPoint::Make(x: 10, y: 10), |
34 | stop_count: 2, colors, stops, tile_mode: DlTileMode::kRepeat); |
35 | } |
36 | |
37 | #ifndef NDEBUG |
38 | TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) { |
39 | auto layer = |
40 | std::make_shared<ShaderMaskLayer>(args: nullptr, args: kEmptyRect, args: DlBlendMode::kSrc); |
41 | |
42 | layer->Preroll(context: preroll_context()); |
43 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
44 | EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); |
45 | EXPECT_FALSE(layer->needs_painting(paint_context())); |
46 | |
47 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
48 | "needs_painting\\(context\\)" ); |
49 | } |
50 | |
51 | TEST_F(ShaderMaskLayerTest, PaintBeforePrerollDies) { |
52 | const SkRect child_bounds = SkRect::MakeLTRB(l: 5.0f, t: 6.0f, r: 20.5f, b: 21.5f); |
53 | const SkPath child_path = SkPath().addRect(rect: child_bounds); |
54 | auto mock_layer = std::make_shared<MockLayer>(args: child_path); |
55 | auto layer = |
56 | std::make_shared<ShaderMaskLayer>(args: nullptr, args: kEmptyRect, args: DlBlendMode::kSrc); |
57 | layer->Add(layer: mock_layer); |
58 | |
59 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
60 | EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect); |
61 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
62 | "needs_painting\\(context\\)" ); |
63 | } |
64 | #endif |
65 | |
66 | TEST_F(ShaderMaskLayerTest, EmptyFilter) { |
67 | const SkMatrix initial_transform = SkMatrix::Translate(dx: 0.5f, dy: 1.0f); |
68 | const SkRect child_bounds = SkRect::MakeLTRB(l: 5.0f, t: 6.0f, r: 20.5f, b: 21.5f); |
69 | const SkRect layer_bounds = SkRect::MakeLTRB(l: 2.0f, t: 4.0f, r: 6.5f, b: 6.5f); |
70 | const SkPath child_path = SkPath().addRect(rect: child_bounds); |
71 | const DlPaint child_paint = DlPaint(DlColor::kYellow()); |
72 | auto mock_layer = std::make_shared<MockLayer>(args: child_path, args: child_paint); |
73 | auto layer = std::make_shared<ShaderMaskLayer>(args: nullptr, args: layer_bounds, |
74 | args: DlBlendMode::kSrc); |
75 | layer->Add(layer: mock_layer); |
76 | |
77 | preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
78 | layer->Preroll(context: preroll_context()); |
79 | EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); |
80 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
81 | EXPECT_EQ(layer->child_paint_bounds(), child_bounds); |
82 | EXPECT_TRUE(mock_layer->needs_painting(paint_context())); |
83 | EXPECT_TRUE(layer->needs_painting(paint_context())); |
84 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
85 | |
86 | DlPaint filter_paint; |
87 | filter_paint.setBlendMode(DlBlendMode::kSrc); |
88 | filter_paint.setColorSource(nullptr); |
89 | |
90 | layer->Paint(context&: display_list_paint_context()); |
91 | DisplayListBuilder expected_builder; |
92 | /* (ShaderMask)layer::Paint */ { |
93 | expected_builder.Save(); |
94 | { |
95 | expected_builder.SaveLayer(bounds: &child_bounds); |
96 | { |
97 | /* mock_layer::Paint */ { |
98 | expected_builder.DrawPath(path: child_path, paint: child_paint); |
99 | } |
100 | expected_builder.Translate(tx: layer_bounds.fLeft, ty: layer_bounds.fTop); |
101 | expected_builder.DrawRect( |
102 | rect: SkRect::MakeWH(w: layer_bounds.width(), h: layer_bounds.height()), |
103 | paint: filter_paint); |
104 | } |
105 | expected_builder.Restore(); |
106 | } |
107 | expected_builder.Restore(); |
108 | } |
109 | EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); |
110 | } |
111 | |
112 | TEST_F(ShaderMaskLayerTest, SimpleFilter) { |
113 | const SkMatrix initial_transform = SkMatrix::Translate(dx: 0.5f, dy: 1.0f); |
114 | const SkRect child_bounds = SkRect::MakeLTRB(l: 5.0f, t: 6.0f, r: 20.5f, b: 21.5f); |
115 | const SkRect layer_bounds = SkRect::MakeLTRB(l: 2.0f, t: 4.0f, r: 6.5f, b: 6.5f); |
116 | const SkPath child_path = SkPath().addRect(rect: child_bounds); |
117 | const DlPaint child_paint = DlPaint(DlColor::kYellow()); |
118 | auto dl_filter = MakeFilter(color: DlColor::kBlue()); |
119 | auto mock_layer = std::make_shared<MockLayer>(args: child_path, args: child_paint); |
120 | auto layer = std::make_shared<ShaderMaskLayer>(args&: dl_filter, args: layer_bounds, |
121 | args: DlBlendMode::kSrc); |
122 | layer->Add(layer: mock_layer); |
123 | |
124 | preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
125 | layer->Preroll(context: preroll_context()); |
126 | EXPECT_EQ(layer->paint_bounds(), child_bounds); |
127 | EXPECT_EQ(layer->child_paint_bounds(), child_bounds); |
128 | EXPECT_TRUE(layer->needs_painting(paint_context())); |
129 | EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
130 | |
131 | DlPaint filter_paint; |
132 | filter_paint.setBlendMode(DlBlendMode::kSrc); |
133 | filter_paint.setColorSource(dl_filter); |
134 | |
135 | layer->Paint(context&: display_list_paint_context()); |
136 | DisplayListBuilder expected_builder; |
137 | /* (ShaderMask)layer::Paint */ { |
138 | expected_builder.Save(); |
139 | { |
140 | expected_builder.SaveLayer(bounds: &child_bounds); |
141 | { |
142 | /* mock_layer::Paint */ { |
143 | expected_builder.DrawPath(path: child_path, paint: child_paint); |
144 | } |
145 | expected_builder.Translate(tx: layer_bounds.fLeft, ty: layer_bounds.fTop); |
146 | expected_builder.DrawRect( |
147 | rect: SkRect::MakeWH(w: layer_bounds.width(), h: layer_bounds.height()), |
148 | paint: filter_paint); |
149 | } |
150 | expected_builder.Restore(); |
151 | } |
152 | expected_builder.Restore(); |
153 | } |
154 | EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); |
155 | } |
156 | |
157 | TEST_F(ShaderMaskLayerTest, MultipleChildren) { |
158 | const SkMatrix initial_transform = SkMatrix::Translate(dx: 0.5f, dy: 1.0f); |
159 | const SkRect child_bounds = SkRect::MakeLTRB(l: 5.0f, t: 6.0f, r: 20.5f, b: 21.5f); |
160 | const SkRect layer_bounds = SkRect::MakeLTRB(l: 2.0f, t: 4.0f, r: 6.5f, b: 6.5f); |
161 | const SkPath child_path1 = SkPath().addRect(rect: child_bounds); |
162 | const SkPath child_path2 = |
163 | SkPath().addRect(rect: child_bounds.makeOffset(dx: 3.0f, dy: 0.0f)); |
164 | const DlPaint child_paint1 = DlPaint(DlColor::kYellow()); |
165 | const DlPaint child_paint2 = DlPaint(DlColor::kCyan()); |
166 | auto dl_filter = MakeFilter(color: DlColor::kBlue()); |
167 | auto mock_layer1 = std::make_shared<MockLayer>(args: child_path1, args: child_paint1); |
168 | auto mock_layer2 = std::make_shared<MockLayer>(args: child_path2, args: child_paint2); |
169 | auto layer = std::make_shared<ShaderMaskLayer>(args&: dl_filter, args: layer_bounds, |
170 | args: DlBlendMode::kSrc); |
171 | layer->Add(layer: mock_layer1); |
172 | layer->Add(layer: mock_layer2); |
173 | |
174 | SkRect children_bounds = child_path1.getBounds(); |
175 | children_bounds.join(r: child_path2.getBounds()); |
176 | preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
177 | layer->Preroll(context: preroll_context()); |
178 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
179 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
180 | EXPECT_EQ(layer->paint_bounds(), children_bounds); |
181 | EXPECT_EQ(layer->child_paint_bounds(), children_bounds); |
182 | EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); |
183 | EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); |
184 | EXPECT_TRUE(layer->needs_painting(paint_context())); |
185 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
186 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
187 | |
188 | DlPaint filter_paint; |
189 | filter_paint.setBlendMode(DlBlendMode::kSrc); |
190 | filter_paint.setColorSource(dl_filter); |
191 | |
192 | layer->Paint(context&: display_list_paint_context()); |
193 | DisplayListBuilder expected_builder; |
194 | /* (ShaderMask)layer::Paint */ { |
195 | expected_builder.Save(); |
196 | { |
197 | expected_builder.SaveLayer(bounds: &children_bounds); |
198 | { |
199 | /* mock_layer1::Paint */ { |
200 | expected_builder.DrawPath(path: child_path1, paint: child_paint1); |
201 | } |
202 | /* mock_layer2::Paint */ { |
203 | expected_builder.DrawPath(path: child_path2, paint: child_paint2); |
204 | } |
205 | expected_builder.Translate(tx: layer_bounds.fLeft, ty: layer_bounds.fTop); |
206 | expected_builder.DrawRect( |
207 | rect: SkRect::MakeWH(w: layer_bounds.width(), h: layer_bounds.height()), |
208 | paint: filter_paint); |
209 | } |
210 | expected_builder.Restore(); |
211 | } |
212 | expected_builder.Restore(); |
213 | } |
214 | EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); |
215 | } |
216 | |
217 | TEST_F(ShaderMaskLayerTest, Nested) { |
218 | const SkMatrix initial_transform = SkMatrix::Translate(dx: 0.5f, dy: 1.0f); |
219 | const SkRect child_bounds = SkRect::MakeLTRB(l: 5.0f, t: 6.0f, r: 7.5f, b: 8.5f); |
220 | const SkRect layer_bounds = SkRect::MakeLTRB(l: 2.0f, t: 4.0f, r: 20.5f, b: 20.5f); |
221 | const SkPath child_path1 = SkPath().addRect(rect: child_bounds); |
222 | const SkPath child_path2 = |
223 | SkPath().addRect(rect: child_bounds.makeOffset(dx: 3.0f, dy: 0.0f)); |
224 | const DlPaint child_paint1 = DlPaint(DlColor::kYellow()); |
225 | const DlPaint child_paint2 = DlPaint(DlColor::kCyan()); |
226 | auto dl_filter1 = MakeFilter(color: DlColor::kGreen()); |
227 | auto dl_filter2 = MakeFilter(color: DlColor::kMagenta()); |
228 | auto mock_layer1 = std::make_shared<MockLayer>(args: child_path1, args: child_paint1); |
229 | auto mock_layer2 = std::make_shared<MockLayer>(args: child_path2, args: child_paint2); |
230 | auto layer1 = std::make_shared<ShaderMaskLayer>(args&: dl_filter1, args: layer_bounds, |
231 | args: DlBlendMode::kSrc); |
232 | auto layer2 = std::make_shared<ShaderMaskLayer>(args&: dl_filter2, args: layer_bounds, |
233 | args: DlBlendMode::kSrc); |
234 | layer2->Add(layer: mock_layer2); |
235 | layer1->Add(layer: mock_layer1); |
236 | layer1->Add(layer: layer2); |
237 | |
238 | SkRect children_bounds = child_path1.getBounds(); |
239 | children_bounds.join(r: child_path2.getBounds()); |
240 | preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
241 | layer1->Preroll(context: preroll_context()); |
242 | EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
243 | EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
244 | EXPECT_EQ(layer1->paint_bounds(), children_bounds); |
245 | EXPECT_EQ(layer1->child_paint_bounds(), children_bounds); |
246 | EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); |
247 | EXPECT_EQ(layer2->child_paint_bounds(), mock_layer2->paint_bounds()); |
248 | EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); |
249 | EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); |
250 | EXPECT_TRUE(layer1->needs_painting(paint_context())); |
251 | EXPECT_TRUE(layer2->needs_painting(paint_context())); |
252 | EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
253 | EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
254 | |
255 | DlPaint filter_paint1, filter_paint2; |
256 | filter_paint1.setBlendMode(DlBlendMode::kSrc); |
257 | filter_paint2.setBlendMode(DlBlendMode::kSrc); |
258 | filter_paint1.setColorSource(dl_filter1); |
259 | filter_paint2.setColorSource(dl_filter2); |
260 | |
261 | layer1->Paint(context&: display_list_paint_context()); |
262 | DisplayListBuilder expected_builder; |
263 | /* (ShaderMask)layer1::Paint */ { |
264 | expected_builder.Save(); |
265 | { |
266 | expected_builder.SaveLayer(bounds: &children_bounds); |
267 | { |
268 | /* mock_layer1::Paint */ { |
269 | expected_builder.DrawPath(path: child_path1, paint: child_paint1); |
270 | } |
271 | /* (ShaderMask)layer2::Paint */ { |
272 | expected_builder.Save(); |
273 | { |
274 | expected_builder.SaveLayer(bounds: &child_path2.getBounds()); |
275 | { |
276 | /* mock_layer2::Paint */ { |
277 | expected_builder.DrawPath(path: child_path2, paint: child_paint2); |
278 | } |
279 | expected_builder.Translate(tx: layer_bounds.fLeft, ty: layer_bounds.fTop); |
280 | expected_builder.DrawRect( |
281 | rect: SkRect::MakeWH(w: layer_bounds.width(), h: layer_bounds.height()), |
282 | paint: filter_paint2); |
283 | } |
284 | expected_builder.Restore(); |
285 | } |
286 | expected_builder.Restore(); |
287 | } |
288 | expected_builder.Translate(tx: layer_bounds.fLeft, ty: layer_bounds.fTop); |
289 | expected_builder.DrawRect( |
290 | rect: SkRect::MakeWH(w: layer_bounds.width(), h: layer_bounds.height()), |
291 | paint: filter_paint1); |
292 | } |
293 | expected_builder.Restore(); |
294 | } |
295 | expected_builder.Restore(); |
296 | } |
297 | EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); |
298 | } |
299 | |
300 | TEST_F(ShaderMaskLayerTest, Readback) { |
301 | const SkRect layer_bounds = SkRect::MakeLTRB(l: 2.0f, t: 4.0f, r: 20.5f, b: 20.5f); |
302 | auto dl_filter = MakeFilter(color: DlColor::kBlue()); |
303 | auto layer = std::make_shared<ShaderMaskLayer>(args&: dl_filter, args: layer_bounds, |
304 | args: DlBlendMode::kSrc); |
305 | |
306 | // ShaderMaskLayer does not read from surface |
307 | preroll_context()->surface_needs_readback = false; |
308 | layer->Preroll(context: preroll_context()); |
309 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
310 | |
311 | // ShaderMaskLayer blocks child with readback |
312 | auto mock_layer = std::make_shared<MockLayer>(args: SkPath(), args: DlPaint()); |
313 | mock_layer->set_fake_reads_surface(true); |
314 | layer->Add(layer: mock_layer); |
315 | preroll_context()->surface_needs_readback = false; |
316 | layer->Preroll(context: preroll_context()); |
317 | EXPECT_FALSE(preroll_context()->surface_needs_readback); |
318 | } |
319 | |
320 | TEST_F(ShaderMaskLayerTest, LayerCached) { |
321 | auto dl_filter = MakeFilter(color: DlColor::kBlue()); |
322 | DlPaint paint; |
323 | const SkRect layer_bounds = SkRect::MakeLTRB(l: 2.0f, t: 4.0f, r: 20.5f, b: 20.5f); |
324 | auto initial_transform = SkMatrix::Translate(dx: 50.0, dy: 25.5); |
325 | const SkPath child_path = SkPath().addRect(rect: SkRect::MakeWH(w: 5.0f, h: 5.0f)); |
326 | auto mock_layer = std::make_shared<MockLayer>(args: child_path); |
327 | auto layer = std::make_shared<ShaderMaskLayer>(args&: dl_filter, args: layer_bounds, |
328 | args: DlBlendMode::kSrc); |
329 | layer->Add(layer: mock_layer); |
330 | |
331 | SkMatrix cache_ctm = initial_transform; |
332 | DisplayListBuilder cache_canvas; |
333 | cache_canvas.Transform(matrix: cache_ctm); |
334 | |
335 | use_mock_raster_cache(); |
336 | preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
337 | const auto* cacheable_shader_masker_item = layer->raster_cache_item(); |
338 | |
339 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); |
340 | EXPECT_EQ(cacheable_shader_masker_item->cache_state(), |
341 | RasterCacheItem::CacheState::kNone); |
342 | EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); |
343 | |
344 | // frame 1. |
345 | layer->Preroll(context: preroll_context()); |
346 | LayerTree::TryToRasterCache(raster_cached_entries: cacheable_items(), paint_context: &paint_context()); |
347 | |
348 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); |
349 | EXPECT_EQ(cacheable_shader_masker_item->cache_state(), |
350 | RasterCacheItem::CacheState::kNone); |
351 | EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); |
352 | |
353 | // frame 2. |
354 | layer->Preroll(context: preroll_context()); |
355 | LayerTree::TryToRasterCache(raster_cached_entries: cacheable_items(), paint_context: &paint_context()); |
356 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0); |
357 | EXPECT_EQ(cacheable_shader_masker_item->cache_state(), |
358 | RasterCacheItem::CacheState::kNone); |
359 | EXPECT_FALSE(cacheable_shader_masker_item->GetId().has_value()); |
360 | |
361 | // frame 3. |
362 | layer->Preroll(context: preroll_context()); |
363 | LayerTree::TryToRasterCache(raster_cached_entries: cacheable_items(), paint_context: &paint_context()); |
364 | EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1); |
365 | EXPECT_EQ(cacheable_shader_masker_item->cache_state(), |
366 | RasterCacheItem::CacheState::kCurrent); |
367 | |
368 | EXPECT_TRUE(raster_cache()->Draw( |
369 | cacheable_shader_masker_item->GetId().value(), cache_canvas, &paint)); |
370 | } |
371 | |
372 | TEST_F(ShaderMaskLayerTest, OpacityInheritance) { |
373 | const SkRect child_bounds = SkRect::MakeLTRB(l: 5.0f, t: 6.0f, r: 20.5f, b: 21.5f); |
374 | const SkPath child_path = SkPath().addRect(rect: child_bounds); |
375 | auto mock_layer = MockLayer::Make(path: child_path); |
376 | const SkRect mask_rect = SkRect::MakeLTRB(l: 10, t: 10, r: 20, b: 20); |
377 | auto shader_mask_layer = |
378 | std::make_shared<ShaderMaskLayer>(args: nullptr, args: mask_rect, args: DlBlendMode::kSrc); |
379 | shader_mask_layer->Add(layer: mock_layer); |
380 | |
381 | // ShaderMaskLayers can always support opacity despite incompatible children |
382 | PrerollContext* context = preroll_context(); |
383 | shader_mask_layer->Preroll(context); |
384 | EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags); |
385 | |
386 | int opacity_alpha = 0x7F; |
387 | SkPoint offset = SkPoint::Make(x: 10, y: 10); |
388 | auto opacity_layer = std::make_shared<OpacityLayer>(args&: opacity_alpha, args&: offset); |
389 | opacity_layer->Add(layer: shader_mask_layer); |
390 | opacity_layer->Preroll(context); |
391 | EXPECT_TRUE(opacity_layer->children_can_accept_opacity()); |
392 | |
393 | DisplayListBuilder expected_builder; |
394 | /* OpacityLayer::Paint() */ { |
395 | expected_builder.Save(); |
396 | { |
397 | expected_builder.Translate(tx: offset.fX, ty: offset.fY); |
398 | /* ShaderMaskLayer::Paint() */ { |
399 | DlPaint sl_paint = DlPaint(opacity_alpha << 24); |
400 | expected_builder.SaveLayer(bounds: &child_path.getBounds(), paint: &sl_paint); |
401 | { |
402 | /* child layer paint */ { |
403 | expected_builder.DrawPath(path: child_path, paint: DlPaint()); |
404 | } |
405 | expected_builder.Translate(tx: mask_rect.fLeft, ty: mask_rect.fTop); |
406 | expected_builder.DrawRect( |
407 | rect: SkRect::MakeWH(w: mask_rect.width(), h: mask_rect.height()), |
408 | paint: DlPaint().setBlendMode(DlBlendMode::kSrc)); |
409 | } |
410 | expected_builder.Restore(); |
411 | } |
412 | } |
413 | expected_builder.Restore(); |
414 | } |
415 | |
416 | opacity_layer->Paint(context&: display_list_paint_context()); |
417 | EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list())); |
418 | } |
419 | |
420 | TEST_F(ShaderMaskLayerTest, SimpleFilterWithRasterCacheLayerNotCached) { |
421 | use_mock_raster_cache(); // Ensure non-fractional alignment. |
422 | |
423 | const SkMatrix initial_transform = SkMatrix::Translate(dx: 0.5f, dy: 1.0f); |
424 | const SkRect child_bounds = SkRect::MakeLTRB(l: 5.0f, t: 6.0f, r: 20.5f, b: 21.5f); |
425 | const SkRect layer_bounds = SkRect::MakeLTRB(l: 2.0f, t: 4.0f, r: 6.5f, b: 6.5f); |
426 | const SkPath child_path = SkPath().addRect(rect: child_bounds); |
427 | const DlPaint child_paint = DlPaint(DlColor::kYellow()); |
428 | auto dl_filter = MakeFilter(color: DlColor::kBlue()); |
429 | auto mock_layer = std::make_shared<MockLayer>(args: child_path, args: child_paint); |
430 | auto layer = std::make_shared<ShaderMaskLayer>(args&: dl_filter, args: layer_bounds, |
431 | args: DlBlendMode::kSrc); |
432 | layer->Add(layer: mock_layer); |
433 | |
434 | preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
435 | layer->Preroll(context: preroll_context()); |
436 | |
437 | DlPaint filter_paint; |
438 | filter_paint.setBlendMode(DlBlendMode::kSrc); |
439 | filter_paint.setColorSource(dl_filter); |
440 | |
441 | layer->Paint(context&: display_list_paint_context()); |
442 | DisplayListBuilder expected_builder; |
443 | /* (ShaderMask)layer::Paint */ { |
444 | expected_builder.Save(); |
445 | { |
446 | expected_builder.TransformReset(); |
447 | // The layer will perform this Identity transform operation by default, |
448 | // but it should be ignored both here and in the layer paint |
449 | expected_builder.Transform(matrix: SkMatrix()); |
450 | expected_builder.SaveLayer(bounds: &child_bounds); |
451 | { |
452 | /* mock_layer::Paint */ { |
453 | expected_builder.DrawPath(path: child_path, paint: child_paint); |
454 | } |
455 | expected_builder.Translate(tx: layer_bounds.fLeft, ty: layer_bounds.fTop); |
456 | expected_builder.DrawRect( |
457 | rect: SkRect::MakeWH(w: layer_bounds.width(), h: layer_bounds.height()), |
458 | paint: filter_paint); |
459 | } |
460 | expected_builder.Restore(); |
461 | } |
462 | expected_builder.Restore(); |
463 | } |
464 | EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); |
465 | } |
466 | |
467 | } // namespace testing |
468 | } // namespace flutter |
469 | |
470 | // NOLINTEND(bugprone-unchecked-optional-access) |
471 | |