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