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/opacity_layer.h"
6
7#include "flutter/flow/layers/clip_rect_layer.h"
8#include "flutter/flow/testing/layer_test.h"
9#include "flutter/flow/testing/mock_layer.h"
10#include "flutter/fml/macros.h"
11#include "flutter/testing/mock_canvas.h"
12
13namespace flutter {
14namespace testing {
15
16using OpacityLayerTest = LayerTest;
17
18#ifndef NDEBUG
19TEST_F(OpacityLayerTest, LeafLayer) {
20 auto layer =
21 std::make_shared<OpacityLayer>(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f));
22
23 EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()),
24 "\\!container->layers\\(\\)\\.empty\\(\\)");
25}
26
27TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) {
28 auto mock_layer = std::make_shared<MockLayer>(SkPath());
29 auto layer =
30 std::make_shared<OpacityLayer>(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f));
31 layer->Add(mock_layer);
32
33 layer->Preroll(preroll_context(), SkMatrix());
34 EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds());
35 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
36 EXPECT_FALSE(mock_layer->needs_painting());
37 EXPECT_FALSE(layer->needs_painting());
38
39 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
40 "needs_painting\\(\\)");
41}
42
43TEST_F(OpacityLayerTest, PaintBeforePreollDies) {
44 SkPath child_path;
45 child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
46 auto mock_layer = std::make_shared<MockLayer>(child_path);
47 auto layer =
48 std::make_shared<OpacityLayer>(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f));
49 layer->Add(mock_layer);
50
51 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
52 "needs_painting\\(\\)");
53}
54#endif
55
56TEST_F(OpacityLayerTest, ChildIsCached) {
57 const SkAlpha alpha_half = 255 / 2;
58 auto initial_transform = SkMatrix::Translate(50.0, 25.5);
59 auto other_transform = SkMatrix::Scale(1.0, 2.0);
60 const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
61 auto mock_layer = std::make_shared<MockLayer>(child_path);
62 auto layer =
63 std::make_shared<OpacityLayer>(alpha_half, SkPoint::Make(0.0f, 0.0f));
64 layer->Add(mock_layer);
65
66 SkMatrix cache_ctm = initial_transform;
67 SkCanvas cache_canvas;
68 cache_canvas.setMatrix(cache_ctm);
69 SkCanvas other_canvas;
70 other_canvas.setMatrix(other_transform);
71
72 use_mock_raster_cache();
73
74 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
75 EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
76 EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
77
78 layer->Preroll(preroll_context(), initial_transform);
79
80 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
81 EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
82 EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
83}
84
85TEST_F(OpacityLayerTest, ChildrenNotCached) {
86 const SkAlpha alpha_half = 255 / 2;
87 auto initial_transform = SkMatrix::Translate(50.0, 25.5);
88 auto other_transform = SkMatrix::Scale(1.0, 2.0);
89 const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
90 const SkPath child_path2 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
91 auto mock_layer1 = std::make_shared<MockLayer>(child_path1);
92 auto mock_layer2 = std::make_shared<MockLayer>(child_path2);
93 auto layer =
94 std::make_shared<OpacityLayer>(alpha_half, SkPoint::Make(0.0f, 0.0f));
95 layer->Add(mock_layer1);
96 layer->Add(mock_layer2);
97
98 SkMatrix cache_ctm = initial_transform;
99 SkCanvas cache_canvas;
100 cache_canvas.setMatrix(cache_ctm);
101 SkCanvas other_canvas;
102 other_canvas.setMatrix(other_transform);
103
104 use_mock_raster_cache();
105
106 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
107 EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas));
108 EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
109 EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
110 EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
111
112 layer->Preroll(preroll_context(), initial_transform);
113
114 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
115 EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), other_canvas));
116 EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
117 EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
118 EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
119}
120
121TEST_F(OpacityLayerTest, FullyOpaque) {
122 const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
123 const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f);
124 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 0.5f);
125 const SkMatrix layer_transform =
126 SkMatrix::Translate(layer_offset.fX, layer_offset.fY);
127#ifndef SUPPORT_FRACTIONAL_TRANSLATION
128 const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM(
129 SkMatrix::Concat(initial_transform, layer_transform));
130#endif
131 const SkPaint child_paint = SkPaint(SkColors::kGreen);
132 const SkRect expected_layer_bounds =
133 layer_transform.mapRect(child_path.getBounds());
134 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
135 auto layer = std::make_shared<OpacityLayer>(SK_AlphaOPAQUE, layer_offset);
136 layer->Add(mock_layer);
137
138 layer->Preroll(preroll_context(), initial_transform);
139 EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
140 EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
141 EXPECT_TRUE(mock_layer->needs_painting());
142 EXPECT_TRUE(layer->needs_painting());
143 EXPECT_EQ(mock_layer->parent_matrix(),
144 SkMatrix::Concat(initial_transform, layer_transform));
145 EXPECT_EQ(mock_layer->parent_mutators(),
146 std::vector({Mutator(layer_transform), Mutator(SK_AlphaOPAQUE)}));
147
148 const SkPaint opacity_paint = SkPaint(SkColors::kBlack); // A = 1.0f
149 SkRect opacity_bounds;
150 expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY)
151 .roundOut(&opacity_bounds);
152 auto expected_draw_calls = std::vector(
153 {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
154 MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}},
155#ifndef SUPPORT_FRACTIONAL_TRANSLATION
156 MockCanvas::DrawCall{
157 1, MockCanvas::SetMatrixData{integral_layer_transform}},
158#endif
159 MockCanvas::DrawCall{
160 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr,
161 2}},
162 MockCanvas::DrawCall{2,
163 MockCanvas::DrawPathData{child_path, child_paint}},
164 MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
165 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}});
166 layer->Paint(paint_context());
167 EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls);
168}
169
170TEST_F(OpacityLayerTest, FullyTransparent) {
171 const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
172 const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f);
173 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 0.5f);
174 const SkMatrix layer_transform =
175 SkMatrix::Translate(layer_offset.fX, layer_offset.fY);
176#ifndef SUPPORT_FRACTIONAL_TRANSLATION
177 const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM(
178 SkMatrix::Concat(initial_transform, layer_transform));
179#endif
180 const SkPaint child_paint = SkPaint(SkColors::kGreen);
181 const SkRect expected_layer_bounds =
182 layer_transform.mapRect(child_path.getBounds());
183 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
184 auto layer =
185 std::make_shared<OpacityLayer>(SK_AlphaTRANSPARENT, layer_offset);
186 layer->Add(mock_layer);
187
188 layer->Preroll(preroll_context(), initial_transform);
189 EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
190 EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
191 EXPECT_TRUE(mock_layer->needs_painting());
192 EXPECT_TRUE(layer->needs_painting());
193 EXPECT_EQ(mock_layer->parent_matrix(),
194 SkMatrix::Concat(initial_transform, layer_transform));
195 EXPECT_EQ(
196 mock_layer->parent_mutators(),
197 std::vector({Mutator(layer_transform), Mutator(SK_AlphaTRANSPARENT)}));
198
199 auto expected_draw_calls = std::vector(
200 {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
201 MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}},
202#ifndef SUPPORT_FRACTIONAL_TRANSLATION
203 MockCanvas::DrawCall{
204 1, MockCanvas::SetMatrixData{integral_layer_transform}},
205#endif
206 MockCanvas::DrawCall{1, MockCanvas::SaveData{2}},
207 MockCanvas::DrawCall{
208 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect,
209 MockCanvas::kHard_ClipEdgeStyle}},
210 MockCanvas::DrawCall{2,
211 MockCanvas::DrawPathData{child_path, child_paint}},
212 MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
213 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}});
214 layer->Paint(paint_context());
215 EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls);
216}
217
218TEST_F(OpacityLayerTest, HalfTransparent) {
219 const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f));
220 const SkPoint layer_offset = SkPoint::Make(0.5f, 1.5f);
221 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 0.5f);
222 const SkMatrix layer_transform =
223 SkMatrix::Translate(layer_offset.fX, layer_offset.fY);
224#ifndef SUPPORT_FRACTIONAL_TRANSLATION
225 const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM(
226 SkMatrix::Concat(initial_transform, layer_transform));
227#endif
228 const SkPaint child_paint = SkPaint(SkColors::kGreen);
229 const SkRect expected_layer_bounds =
230 layer_transform.mapRect(child_path.getBounds());
231 const SkAlpha alpha_half = 255 / 2;
232 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
233 auto layer = std::make_shared<OpacityLayer>(alpha_half, layer_offset);
234 layer->Add(mock_layer);
235
236 layer->Preroll(preroll_context(), initial_transform);
237 EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
238 EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
239 EXPECT_TRUE(mock_layer->needs_painting());
240 EXPECT_TRUE(layer->needs_painting());
241 EXPECT_EQ(mock_layer->parent_matrix(),
242 SkMatrix::Concat(initial_transform, layer_transform));
243 EXPECT_EQ(mock_layer->parent_mutators(),
244 std::vector({Mutator(layer_transform), Mutator(alpha_half)}));
245
246 const SkPaint opacity_paint =
247 SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha_half)));
248 SkRect opacity_bounds;
249 expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY)
250 .roundOut(&opacity_bounds);
251 auto expected_draw_calls = std::vector(
252 {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
253 MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}},
254#ifndef SUPPORT_FRACTIONAL_TRANSLATION
255 MockCanvas::DrawCall{
256 1, MockCanvas::SetMatrixData{integral_layer_transform}},
257#endif
258 MockCanvas::DrawCall{
259 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr,
260 2}},
261 MockCanvas::DrawCall{2,
262 MockCanvas::DrawPathData{child_path, child_paint}},
263 MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
264 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}});
265 layer->Paint(paint_context());
266 EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls);
267}
268
269TEST_F(OpacityLayerTest, Nested) {
270 const SkPath child1_path = SkPath().addRect(SkRect::MakeWH(5.0f, 6.0f));
271 const SkPath child2_path = SkPath().addRect(SkRect::MakeWH(2.0f, 7.0f));
272 const SkPath child3_path = SkPath().addRect(SkRect::MakeWH(6.0f, 6.0f));
273 const SkPoint layer1_offset = SkPoint::Make(0.5f, 1.5f);
274 const SkPoint layer2_offset = SkPoint::Make(2.5f, 0.5f);
275 const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 0.5f);
276 const SkMatrix layer1_transform =
277 SkMatrix::Translate(layer1_offset.fX, layer1_offset.fY);
278 const SkMatrix layer2_transform =
279 SkMatrix::Translate(layer2_offset.fX, layer2_offset.fY);
280#ifndef SUPPORT_FRACTIONAL_TRANSLATION
281 const SkMatrix integral_layer1_transform = RasterCache::GetIntegralTransCTM(
282 SkMatrix::Concat(initial_transform, layer1_transform));
283 const SkMatrix integral_layer2_transform = RasterCache::GetIntegralTransCTM(
284 SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform),
285 layer2_transform));
286#endif
287 const SkPaint child1_paint = SkPaint(SkColors::kRed);
288 const SkPaint child2_paint = SkPaint(SkColors::kBlue);
289 const SkPaint child3_paint = SkPaint(SkColors::kGreen);
290 const SkAlpha alpha1 = 155;
291 const SkAlpha alpha2 = 224;
292 auto mock_layer1 = std::make_shared<MockLayer>(child1_path, child1_paint);
293 auto mock_layer2 = std::make_shared<MockLayer>(child2_path, child2_paint);
294 auto mock_layer3 = std::make_shared<MockLayer>(child3_path, child3_paint);
295 auto layer1 = std::make_shared<OpacityLayer>(alpha1, layer1_offset);
296 auto layer2 = std::make_shared<OpacityLayer>(alpha2, layer2_offset);
297 layer2->Add(mock_layer2);
298 layer1->Add(mock_layer1);
299 layer1->Add(layer2);
300 layer1->Add(mock_layer3); // Ensure something is processed after recursion
301
302 const SkRect expected_layer2_bounds =
303 layer2_transform.mapRect(child2_path.getBounds());
304 SkRect expected_layer1_bounds = expected_layer2_bounds;
305 expected_layer1_bounds.join(child1_path.getBounds());
306 expected_layer1_bounds.join(child3_path.getBounds());
307 expected_layer1_bounds = layer1_transform.mapRect(expected_layer1_bounds);
308 layer1->Preroll(preroll_context(), initial_transform);
309 EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.getBounds());
310 EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.getBounds());
311 EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds());
312 EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds);
313 EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds);
314 EXPECT_TRUE(mock_layer1->needs_painting());
315 EXPECT_TRUE(mock_layer2->needs_painting());
316 EXPECT_TRUE(mock_layer3->needs_painting());
317 EXPECT_TRUE(layer1->needs_painting());
318 EXPECT_TRUE(layer2->needs_painting());
319 EXPECT_EQ(mock_layer1->parent_matrix(),
320 SkMatrix::Concat(initial_transform, layer1_transform));
321 // EXPECT_EQ(mock_layer1->parent_mutators(),
322 // std::vector({Mutator(layer1_transform), Mutator(alpha1)}));
323 EXPECT_EQ(
324 mock_layer2->parent_matrix(),
325 SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform),
326 layer2_transform));
327 // EXPECT_EQ(mock_layer2->parent_mutators(),
328 // std::vector({Mutator(layer1_transform), Mutator(alpha1),
329 // Mutator(layer2_transform), Mutator(alpha2)}));
330 EXPECT_EQ(mock_layer3->parent_matrix(),
331 SkMatrix::Concat(initial_transform, layer1_transform));
332 // EXPECT_EQ(mock_layer3->parent_mutators(),
333 // std::vector({Mutator(layer1_transform), Mutator(alpha1)}));
334
335 const SkPaint opacity1_paint =
336 SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha1)));
337 const SkPaint opacity2_paint =
338 SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha2)));
339 SkRect opacity1_bounds, opacity2_bounds;
340 expected_layer1_bounds.makeOffset(-layer1_offset.fX, -layer1_offset.fY)
341 .roundOut(&opacity1_bounds);
342 expected_layer2_bounds.makeOffset(-layer2_offset.fX, -layer2_offset.fY)
343 .roundOut(&opacity2_bounds);
344 auto expected_draw_calls = std::vector(
345 {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
346 MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer1_transform}},
347#ifndef SUPPORT_FRACTIONAL_TRANSLATION
348 MockCanvas::DrawCall{
349 1, MockCanvas::SetMatrixData{integral_layer1_transform}},
350#endif
351 MockCanvas::DrawCall{
352 1, MockCanvas::SaveLayerData{opacity1_bounds, opacity1_paint,
353 nullptr, 2}},
354 MockCanvas::DrawCall{
355 2, MockCanvas::DrawPathData{child1_path, child1_paint}},
356 MockCanvas::DrawCall{2, MockCanvas::SaveData{3}},
357 MockCanvas::DrawCall{3, MockCanvas::ConcatMatrixData{layer2_transform}},
358#ifndef SUPPORT_FRACTIONAL_TRANSLATION
359 MockCanvas::DrawCall{
360 3, MockCanvas::SetMatrixData{integral_layer2_transform}},
361#endif
362 MockCanvas::DrawCall{
363 3, MockCanvas::SaveLayerData{opacity2_bounds, opacity2_paint,
364 nullptr, 4}},
365 MockCanvas::DrawCall{
366 4, MockCanvas::DrawPathData{child2_path, child2_paint}},
367 MockCanvas::DrawCall{4, MockCanvas::RestoreData{3}},
368 MockCanvas::DrawCall{3, MockCanvas::RestoreData{2}},
369 MockCanvas::DrawCall{
370 2, MockCanvas::DrawPathData{child3_path, child3_paint}},
371 MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
372 MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}});
373 layer1->Paint(paint_context());
374 EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls);
375}
376
377TEST_F(OpacityLayerTest, Readback) {
378 auto initial_transform = SkMatrix();
379 auto layer = std::make_shared<OpacityLayer>(kOpaque_SkAlphaType, SkPoint());
380 layer->Add(std::make_shared<MockLayer>(SkPath()));
381
382 // OpacityLayer does not read from surface
383 preroll_context()->surface_needs_readback = false;
384 layer->Preroll(preroll_context(), initial_transform);
385 EXPECT_FALSE(preroll_context()->surface_needs_readback);
386
387 // OpacityLayer blocks child with readback
388 auto mock_layer =
389 std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true);
390 layer->Add(mock_layer);
391 preroll_context()->surface_needs_readback = false;
392 layer->Preroll(preroll_context(), initial_transform);
393 EXPECT_FALSE(preroll_context()->surface_needs_readback);
394}
395
396TEST_F(OpacityLayerTest, CullRectIsTransformed) {
397 auto clipRectLayer = std::make_shared<ClipRectLayer>(
398 SkRect::MakeLTRB(0, 0, 10, 10), flutter::hardEdge);
399 auto opacityLayer =
400 std::make_shared<OpacityLayer>(128, SkPoint::Make(20, 20));
401 auto mockLayer = std::make_shared<MockLayer>(SkPath());
402 clipRectLayer->Add(opacityLayer);
403 opacityLayer->Add(mockLayer);
404 clipRectLayer->Preroll(preroll_context(), SkMatrix::I());
405 EXPECT_EQ(mockLayer->parent_cull_rect().fLeft, -20);
406 EXPECT_EQ(mockLayer->parent_cull_rect().fTop, -20);
407}
408
409} // namespace testing
410} // namespace flutter
411