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 | |
15 | using PhysicalShapeLayerTest = LayerTest; |
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()); |
27 | EXPECT_FALSE(layer->needs_system_composite()); |
28 | |
29 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
30 | "needs_painting\\(\\)" ); |
31 | } |
32 | |
33 | TEST_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 | |
48 | TEST_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 | |
69 | TEST_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 | |
119 | TEST_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 | |
152 | TEST_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 | |
216 | static 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 | |
235 | TEST_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 | |