| Flutter Impeller |

canvas_unittests.cc
Go to the documentation of this file.
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/testing/testing.h"
6 #include "impeller/aiks/canvas.h"
7 #include "impeller/geometry/path_builder.h"
8
9 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
10 // NOLINTBEGIN(bugprone-unchecked-optional-access)
11
12 namespace impeller {
13 namespace testing {
14
15 using AiksCanvasTest = ::testing::Test;
16
17 TEST(AiksCanvasTest, EmptyCullRect) {
18 Canvas canvas;
19
20 ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
21 }
22
23 TEST(AiksCanvasTest, InitialCullRect) {
24 Rect initial_cull(0, 0, 10, 10);
25
26 Canvas canvas(initial_cull);
27
28 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
29 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), initial_cull);
30 }
31
32 TEST(AiksCanvasTest, TranslatedCullRect) {
33 Rect initial_cull(5, 5, 10, 10);
34 Rect translated_cull(0, 0, 10, 10);
35
36 Canvas canvas(initial_cull);
37 canvas.Translate(Vector3(5, 5, 0));
38
39 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
40 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), translated_cull);
41 }
42
43 TEST(AiksCanvasTest, ScaledCullRect) {
44 Rect initial_cull(5, 5, 10, 10);
45 Rect scaled_cull(10, 10, 20, 20);
46
47 Canvas canvas(initial_cull);
48 canvas.Scale(Vector2(0.5, 0.5));
49
50 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
51 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), scaled_cull);
52 }
53
54 TEST(AiksCanvasTest, RectClipIntersectAgainstEmptyCullRect) {
55 Rect rect_clip(5, 5, 10, 10);
56
57 Canvas canvas;
58 canvas.ClipRect(rect_clip, Entity::ClipOperation::kIntersect);
59
60 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
61 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), rect_clip);
62 }
63
64 TEST(AiksCanvasTest, RectClipDiffAgainstEmptyCullRect) {
65 Rect rect_clip(5, 5, 10, 10);
66
67 Canvas canvas;
68 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
69
70 ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
71 }
72
73 TEST(AiksCanvasTest, RectClipIntersectAgainstCullRect) {
74 Rect initial_cull(0, 0, 10, 10);
75 Rect rect_clip(5, 5, 10, 10);
76 Rect result_cull(5, 5, 5, 5);
77
78 Canvas canvas(initial_cull);
79 canvas.ClipRect(rect_clip, Entity::ClipOperation::kIntersect);
80
81 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
82 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
83 }
84
85 TEST(AiksCanvasTest, RectClipDiffAgainstNonCoveredCullRect) {
86 Rect initial_cull(0, 0, 10, 10);
87 Rect rect_clip(5, 5, 10, 10);
88 Rect result_cull(0, 0, 10, 10);
89
90 Canvas canvas(initial_cull);
91 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
92
93 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
94 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
95 }
96
97 TEST(AiksCanvasTest, RectClipDiffAboveCullRect) {
98 Rect initial_cull(5, 5, 10, 10);
99 Rect rect_clip(0, 0, 20, 4);
100 Rect result_cull(5, 5, 10, 10);
101
102 Canvas canvas(initial_cull);
103 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
104
105 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
106 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
107 }
108
109 TEST(AiksCanvasTest, RectClipDiffBelowCullRect) {
110 Rect initial_cull(5, 5, 10, 10);
111 Rect rect_clip(0, 16, 20, 4);
112 Rect result_cull(5, 5, 10, 10);
113
114 Canvas canvas(initial_cull);
115 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
116
117 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
118 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
119 }
120
121 TEST(AiksCanvasTest, RectClipDiffLeftOfCullRect) {
122 Rect initial_cull(5, 5, 10, 10);
123 Rect rect_clip(0, 0, 4, 20);
124 Rect result_cull(5, 5, 10, 10);
125
126 Canvas canvas(initial_cull);
127 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
128
129 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
130 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
131 }
132
133 TEST(AiksCanvasTest, RectClipDiffRightOfCullRect) {
134 Rect initial_cull(5, 5, 10, 10);
135 Rect rect_clip(16, 0, 4, 20);
136 Rect result_cull(5, 5, 10, 10);
137
138 Canvas canvas(initial_cull);
139 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
140
141 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
142 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
143 }
144
145 TEST(AiksCanvasTest, RectClipDiffAgainstVCoveredCullRect) {
146 Rect initial_cull(0, 0, 10, 10);
147 Rect rect_clip(5, 0, 10, 10);
148 Rect result_cull(0, 0, 5, 10);
149
150 Canvas canvas(initial_cull);
151 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
152
153 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
154 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
155 }
156
157 TEST(AiksCanvasTest, RectClipDiffAgainstHCoveredCullRect) {
158 Rect initial_cull(0, 0, 10, 10);
159 Rect rect_clip(0, 5, 10, 10);
160 Rect result_cull(0, 0, 10, 5);
161
162 Canvas canvas(initial_cull);
163 canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
164
165 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
166 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
167 }
168
169 TEST(AiksCanvasTest, RRectClipIntersectAgainstEmptyCullRect) {
170 Rect rect_clip(5, 5, 10, 10);
171
172 Canvas canvas;
173 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kIntersect);
174
175 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
176 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), rect_clip);
177 }
178
179 TEST(AiksCanvasTest, RRectClipDiffAgainstEmptyCullRect) {
180 Rect rect_clip(5, 5, 10, 10);
181
182 Canvas canvas;
183 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
184
185 ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
186 }
187
188 TEST(AiksCanvasTest, RRectClipIntersectAgainstCullRect) {
189 Rect initial_cull(0, 0, 10, 10);
190 Rect rect_clip(5, 5, 10, 10);
191 Rect result_cull(5, 5, 5, 5);
192
193 Canvas canvas(initial_cull);
194 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kIntersect);
195
196 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
197 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
198 }
199
200 TEST(AiksCanvasTest, RRectClipDiffAgainstNonCoveredCullRect) {
201 Rect initial_cull(0, 0, 10, 10);
202 Rect rect_clip(5, 5, 10, 10);
203 Rect result_cull(0, 0, 10, 10);
204
205 Canvas canvas(initial_cull);
206 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
207
208 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
209 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
210 }
211
212 TEST(AiksCanvasTest, RRectClipDiffAgainstVPartiallyCoveredCullRect) {
213 Rect initial_cull(0, 0, 10, 10);
214 Rect rect_clip(5, 0, 10, 10);
215 Rect result_cull(0, 0, 6, 10);
216
217 Canvas canvas(initial_cull);
218 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
219
220 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
221 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
222 }
223
224 TEST(AiksCanvasTest, RRectClipDiffAgainstVFullyCoveredCullRect) {
225 Rect initial_cull(0, 0, 10, 10);
226 Rect rect_clip(5, -2, 10, 14);
227 Rect result_cull(0, 0, 5, 10);
228
229 Canvas canvas(initial_cull);
230 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
231
232 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
233 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
234 }
235
236 TEST(AiksCanvasTest, RRectClipDiffAgainstHPartiallyCoveredCullRect) {
237 Rect initial_cull(0, 0, 10, 10);
238 Rect rect_clip(0, 5, 10, 10);
239 Rect result_cull(0, 0, 10, 6);
240
241 Canvas canvas(initial_cull);
242 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
243
244 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
245 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
246 }
247
248 TEST(AiksCanvasTest, RRectClipDiffAgainstHFullyCoveredCullRect) {
249 Rect initial_cull(0, 0, 10, 10);
250 Rect rect_clip(-2, 5, 14, 10);
251 Rect result_cull(0, 0, 10, 5);
252
253 Canvas canvas(initial_cull);
254 canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
255
256 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
257 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
258 }
259
260 TEST(AiksCanvasTest, PathClipIntersectAgainstEmptyCullRect) {
261 PathBuilder builder;
262 builder.AddRect({5, 5, 1, 1});
263 builder.AddRect({5, 14, 1, 1});
264 builder.AddRect({14, 5, 1, 1});
265 builder.AddRect({14, 14, 1, 1});
266 Path path = builder.TakePath();
267 Rect rect_clip(5, 5, 10, 10);
268
269 Canvas canvas;
270 canvas.ClipPath(path, Entity::ClipOperation::kIntersect);
271
272 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
273 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), rect_clip);
274 }
275
276 TEST(AiksCanvasTest, PathClipDiffAgainstEmptyCullRect) {
277 PathBuilder builder;
278 builder.AddRect({5, 5, 1, 1});
279 builder.AddRect({5, 14, 1, 1});
280 builder.AddRect({14, 5, 1, 1});
281 builder.AddRect({14, 14, 1, 1});
282 Path path = builder.TakePath();
283
284 Canvas canvas;
285 canvas.ClipPath(path, Entity::ClipOperation::kDifference);
286
287 ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
288 }
289
290 TEST(AiksCanvasTest, PathClipIntersectAgainstCullRect) {
291 Rect initial_cull(0, 0, 10, 10);
292 PathBuilder builder;
293 builder.AddRect({5, 5, 1, 1});
294 builder.AddRect({5, 14, 1, 1});
295 builder.AddRect({14, 5, 1, 1});
296 builder.AddRect({14, 14, 1, 1});
297 Path path = builder.TakePath();
298 Rect result_cull(5, 5, 5, 5);
299
300 Canvas canvas(initial_cull);
301 canvas.ClipPath(path, Entity::ClipOperation::kIntersect);
302
303 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
304 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
305 }
306
307 TEST(AiksCanvasTest, PathClipDiffAgainstNonCoveredCullRect) {
308 Rect initial_cull(0, 0, 10, 10);
309 PathBuilder builder;
310 builder.AddRect({5, 5, 1, 1});
311 builder.AddRect({5, 14, 1, 1});
312 builder.AddRect({14, 5, 1, 1});
313 builder.AddRect({14, 14, 1, 1});
314 Path path = builder.TakePath();
315 Rect result_cull(0, 0, 10, 10);
316
317 Canvas canvas(initial_cull);
318 canvas.ClipPath(path, Entity::ClipOperation::kDifference);
319
320 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
321 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
322 }
323
324 TEST(AiksCanvasTest, PathClipDiffAgainstFullyCoveredCullRect) {
325 Rect initial_cull(5, 5, 10, 10);
326 PathBuilder builder;
327 builder.AddRect({0, 0, 100, 100});
328 Path path = builder.TakePath();
329 // Diff clip of Paths is ignored due to complexity
330 Rect result_cull(5, 5, 10, 10);
331
332 Canvas canvas(initial_cull);
333 canvas.ClipPath(path, Entity::ClipOperation::kDifference);
334
335 ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
336 ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
337 }
338
339 } // namespace testing
340 } // namespace impeller
341
342 // NOLINTEND(bugprone-unchecked-optional-access)
impeller::Entity::ClipOperation::kIntersect
@ kIntersect
impeller::Entity::ClipOperation::kDifference
@ kDifference
impeller::Canvas
Definition: canvas.h:50
impeller::PathBuilder
Definition: path_builder.h:13
impeller::testing::TEST
TEST(AiksCanvasTest, EmptyCullRect)
Definition: canvas_unittests.cc:17
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:181
path_builder.h
impeller::Canvas::Scale
void Scale(const Vector2 &scale)
Definition: canvas.cc:145
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:54
canvas.h
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:21
impeller::testing::AiksCanvasTest
::testing::Test AiksCanvasTest
Definition: canvas_unittests.cc:15
impeller::Canvas::ClipRRect
void ClipRRect(const Rect &rect, Scalar corner_radius, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:315
impeller::Canvas::GetCurrentLocalCullingBounds
const std::optional< Rect > GetCurrentLocalCullingBounds() const
Definition: canvas.cc:132
impeller::Canvas::ClipRect
void ClipRect(const Rect &rect, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:294
impeller
Definition: aiks_context.cc:10
impeller::TRect< Scalar >
impeller::Vector3
Definition: vector.h:17
impeller::Canvas::Translate
void Translate(const Vector3 &offset)
Definition: canvas.cc:141
impeller::Canvas::ClipPath
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:284