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
12namespace flutter {
13namespace testing {
14
15using ClipPathLayerTest = LayerTest;
16
17#ifndef NDEBUG
18TEST_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
24TEST_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
37TEST_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
48TEST_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
76TEST_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
118TEST_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
153TEST_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
195static 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
211TEST_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