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"
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 = Rect::MakeXYWH(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 = Rect::MakeXYWH(5, 5, 10, 10);
35  Rect translated_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(5, 5, 10, 10);
46  Rect scaled_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(5, 5, 10, 10);
57 
58  Canvas canvas;
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 = Rect::MakeXYWH(5, 5, 10, 10);
67 
68  Canvas canvas;
70 
71  ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
72 }
73 
74 TEST(AiksCanvasTest, RectClipIntersectAgainstCullRect) {
75  Rect initial_cull = Rect::MakeXYWH(0, 0, 10, 10);
76  Rect rect_clip = Rect::MakeXYWH(5, 5, 10, 10);
77  Rect result_cull = Rect::MakeXYWH(5, 5, 5, 5);
78 
79  Canvas canvas(initial_cull);
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 = Rect::MakeXYWH(0, 0, 10, 10);
88  Rect rect_clip = Rect::MakeXYWH(5, 5, 10, 10);
89  Rect result_cull = Rect::MakeXYWH(0, 0, 10, 10);
90 
91  Canvas canvas(initial_cull);
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 = Rect::MakeXYWH(5, 5, 10, 10);
100  Rect rect_clip = Rect::MakeXYWH(0, 0, 20, 4);
101  Rect result_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(5, 5, 10, 10);
112  Rect rect_clip = Rect::MakeXYWH(0, 16, 20, 4);
113  Rect result_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(5, 5, 10, 10);
124  Rect rect_clip = Rect::MakeXYWH(0, 0, 4, 20);
125  Rect result_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(5, 5, 10, 10);
136  Rect rect_clip = Rect::MakeXYWH(16, 0, 4, 20);
137  Rect result_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(0, 0, 10, 10);
148  Rect rect_clip = Rect::MakeXYWH(5, 0, 10, 10);
149  Rect result_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(0, 0, 10, 10);
160  Rect rect_clip = Rect::MakeXYWH(0, 5, 10, 10);
161  Rect result_cull = Rect::MakeXYWH(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 = Rect::MakeXYWH(5, 5, 10, 10);
172 
173  Canvas canvas;
174  canvas.ClipRRect(rect_clip, {1, 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 = Rect::MakeXYWH(5, 5, 10, 10);
182 
183  Canvas canvas;
184  canvas.ClipRRect(rect_clip, {1, 1}, Entity::ClipOperation::kDifference);
185 
186  ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
187 }
188 
189 TEST(AiksCanvasTest, RRectClipIntersectAgainstCullRect) {
190  Rect initial_cull = Rect::MakeXYWH(0, 0, 10, 10);
191  Rect rect_clip = Rect::MakeXYWH(5, 5, 10, 10);
192  Rect result_cull = Rect::MakeXYWH(5, 5, 5, 5);
193 
194  Canvas canvas(initial_cull);
195  canvas.ClipRRect(rect_clip, {1, 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 = Rect::MakeXYWH(0, 0, 10, 10);
203  Rect rect_clip = Rect::MakeXYWH(5, 5, 10, 10);
204  Rect result_cull = Rect::MakeXYWH(0, 0, 10, 10);
205 
206  Canvas canvas(initial_cull);
207  canvas.ClipRRect(rect_clip, {1, 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 = Rect::MakeXYWH(0, 0, 10, 10);
215  Rect rect_clip = Rect::MakeXYWH(5, 0, 10, 10);
216  Rect result_cull = Rect::MakeXYWH(0, 0, 6, 10);
217 
218  Canvas canvas(initial_cull);
219  canvas.ClipRRect(rect_clip, {1, 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 = Rect::MakeXYWH(0, 0, 10, 10);
227  Rect rect_clip = Rect::MakeXYWH(5, -2, 10, 14);
228  Rect result_cull = Rect::MakeXYWH(0, 0, 5, 10);
229 
230  Canvas canvas(initial_cull);
231  canvas.ClipRRect(rect_clip, {1, 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 = Rect::MakeXYWH(0, 0, 10, 10);
239  Rect rect_clip = Rect::MakeXYWH(0, 5, 10, 10);
240  Rect result_cull = Rect::MakeXYWH(0, 0, 10, 6);
241 
242  Canvas canvas(initial_cull);
243  canvas.ClipRRect(rect_clip, {1, 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 = Rect::MakeXYWH(0, 0, 10, 10);
251  Rect rect_clip = Rect::MakeXYWH(-2, 5, 14, 10);
252  Rect result_cull = Rect::MakeXYWH(0, 0, 10, 5);
253 
254  Canvas canvas(initial_cull);
255  canvas.ClipRRect(rect_clip, {1, 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(Rect::MakeXYWH(5, 5, 1, 1));
264  builder.AddRect(Rect::MakeXYWH(5, 14, 1, 1));
265  builder.AddRect(Rect::MakeXYWH(14, 5, 1, 1));
266  builder.AddRect(Rect::MakeXYWH(14, 14, 1, 1));
267  Path path = builder.TakePath();
268  Rect rect_clip = Rect::MakeXYWH(5, 5, 10, 10);
269 
270  Canvas canvas;
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(Rect::MakeXYWH(5, 5, 1, 1));
280  builder.AddRect(Rect::MakeXYWH(5, 14, 1, 1));
281  builder.AddRect(Rect::MakeXYWH(14, 5, 1, 1));
282  builder.AddRect(Rect::MakeXYWH(14, 14, 1, 1));
283  Path path = builder.TakePath();
284 
285  Canvas canvas;
287 
288  ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
289 }
290 
291 TEST(AiksCanvasTest, PathClipIntersectAgainstCullRect) {
292  Rect initial_cull = Rect::MakeXYWH(0, 0, 10, 10);
293  PathBuilder builder;
294  builder.AddRect(Rect::MakeXYWH(5, 5, 1, 1));
295  builder.AddRect(Rect::MakeXYWH(5, 14, 1, 1));
296  builder.AddRect(Rect::MakeXYWH(14, 5, 1, 1));
297  builder.AddRect(Rect::MakeXYWH(14, 14, 1, 1));
298  Path path = builder.TakePath();
299  Rect result_cull = Rect::MakeXYWH(5, 5, 5, 5);
300 
301  Canvas canvas(initial_cull);
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 = Rect::MakeXYWH(0, 0, 10, 10);
310  PathBuilder builder;
311  builder.AddRect(Rect::MakeXYWH(5, 5, 1, 1));
312  builder.AddRect(Rect::MakeXYWH(5, 14, 1, 1));
313  builder.AddRect(Rect::MakeXYWH(14, 5, 1, 1));
314  builder.AddRect(Rect::MakeXYWH(14, 14, 1, 1));
315  Path path = builder.TakePath();
316  Rect result_cull = Rect::MakeXYWH(0, 0, 10, 10);
317 
318  Canvas canvas(initial_cull);
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 = Rect::MakeXYWH(5, 5, 10, 10);
327  PathBuilder builder;
328  builder.AddRect(Rect::MakeXYWH(0, 0, 100, 100));
329  Path path = builder.TakePath();
330  // Diff clip of Paths is ignored due to complexity
331  Rect result_cull = Rect::MakeXYWH(5, 5, 10, 10);
332 
333  Canvas canvas(initial_cull);
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(
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:23
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::Canvas
Definition: canvas.h:58
impeller::PathBuilder
Definition: path_builder.h:14
impeller::Vector2
Point Vector2
Definition: point.h:320
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::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:112
impeller::Canvas::SaveLayer
void SaveLayer(const Paint &paint, std::optional< Rect > bounds=std::nullopt, const std::shared_ptr< ImageFilter > &backdrop_filter=nullptr, ContentBoundsPromise bounds_promise=ContentBoundsPromise::kUnknown)
Definition: canvas.cc:786
path_builder.h
impeller::Canvas::Scale
void Scale(const Vector2 &scale)
Definition: canvas.cc:264
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:51
impeller::Canvas::Save
void Save()
Definition: canvas.cc:136
impeller::testing::TEST
TEST(CanvasRecorder, Save)
Definition: canvas_recorder_unittests.cc:65
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
impeller::Canvas::Restore
bool Restore()
Definition: canvas.cc:208
canvas.h
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
impeller::testing::AiksCanvasTest
::testing::Test AiksCanvasTest
Definition: canvas_unittests.cc:16
impeller::Canvas::ClipRRect
void ClipRRect(const Rect &rect, const Size &corner_radii, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:590
impeller::Canvas::GetCurrentLocalCullingBounds
const std::optional< Rect > GetCurrentLocalCullingBounds() const
Definition: canvas.cc:251
impeller::Canvas::ClipRect
void ClipRect(const Rect &rect, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:549
impeller
Definition: aiks_blur_unittests.cc:20
impeller::TRect
Definition: rect.h:122
impeller::Vector3
Definition: vector.h:20
impeller::Paint::image_filter
std::shared_ptr< ImageFilter > image_filter
Definition: paint.h:67
impeller::Canvas::Translate
void Translate(const Vector3 &offset)
Definition: canvas.cc:260
impeller::Canvas::ClipPath
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition: canvas.cc:539