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