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
12namespace flutter {
13namespace testing {
14
15using ClipRectLayerTest = LayerTest;
16
17#ifndef NDEBUG
18TEST_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
24TEST_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
37TEST_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
47TEST_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
74TEST_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
116TEST_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
151TEST_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
193static 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
208TEST_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