Flutter Engine
physical_shape_layer_unittests.cc
Go to the documentation of this file.
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/physical_shape_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 
12 namespace flutter {
13 namespace testing {
14 
16 
17 #ifndef NDEBUG
18 TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) {
19  auto layer =
20  std::make_shared<PhysicalShapeLayer>(SK_ColorBLACK, SK_ColorBLACK,
21  0.0f, // elevation
22  SkPath(), Clip::none);
23 
24  layer->Preroll(preroll_context(), SkMatrix());
25  EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
26  EXPECT_FALSE(layer->needs_painting(paint_context()));
27 
28  EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
29  "needs_painting\\(context\\)");
30 }
31 
32 TEST_F(PhysicalShapeLayerTest, PaintBeforePrerollDies) {
33  SkPath child_path;
34  child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
35  auto mock_layer = std::make_shared<MockLayer>(child_path, SkPaint());
36  auto layer =
37  std::make_shared<PhysicalShapeLayer>(SK_ColorBLACK, SK_ColorBLACK,
38  0.0f, // elevation
39  SkPath(), Clip::none);
40  layer->Add(mock_layer);
41 
42  EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
43  "needs_painting\\(context\\)");
44 }
45 #endif
46 
47 TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) {
48  SkPath layer_path;
49  layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
50  auto layer =
51  std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
52  0.0f, // elevation
53  layer_path, Clip::none);
54  layer->Preroll(preroll_context(), SkMatrix());
55  EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
56  EXPECT_TRUE(layer->needs_painting(paint_context()));
57 
58  SkPaint layer_paint;
59  layer_paint.setColor(SK_ColorGREEN);
60  layer_paint.setAntiAlias(true);
61  layer->Paint(paint_context());
62  EXPECT_EQ(mock_canvas().draw_calls(),
63  std::vector({MockCanvas::DrawCall{
64  0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
65 }
66 
67 TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) {
68  SkPath layer_path;
69  layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
70  SkPath child1_path;
71  child1_path.addRect(4, 0, 12, 12).close();
72  SkPath child2_path;
73  child2_path.addRect(3, 2, 5, 15).close();
74  auto child1 = std::make_shared<PhysicalShapeLayer>(SK_ColorRED, SK_ColorBLACK,
75  0.0f, // elevation
76  child1_path, Clip::none);
77  auto child2 =
78  std::make_shared<PhysicalShapeLayer>(SK_ColorBLUE, SK_ColorBLACK,
79  0.0f, // elevation
80  child2_path, Clip::none);
81  auto layer =
82  std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
83  0.0f, // elevation
84  layer_path, Clip::none);
85  layer->Add(child1);
86  layer->Add(child2);
87 
88  SkRect child_paint_bounds;
89  layer->Preroll(preroll_context(), SkMatrix());
90  child_paint_bounds.join(child1->paint_bounds());
91  child_paint_bounds.join(child2->paint_bounds());
92  EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
93  EXPECT_NE(layer->paint_bounds(), child_paint_bounds);
94  EXPECT_TRUE(layer->needs_painting(paint_context()));
95 
96  SkPaint layer_paint;
97  layer_paint.setColor(SK_ColorGREEN);
98  layer_paint.setAntiAlias(true);
99  SkPaint child1_paint;
100  child1_paint.setColor(SK_ColorRED);
101  child1_paint.setAntiAlias(true);
102  SkPaint child2_paint;
103  child2_paint.setColor(SK_ColorBLUE);
104  child2_paint.setAntiAlias(true);
105  layer->Paint(paint_context());
106  EXPECT_EQ(
107  mock_canvas().draw_calls(),
108  std::vector({MockCanvas::DrawCall{
109  0, MockCanvas::DrawPathData{layer_path, layer_paint}},
111  0, MockCanvas::DrawPathData{child1_path, child1_paint}},
113  child2_path, child2_paint}}}));
114 }
115 
116 TEST_F(PhysicalShapeLayerTest, ElevationSimple) {
117  constexpr float initial_elevation = 20.0f;
118  SkPath layer_path;
119  layer_path.addRect(0, 0, 8, 8).close();
120  auto layer = std::make_shared<PhysicalShapeLayer>(
121  SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, Clip::none);
122 
123  layer->Preroll(preroll_context(), SkMatrix());
124  // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
125  // their shadows , so we do not do any painting there.
126  EXPECT_EQ(layer->paint_bounds(),
128  layer_path, initial_elevation, 1.0f, SkMatrix()));
129  EXPECT_TRUE(layer->needs_painting(paint_context()));
130  EXPECT_EQ(layer->elevation(), initial_elevation);
131 
132  SkPaint layer_paint;
133  layer_paint.setColor(SK_ColorGREEN);
134  layer_paint.setAntiAlias(true);
135  layer->Paint(paint_context());
136  EXPECT_EQ(
137  mock_canvas().draw_calls(),
138  std::vector(
141  0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
142 }
143 
144 TEST_F(PhysicalShapeLayerTest, ElevationComplex) {
145  // The layer tree should look like this:
146  // layers[0] +1.0f = 1.0f
147  // | \
148  // | \
149  // | \
150  // | layers[2] +3.0f = 4.0f
151  // | |
152  // | layers[3] +4.0f = 8.0f
153  // |
154  // |
155  // layers[1] + 2.0f = 3.0f
156  constexpr float initial_elevations[4] = {1.0f, 2.0f, 3.0f, 4.0f};
157  SkPath layer_path;
158  layer_path.addRect(0, 0, 80, 80).close();
159 
160  std::shared_ptr<PhysicalShapeLayer> layers[4];
161  for (int i = 0; i < 4; i += 1) {
162  layers[i] = std::make_shared<PhysicalShapeLayer>(
163  SK_ColorBLACK, SK_ColorBLACK, initial_elevations[i], layer_path,
164  Clip::none);
165  }
166  layers[0]->Add(layers[1]);
167  layers[0]->Add(layers[2]);
168  layers[2]->Add(layers[3]);
169 
170  layers[0]->Preroll(preroll_context(), SkMatrix());
171  for (int i = 0; i < 4; i += 1) {
172  // On Fuchsia, the system compositor handles all elevated
173  // PhysicalShapeLayers and their shadows , so we do not do any painting
174  // there.
175  EXPECT_EQ(layers[i]->paint_bounds(),
177  layer_path, initial_elevations[i], 1.0f /* pixel_ratio */,
178  SkMatrix())));
179  EXPECT_TRUE(layers[i]->needs_painting(paint_context()));
180  }
181 
182  SkPaint layer_paint;
183  layer_paint.setColor(SK_ColorBLACK);
184  layer_paint.setAntiAlias(true);
185  layers[0]->Paint(paint_context());
186  EXPECT_EQ(
187  mock_canvas().draw_calls(),
188  std::vector(
191  0, MockCanvas::DrawPathData{layer_path, layer_paint}},
194  0, MockCanvas::DrawPathData{layer_path, layer_paint}},
197  0, MockCanvas::DrawPathData{layer_path, layer_paint}},
200  0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
201 }
202 
203 TEST_F(PhysicalShapeLayerTest, ShadowNotDependsCtm) {
204  constexpr SkScalar elevations[] = {1, 2, 3, 4, 5, 10};
205  constexpr SkScalar scales[] = {0.5, 1, 1.5, 2, 3, 5};
206  constexpr SkScalar translates[] = {0, 1, -1, 0.5, 2, 10};
207 
208  SkPath path;
209  path.addRect(0, 0, 8, 8).close();
210 
211  for (SkScalar elevation : elevations) {
212  SkRect baseline_bounds = PhysicalShapeLayer::ComputeShadowBounds(
213  path, elevation, 1.0f, SkMatrix());
214  for (SkScalar scale : scales) {
215  for (SkScalar translateX : translates) {
216  for (SkScalar translateY : translates) {
217  SkMatrix ctm;
218  ctm.setScaleTranslate(scale, scale, translateX, translateY);
220  path, elevation, scale, ctm);
221  EXPECT_FLOAT_EQ(bounds.fLeft, baseline_bounds.fLeft);
222  EXPECT_FLOAT_EQ(bounds.fTop, baseline_bounds.fTop);
223  EXPECT_FLOAT_EQ(bounds.fRight, baseline_bounds.fRight);
224  EXPECT_FLOAT_EQ(bounds.fBottom, baseline_bounds.fBottom);
225  }
226  }
227  }
228  }
229 }
230 
232  const std::function<void(SkCanvas*)>& actual_draw_function,
233  const std::function<void(SkCanvas*)>& expected_draw_function,
234  const SkSize& canvas_size) {
235  sk_sp<SkSurface> actual_surface =
236  SkSurface::MakeRasterN32Premul(canvas_size.width(), canvas_size.height());
237  sk_sp<SkSurface> expected_surface =
238  SkSurface::MakeRasterN32Premul(canvas_size.width(), canvas_size.height());
239 
240  actual_surface->getCanvas()->drawColor(SK_ColorWHITE);
241  expected_surface->getCanvas()->drawColor(SK_ColorWHITE);
242 
243  actual_draw_function(actual_surface->getCanvas());
244  expected_draw_function(expected_surface->getCanvas());
245 
246  SkPixmap actual_pixels;
247  EXPECT_TRUE(actual_surface->peekPixels(&actual_pixels));
248 
249  SkPixmap expected_pixels;
250  EXPECT_TRUE(expected_surface->peekPixels(&expected_pixels));
251 
252  int different_pixels = 0;
253  for (int y = 0; y < canvas_size.height(); y++) {
254  const uint32_t* actual_row = actual_pixels.addr32(0, y);
255  const uint32_t* expected_row = expected_pixels.addr32(0, y);
256  for (int x = 0; x < canvas_size.width(); x++) {
257  if (actual_row[x] != expected_row[x]) {
258  different_pixels++;
259  }
260  }
261  }
262  return different_pixels;
263 }
264 
265 TEST_F(PhysicalShapeLayerTest, ShadowNotDependsPathSize) {
266  constexpr SkRect test_cases[][2] = {
267  {{20, -100, 80, 80}, {20, -1000, 80, 80}},
268  {{20, 20, 80, 200}, {20, 20, 80, 2000}},
269  };
270 
271  for (const SkRect* test_case : test_cases) {
273  [=](SkCanvas* canvas) {
274  SkPath path;
275  path.addRect(test_case[0]).close();
276  PhysicalShapeLayer::DrawShadow(canvas, path, SK_ColorBLACK,
277  1.0f, false, 1.0f);
278  },
279  [=](SkCanvas* canvas) {
280  SkPath path;
281  path.addRect(test_case[1]).close();
282  PhysicalShapeLayer::DrawShadow(canvas, path, SK_ColorBLACK,
283  1.0f, false, 1.0f);
284  },
285  SkSize::Make(100, 100)),
286  0);
287  }
288 }
289 
290 static bool ReadbackResult(PrerollContext* context,
291  Clip clip_behavior,
292  std::shared_ptr<Layer> child,
293  bool before) {
294  const SkMatrix initial_matrix = SkMatrix();
295  const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
296  const SkPath layer_path = SkPath().addRect(layer_bounds);
297  auto layer =
298  std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
299  0.0f, // elevation
300  layer_path, clip_behavior);
301  if (child != nullptr) {
302  layer->Add(child);
303  }
304  context->surface_needs_readback = before;
305  layer->Preroll(context, initial_matrix);
306  return context->surface_needs_readback;
307 }
308 
309 TEST_F(PhysicalShapeLayerTest, Readback) {
310  PrerollContext* context = preroll_context();
311  SkPath path;
312  SkPaint paint;
313 
314  const Clip hard = Clip::hardEdge;
315  const Clip soft = Clip::antiAlias;
316  const Clip save_layer = Clip::antiAliasWithSaveLayer;
317 
318  std::shared_ptr<MockLayer> nochild;
319  auto reader = std::make_shared<MockLayer>(path, paint, false, true);
320  auto nonreader = std::make_shared<MockLayer>(path, paint);
321 
322  // No children, no prior readback -> no readback after
323  EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
324  EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
325  EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
326 
327  // No children, prior readback -> readback after
328  EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
329  EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
330  EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
331 
332  // Non readback child, no prior readback -> no readback after
333  EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
334  EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
335  EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
336 
337  // Non readback child, prior readback -> readback after
338  EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
339  EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
340  EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
341 
342  // Readback child, no prior readback -> readback after unless SaveLayer
343  EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
344  EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
345  EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
346 
347  // Readback child, prior readback -> readback after
348  EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
349  EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
350  EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
351 }
352 
353 } // namespace testing
354 } // namespace flutter
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
Dart_NativeFunction function
Definition: fuchsia.cc:51
static SkRect ComputeShadowBounds(const SkPath &path, float elevation, SkScalar dpr, const SkMatrix &ctm)
LayerTestBase<::testing::Test > LayerTest
Definition: layer_test.h:151
static bool ReadbackResult(PrerollContext *context, Clip clip_behavior, std::shared_ptr< Layer > child, bool before)
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
static void DrawShadow(SkCanvas *canvas, const SkPath &path, SkColor color, float elevation, bool transparentOccluder, SkScalar dpr)
Clip
Definition: layer.h:40
bool surface_needs_readback
Definition: layer.h:49
static int RasterizedDifferenceInPixels(const std::function< void(SkCanvas *)> &actual_draw_function, const std::function< void(SkCanvas *)> &expected_draw_function, const SkSize &canvas_size)