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