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
12namespace flutter {
13namespace testing {
14
15using PhysicalShapeLayerTest = LayerTest;
16
17#ifndef NDEBUG
18TEST_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());
27 EXPECT_FALSE(layer->needs_system_composite());
28
29 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
30 "needs_painting\\(\\)");
31}
32
33TEST_F(PhysicalShapeLayerTest, PaintBeforePreollDies) {
34 SkPath child_path;
35 child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
36 auto mock_layer = std::make_shared<MockLayer>(child_path, SkPaint());
37 auto layer =
38 std::make_shared<PhysicalShapeLayer>(SK_ColorBLACK, SK_ColorBLACK,
39 0.0f, // elevation
40 SkPath(), Clip::none);
41 layer->Add(mock_layer);
42
43 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
44 "needs_painting\\(\\)");
45}
46#endif
47
48TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) {
49 SkPath layer_path;
50 layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
51 auto layer =
52 std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
53 0.0f, // elevation
54 layer_path, Clip::none);
55 layer->Preroll(preroll_context(), SkMatrix());
56 EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
57 EXPECT_TRUE(layer->needs_painting());
58 EXPECT_FALSE(layer->needs_system_composite());
59
60 SkPaint layer_paint;
61 layer_paint.setColor(SK_ColorGREEN);
62 layer_paint.setAntiAlias(true);
63 layer->Paint(paint_context());
64 EXPECT_EQ(mock_canvas().draw_calls(),
65 std::vector({MockCanvas::DrawCall{
66 0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
67}
68
69TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) {
70 SkPath layer_path;
71 layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
72 SkPath child1_path;
73 child1_path.addRect(4, 0, 12, 12).close();
74 SkPath child2_path;
75 child2_path.addRect(3, 2, 5, 15).close();
76 auto child1 = std::make_shared<PhysicalShapeLayer>(SK_ColorRED, SK_ColorBLACK,
77 0.0f, // elevation
78 child1_path, Clip::none);
79 auto child2 =
80 std::make_shared<PhysicalShapeLayer>(SK_ColorBLUE, SK_ColorBLACK,
81 0.0f, // elevation
82 child2_path, Clip::none);
83 auto layer =
84 std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
85 0.0f, // elevation
86 layer_path, Clip::none);
87 layer->Add(child1);
88 layer->Add(child2);
89
90 SkRect child_paint_bounds;
91 layer->Preroll(preroll_context(), SkMatrix());
92 child_paint_bounds.join(child1->paint_bounds());
93 child_paint_bounds.join(child2->paint_bounds());
94 EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
95 EXPECT_NE(layer->paint_bounds(), child_paint_bounds);
96 EXPECT_TRUE(layer->needs_painting());
97 EXPECT_FALSE(layer->needs_system_composite());
98
99 SkPaint layer_paint;
100 layer_paint.setColor(SK_ColorGREEN);
101 layer_paint.setAntiAlias(true);
102 SkPaint child1_paint;
103 child1_paint.setColor(SK_ColorRED);
104 child1_paint.setAntiAlias(true);
105 SkPaint child2_paint;
106 child2_paint.setColor(SK_ColorBLUE);
107 child2_paint.setAntiAlias(true);
108 layer->Paint(paint_context());
109 EXPECT_EQ(
110 mock_canvas().draw_calls(),
111 std::vector({MockCanvas::DrawCall{
112 0, MockCanvas::DrawPathData{layer_path, layer_paint}},
113 MockCanvas::DrawCall{
114 0, MockCanvas::DrawPathData{child1_path, child1_paint}},
115 MockCanvas::DrawCall{0, MockCanvas::DrawPathData{
116 child2_path, child2_paint}}}));
117}
118
119TEST_F(PhysicalShapeLayerTest, ElevationSimple) {
120 constexpr float initial_elevation = 20.0f;
121 SkPath layer_path;
122 layer_path.addRect(0, 0, 8, 8).close();
123 auto layer = std::make_shared<PhysicalShapeLayer>(
124 SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, Clip::none);
125
126 layer->Preroll(preroll_context(), SkMatrix());
127 // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
128 // their shadows , so we do not do any painting there.
129 EXPECT_EQ(layer->paint_bounds(),
130 PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(),
131 initial_elevation, 1.0f));
132 EXPECT_TRUE(layer->needs_painting());
133 EXPECT_FALSE(layer->needs_system_composite());
134 EXPECT_EQ(layer->elevation(), initial_elevation);
135
136 // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
137 // their shadows , so we do not use the direct |Paint()| path there.
138#if !defined(LEGACY_FUCHSIA_EMBEDDER)
139 SkPaint layer_paint;
140 layer_paint.setColor(SK_ColorGREEN);
141 layer_paint.setAntiAlias(true);
142 layer->Paint(paint_context());
143 EXPECT_EQ(
144 mock_canvas().draw_calls(),
145 std::vector(
146 {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
147 MockCanvas::DrawCall{
148 0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
149#endif
150}
151
152TEST_F(PhysicalShapeLayerTest, ElevationComplex) {
153 // The layer tree should look like this:
154 // layers[0] +1.0f = 1.0f
155 // | \
156 // | \
157 // | \
158 // | layers[2] +3.0f = 4.0f
159 // | |
160 // | layers[3] +4.0f = 8.0f
161 // |
162 // |
163 // layers[1] + 2.0f = 3.0f
164 constexpr float initial_elevations[4] = {1.0f, 2.0f, 3.0f, 4.0f};
165 SkPath layer_path;
166 layer_path.addRect(0, 0, 80, 80).close();
167
168 std::shared_ptr<PhysicalShapeLayer> layers[4];
169 for (int i = 0; i < 4; i += 1) {
170 layers[i] = std::make_shared<PhysicalShapeLayer>(
171 SK_ColorBLACK, SK_ColorBLACK, initial_elevations[i], layer_path,
172 Clip::none);
173 }
174 layers[0]->Add(layers[1]);
175 layers[0]->Add(layers[2]);
176 layers[2]->Add(layers[3]);
177
178 layers[0]->Preroll(preroll_context(), SkMatrix());
179 for (int i = 0; i < 4; i += 1) {
180 // On Fuchsia, the system compositor handles all elevated
181 // PhysicalShapeLayers and their shadows , so we do not do any painting
182 // there.
183 EXPECT_EQ(layers[i]->paint_bounds(),
184 (PhysicalShapeLayer::ComputeShadowBounds(
185 layer_path.getBounds(), initial_elevations[i],
186 1.0f /* pixel_ratio */)));
187 EXPECT_TRUE(layers[i]->needs_painting());
188 EXPECT_FALSE(layers[i]->needs_system_composite());
189 }
190
191 // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
192 // their shadows , so we do not use the direct |Paint()| path there.
193#if !defined(LEGACY_FUCHSIA_EMBEDDER)
194 SkPaint layer_paint;
195 layer_paint.setColor(SK_ColorBLACK);
196 layer_paint.setAntiAlias(true);
197 layers[0]->Paint(paint_context());
198 EXPECT_EQ(
199 mock_canvas().draw_calls(),
200 std::vector(
201 {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
202 MockCanvas::DrawCall{
203 0, MockCanvas::DrawPathData{layer_path, layer_paint}},
204 MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
205 MockCanvas::DrawCall{
206 0, MockCanvas::DrawPathData{layer_path, layer_paint}},
207 MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
208 MockCanvas::DrawCall{
209 0, MockCanvas::DrawPathData{layer_path, layer_paint}},
210 MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
211 MockCanvas::DrawCall{
212 0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
213#endif
214}
215
216static bool ReadbackResult(PrerollContext* context,
217 Clip clip_behavior,
218 std::shared_ptr<Layer> child,
219 bool before) {
220 const SkMatrix initial_matrix = SkMatrix();
221 const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
222 const SkPath layer_path = SkPath().addRect(layer_bounds);
223 auto layer =
224 std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
225 0.0f, // elevation
226 layer_path, clip_behavior);
227 if (child != nullptr) {
228 layer->Add(child);
229 }
230 context->surface_needs_readback = before;
231 layer->Preroll(context, initial_matrix);
232 return context->surface_needs_readback;
233}
234
235TEST_F(PhysicalShapeLayerTest, Readback) {
236 PrerollContext* context = preroll_context();
237 SkPath path;
238 SkPaint paint;
239
240 const Clip hard = Clip::hardEdge;
241 const Clip soft = Clip::antiAlias;
242 const Clip save_layer = Clip::antiAliasWithSaveLayer;
243
244 std::shared_ptr<MockLayer> nochild;
245 auto reader = std::make_shared<MockLayer>(path, paint, false, false, true);
246 auto nonreader = std::make_shared<MockLayer>(path, paint);
247
248 // No children, no prior readback -> no readback after
249 EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
250 EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
251 EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
252
253 // No children, prior readback -> readback after
254 EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
255 EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
256 EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
257
258 // Non readback child, no prior readback -> no readback after
259 EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
260 EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
261 EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
262
263 // Non readback child, prior readback -> readback after
264 EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
265 EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
266 EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
267
268 // Readback child, no prior readback -> readback after unless SaveLayer
269 EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
270 EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
271 EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
272
273 // Readback child, prior readback -> readback after
274 EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
275 EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
276 EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
277}
278
279} // namespace testing
280} // namespace flutter
281