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
12namespace flutter {
13namespace testing {
14
15using ClipRRectLayerTest = LayerTest;
16
17#ifndef NDEBUG
18TEST_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
25TEST_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
39TEST_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
50TEST_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
79TEST_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
121TEST_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
156TEST_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
198static 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
214TEST_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