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/clip_path_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 ClipPathLayerTest = LayerTest; |
16 | |
17 | #ifndef NDEBUG |
18 | TEST_F(ClipPathLayerTest, ClipNoneBehaviorDies) { |
19 | EXPECT_DEATH_IF_SUPPORTED( |
20 | auto clip = std::make_shared<ClipPathLayer>(SkPath(), Clip::none), |
21 | "clip_behavior != Clip::none" ); |
22 | } |
23 | |
24 | TEST_F(ClipPathLayerTest, PaintingEmptyLayerDies) { |
25 | auto layer = std::make_shared<ClipPathLayer>(SkPath(), Clip::hardEdge); |
26 | |
27 | layer->Preroll(preroll_context(), SkMatrix()); |
28 | EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched |
29 | EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched |
30 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
31 | EXPECT_FALSE(layer->needs_painting()); |
32 | |
33 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
34 | "needs_painting\\(\\)" ); |
35 | } |
36 | |
37 | TEST_F(ClipPathLayerTest, PaintBeforePrerollDies) { |
38 | const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); |
39 | const SkPath layer_path = SkPath().addRect(layer_bounds); |
40 | auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::hardEdge); |
41 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
42 | EXPECT_FALSE(layer->needs_painting()); |
43 | |
44 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
45 | "needs_painting\\(\\)" ); |
46 | } |
47 | |
48 | TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { |
49 | const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); |
50 | const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); |
51 | const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); |
52 | const SkPath child_path = SkPath().addRect(child_bounds); |
53 | const SkPath layer_path = SkPath().addRect(layer_bounds); |
54 | auto mock_layer = std::make_shared<MockLayer>(child_path); |
55 | auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::hardEdge); |
56 | layer->Add(mock_layer); |
57 | |
58 | preroll_context()->cull_rect = kEmptyRect; // Cull everything |
59 | |
60 | layer->Preroll(preroll_context(), initial_matrix); |
61 | EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched |
62 | EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched |
63 | EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); |
64 | EXPECT_EQ(layer->paint_bounds(), kEmptyRect); |
65 | EXPECT_FALSE(mock_layer->needs_painting()); |
66 | EXPECT_FALSE(layer->needs_painting()); |
67 | EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); |
68 | EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); |
69 | EXPECT_EQ(mock_layer->parent_mutators(), std::vector<Mutator>()); |
70 | |
71 | EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
72 | "needs_painting\\(\\)" ); |
73 | } |
74 | #endif |
75 | |
76 | TEST_F(ClipPathLayerTest, ChildOutsideBounds) { |
77 | const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); |
78 | const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); |
79 | const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); |
80 | const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); |
81 | const SkPath child_path = SkPath().addRect(child_bounds); |
82 | const SkPath layer_path = SkPath().addRect(layer_bounds); |
83 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
84 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
85 | auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::hardEdge); |
86 | layer->Add(mock_layer); |
87 | |
88 | SkRect intersect_bounds = layer_bounds; |
89 | SkRect child_intersect_bounds = layer_bounds; |
90 | intersect_bounds.intersect(cull_bounds); |
91 | child_intersect_bounds.intersect(child_bounds); |
92 | preroll_context()->cull_rect = cull_bounds; // Cull child |
93 | |
94 | layer->Preroll(preroll_context(), initial_matrix); |
95 | EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched |
96 | EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched |
97 | EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); |
98 | EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); |
99 | EXPECT_TRUE(mock_layer->needs_painting()); |
100 | EXPECT_TRUE(layer->needs_painting()); |
101 | EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); |
102 | EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); |
103 | EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); |
104 | |
105 | layer->Paint(paint_context()); |
106 | EXPECT_EQ( |
107 | mock_canvas().draw_calls(), |
108 | std::vector( |
109 | {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, |
110 | MockCanvas::DrawCall{ |
111 | 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, |
112 | MockCanvas::kHard_ClipEdgeStyle}}, |
113 | MockCanvas::DrawCall{ |
114 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
115 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
116 | } |
117 | |
118 | TEST_F(ClipPathLayerTest, FullyContainedChild) { |
119 | const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); |
120 | const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); |
121 | const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); |
122 | const SkPath child_path = SkPath().addRect(child_bounds); |
123 | const SkPath layer_path = SkPath().addRect(layer_bounds); |
124 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
125 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
126 | auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::hardEdge); |
127 | layer->Add(mock_layer); |
128 | |
129 | layer->Preroll(preroll_context(), initial_matrix); |
130 | EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched |
131 | EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched |
132 | EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); |
133 | EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); |
134 | EXPECT_TRUE(mock_layer->needs_painting()); |
135 | EXPECT_TRUE(layer->needs_painting()); |
136 | EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); |
137 | EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); |
138 | EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); |
139 | |
140 | layer->Paint(paint_context()); |
141 | EXPECT_EQ( |
142 | mock_canvas().draw_calls(), |
143 | std::vector( |
144 | {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, |
145 | MockCanvas::DrawCall{ |
146 | 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, |
147 | MockCanvas::kHard_ClipEdgeStyle}}, |
148 | MockCanvas::DrawCall{ |
149 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
150 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
151 | } |
152 | |
153 | TEST_F(ClipPathLayerTest, PartiallyContainedChild) { |
154 | const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); |
155 | const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 4.0, 5.5); |
156 | const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); |
157 | const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); |
158 | const SkPath child_path = SkPath().addRect(child_bounds); |
159 | const SkPath layer_path = SkPath().addRect(layer_bounds); |
160 | const SkPaint child_paint = SkPaint(SkColors::kYellow); |
161 | auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
162 | auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::hardEdge); |
163 | layer->Add(mock_layer); |
164 | |
165 | SkRect intersect_bounds = layer_bounds; |
166 | SkRect child_intersect_bounds = layer_bounds; |
167 | intersect_bounds.intersect(cull_bounds); |
168 | child_intersect_bounds.intersect(child_bounds); |
169 | preroll_context()->cull_rect = cull_bounds; // Cull child |
170 | |
171 | layer->Preroll(preroll_context(), initial_matrix); |
172 | EXPECT_EQ(preroll_context()->cull_rect, cull_bounds); // Untouched |
173 | EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched |
174 | EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); |
175 | EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); |
176 | EXPECT_TRUE(mock_layer->needs_painting()); |
177 | EXPECT_TRUE(layer->needs_painting()); |
178 | EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); |
179 | EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); |
180 | EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); |
181 | |
182 | layer->Paint(paint_context()); |
183 | EXPECT_EQ( |
184 | mock_canvas().draw_calls(), |
185 | std::vector( |
186 | {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, |
187 | MockCanvas::DrawCall{ |
188 | 1, MockCanvas::ClipRectData{layer_bounds, SkClipOp::kIntersect, |
189 | MockCanvas::kHard_ClipEdgeStyle}}, |
190 | MockCanvas::DrawCall{ |
191 | 1, MockCanvas::DrawPathData{child_path, child_paint}}, |
192 | MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); |
193 | } |
194 | |
195 | static bool ReadbackResult(PrerollContext* context, |
196 | Clip clip_behavior, |
197 | std::shared_ptr<Layer> child, |
198 | bool before) { |
199 | const SkMatrix initial_matrix = SkMatrix(); |
200 | const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); |
201 | const SkPath layer_path = SkPath().addRect(layer_bounds); |
202 | auto layer = std::make_shared<ClipPathLayer>(layer_path, clip_behavior); |
203 | if (child != nullptr) { |
204 | layer->Add(child); |
205 | } |
206 | context->surface_needs_readback = before; |
207 | layer->Preroll(context, initial_matrix); |
208 | return context->surface_needs_readback; |
209 | } |
210 | |
211 | TEST_F(ClipPathLayerTest, Readback) { |
212 | PrerollContext* context = preroll_context(); |
213 | SkPath path; |
214 | SkPaint paint; |
215 | |
216 | const Clip hard = Clip::hardEdge; |
217 | const Clip soft = Clip::antiAlias; |
218 | const Clip save_layer = Clip::antiAliasWithSaveLayer; |
219 | |
220 | std::shared_ptr<MockLayer> nochild; |
221 | auto reader = std::make_shared<MockLayer>(path, paint, false, false, true); |
222 | auto nonreader = std::make_shared<MockLayer>(path, paint); |
223 | |
224 | // No children, no prior readback -> no readback after |
225 | EXPECT_FALSE(ReadbackResult(context, hard, nochild, false)); |
226 | EXPECT_FALSE(ReadbackResult(context, soft, nochild, false)); |
227 | EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false)); |
228 | |
229 | // No children, prior readback -> readback after |
230 | EXPECT_TRUE(ReadbackResult(context, hard, nochild, true)); |
231 | EXPECT_TRUE(ReadbackResult(context, soft, nochild, true)); |
232 | EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true)); |
233 | |
234 | // Non readback child, no prior readback -> no readback after |
235 | EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false)); |
236 | EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false)); |
237 | EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false)); |
238 | |
239 | // Non readback child, prior readback -> readback after |
240 | EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true)); |
241 | EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true)); |
242 | EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true)); |
243 | |
244 | // Readback child, no prior readback -> readback after unless SaveLayer |
245 | EXPECT_TRUE(ReadbackResult(context, hard, reader, false)); |
246 | EXPECT_TRUE(ReadbackResult(context, soft, reader, false)); |
247 | EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false)); |
248 | |
249 | // Readback child, prior readback -> readback after |
250 | EXPECT_TRUE(ReadbackResult(context, hard, reader, true)); |
251 | EXPECT_TRUE(ReadbackResult(context, soft, reader, true)); |
252 | EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true)); |
253 | } |
254 | |
255 | } // namespace testing |
256 | } // namespace flutter |
257 | |