Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
aiks_path_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/impeller/aiks/aiks_unittests.h"
6
10#include "third_party/imgui/imgui.h"
11
12////////////////////////////////////////////////////////////////////////////////
13// This is for tests of Canvas that are interested the results of rendering
14// paths.
15////////////////////////////////////////////////////////////////////////////////
16
17namespace impeller {
18namespace testing {
19
20TEST_P(AiksTest, RotateColorFilteredPath) {
21 Canvas canvas;
22 canvas.Concat(Matrix::MakeTranslation({300, 300}));
24 auto arrow_stem =
25 PathBuilder{}.MoveTo({120, 190}).LineTo({120, 50}).TakePath();
26 auto arrow_head = PathBuilder{}
27 .MoveTo({50, 120})
28 .LineTo({120, 190})
29 .LineTo({190, 120})
30 .TakePath();
31 auto paint = Paint{
32 .stroke_width = 15.0,
33 .stroke_cap = Cap::kRound,
34 .stroke_join = Join::kRound,
35 .style = Paint::Style::kStroke,
36 .color_filter =
38 };
39
40 canvas.DrawPath(arrow_stem, paint);
41 canvas.DrawPath(arrow_head, paint);
42 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
43}
44
45TEST_P(AiksTest, CanRenderStrokes) {
46 Canvas canvas;
48 paint.color = Color::Red();
49 paint.stroke_width = 20.0;
51 canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).TakePath(),
52 paint);
53 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
54}
55
56TEST_P(AiksTest, CanRenderCurvedStrokes) {
57 Canvas canvas;
59 paint.color = Color::Red();
60 paint.stroke_width = 25.0;
62 canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint);
63 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
64}
65
66TEST_P(AiksTest, CanRenderThickCurvedStrokes) {
67 Canvas canvas;
69 paint.color = Color::Red();
70 paint.stroke_width = 100.0;
72 canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath(), paint);
73 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
74}
75
76TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) {
77 Canvas canvas;
78
80 paint.color = Color::Red();
82 paint.stroke_width = 200;
83
84 Rect rect = Rect::MakeXYWH(100, 100, 200, 200);
85 PathBuilder builder;
86 builder.AddArc(rect, Degrees(0), Degrees(90), false);
87
88 canvas.DrawPath(builder.TakePath(), paint);
89 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
90}
91
92TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) {
93 Canvas canvas;
94
96 paint.color = Color::Red();
98 paint.stroke_width = 20;
99
100 PathBuilder builder;
101 builder.AddCubicCurve({0, 200}, {50, 400}, {350, 0}, {400, 200});
102
103 canvas.DrawPath(builder.TakePath(), paint);
104 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
105}
106
107TEST_P(AiksTest, CanRenderDifferencePaths) {
108 Canvas canvas;
109
110 Paint paint;
111 paint.color = Color::Red();
112
113 PathBuilder builder;
114
116 radii.top_left = {50, 25};
117 radii.top_right = {25, 50};
118 radii.bottom_right = {50, 25};
119 radii.bottom_left = {25, 50};
120
121 builder.AddRoundedRect(Rect::MakeXYWH(100, 100, 200, 200), radii);
122 builder.AddCircle({200, 200}, 50);
123 auto path = builder.TakePath(FillType::kOdd);
124
125 canvas.DrawImage(
126 std::make_shared<Image>(CreateTextureForFixture("boston.jpg")), {10, 10},
127 Paint{});
128 canvas.DrawPath(path, paint);
129
130 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
131}
132
133// Regression test for https://github.com/flutter/flutter/issues/134816.
134//
135// It should be possible to draw 3 lines, and not have an implicit close path.
136TEST_P(AiksTest, CanDrawAnOpenPath) {
137 Canvas canvas;
138
139 // Starting at (50, 50), draw lines from:
140 // 1. (50, height)
141 // 2. (width, height)
142 // 3. (width, 50)
143 PathBuilder builder;
144 builder.MoveTo({50, 50});
145 builder.LineTo({50, 100});
146 builder.LineTo({100, 100});
147 builder.LineTo({100, 50});
148
149 Paint paint;
150 paint.color = Color::Red();
152 paint.stroke_width = 10;
153
154 canvas.DrawPath(builder.TakePath(), paint);
155
156 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
157}
158
159TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) {
160 Canvas canvas;
161
162 // Draw a stroked path that is explicitly closed to verify
163 // It doesn't become a rectangle.
164 PathBuilder builder;
165 builder.MoveTo({50, 50});
166 builder.LineTo({520, 120});
167 builder.LineTo({300, 310});
168 builder.LineTo({100, 50});
169 builder.Close();
170
171 Paint paint;
172 paint.color = Color::Red();
174 paint.stroke_width = 10;
175
176 canvas.DrawPath(builder.TakePath(), paint);
177
178 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
179}
180
181TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
182 // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
183 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
184 static Color color = Color::Black().WithAlpha(0.5);
185 static float scale = 3;
186 static bool add_circle_clip = true;
187
188 if (AiksTest::ImGuiBegin("Controls", nullptr,
189 ImGuiWindowFlags_AlwaysAutoResize)) {
190 ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
191 ImGui::SliderFloat("Scale", &scale, 0, 6);
192 ImGui::Checkbox("Circle clip", &add_circle_clip);
193 ImGui::End();
194 }
195
196 Canvas canvas;
197 canvas.Scale(GetContentScale());
198 Paint paint;
199
200 paint.color = Color::White();
201 canvas.DrawPaint(paint);
202
203 paint.color = color;
205 paint.stroke_width = 10;
206
207 Path path = PathBuilder{}
208 .MoveTo({20, 20})
209 .QuadraticCurveTo({60, 20}, {60, 60})
210 .Close()
211 .MoveTo({60, 20})
212 .QuadraticCurveTo({60, 60}, {20, 60})
213 .TakePath();
214
215 canvas.Scale(Vector2(scale, scale));
216
217 if (add_circle_clip) {
218 static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
219 Color::Red());
220 static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
221 Color::Red());
222 auto [handle_a, handle_b] =
223 DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
224
225 auto screen_to_canvas = canvas.GetCurrentTransform().Invert();
226 Point point_a = screen_to_canvas * handle_a * GetContentScale();
227 Point point_b = screen_to_canvas * handle_b * GetContentScale();
228
229 Point middle = (point_a + point_b) / 2;
230 auto radius = point_a.GetDistance(middle);
231 canvas.ClipPath(PathBuilder{}.AddCircle(middle, radius).TakePath());
232 }
233
234 for (auto join : {Join::kBevel, Join::kRound, Join::kMiter}) {
235 paint.stroke_join = join;
236 for (auto cap : {Cap::kButt, Cap::kSquare, Cap::kRound}) {
237 paint.stroke_cap = cap;
238 canvas.DrawPath(path, paint);
239 canvas.Translate({80, 0});
240 }
241 canvas.Translate({-240, 60});
242 }
243
244 return canvas.EndRecordingAsPicture();
245 };
246
247 ASSERT_TRUE(OpenPlaygroundHere(callback));
248}
249
250TEST_P(AiksTest, DrawLinesRenderCorrectly) {
251 Canvas canvas;
252 canvas.Scale(GetContentScale());
253 Paint paint;
254 paint.color = Color::Blue();
255 paint.stroke_width = 10;
256
257 auto draw = [&canvas](Paint& paint) {
258 for (auto cap : {Cap::kButt, Cap::kSquare, Cap::kRound}) {
259 paint.stroke_cap = cap;
260 Point origin = {100, 100};
261 Point p0 = {50, 0};
262 Point p1 = {150, 0};
263 canvas.DrawLine({150, 100}, {250, 100}, paint);
264 for (int d = 15; d < 90; d += 15) {
266 canvas.DrawLine(origin + m * p0, origin + m * p1, paint);
267 }
268 canvas.DrawLine({100, 150}, {100, 250}, paint);
269 canvas.DrawCircle({origin}, 35, paint);
270
271 canvas.DrawLine({250, 250}, {250, 250}, paint);
272
273 canvas.Translate({250, 0});
274 }
275 canvas.Translate({-750, 250});
276 };
277
278 std::vector<Color> colors = {
279 Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
280 Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
281 Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
282 Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
283 Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
284 Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
285 Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
286 std::vector<Scalar> stops = {
287 0.0,
288 (1.0 / 6.0) * 1,
289 (1.0 / 6.0) * 2,
290 (1.0 / 6.0) * 3,
291 (1.0 / 6.0) * 4,
292 (1.0 / 6.0) * 5,
293 1.0,
294 };
295
296 auto texture = CreateTextureForFixture("airplane.jpg",
297 /*enable_mipmapping=*/true);
298
299 draw(paint);
300
302 {100, 100}, 200, std::move(colors), std::move(stops),
304 draw(paint);
305
306 paint.color_source = ColorSource::MakeImage(
308 Matrix::MakeTranslation({-150, 75}));
309 draw(paint);
310
311 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
312}
313
314TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
315 Canvas canvas;
316 Paint paint;
317 paint.color = Color::Red();
319 paint.stroke_width = 10;
320
321 canvas.Translate({100, 100});
322 canvas.DrawPath(
323 PathBuilder{}.AddRect(Rect::MakeSize(Size{100, 100})).TakePath(),
324 {paint});
325
326 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
327}
328
329TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) {
330 Canvas canvas;
331 Paint paint;
332 paint.color = Color::Red();
334 paint.stroke_width = 10;
335 paint.stroke_join = Join::kBevel;
336
337 canvas.Translate({100, 100});
338 canvas.DrawPath(
339 PathBuilder{}.AddRect(Rect::MakeSize(Size{100, 100})).TakePath(),
340 {paint});
341
342 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
343}
344
345TEST_P(AiksTest, CanDrawMultiContourConvexPath) {
346 PathBuilder builder = {};
347 for (auto i = 0; i < 10; i++) {
348 if (i % 2 == 0) {
349 builder.AddCircle(Point(100 + 50 * i, 100 + 50 * i), 100);
350 } else {
351 builder.MoveTo({100.f + 50.f * i - 100, 100.f + 50.f * i});
352 builder.LineTo({100.f + 50.f * i, 100.f + 50.f * i - 100});
353 builder.LineTo({100.f + 50.f * i - 100, 100.f + 50.f * i - 100});
354 builder.Close();
355 }
356 }
357 builder.SetConvexity(Convexity::kConvex);
358
359 Canvas canvas;
360 canvas.DrawPath(builder.TakePath(), {.color = Color::Red().WithAlpha(0.4)});
361
362 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
363}
364
365TEST_P(AiksTest, ArcWithZeroSweepAndBlur) {
366 Canvas canvas;
367 canvas.Scale(GetContentScale());
368
369 Paint paint;
370 paint.color = Color::Red();
371 std::vector<Color> colors = {Color{1.0, 0.0, 0.0, 1.0},
372 Color{0.0, 0.0, 0.0, 1.0}};
373 std::vector<Scalar> stops = {0.0, 1.0};
375 {100, 100}, Degrees(45), Degrees(135), std::move(colors),
376 std::move(stops), Entity::TileMode::kMirror, {});
377 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
379 .sigma = Sigma(20),
380 };
381
382 PathBuilder builder;
383 builder.AddArc(Rect::MakeXYWH(10, 10, 100, 100), Degrees(0), Degrees(0),
384 false);
385 canvas.DrawPath(builder.TakePath(), paint);
386
387 // Check that this empty picture can be created without crashing.
388 canvas.EndRecordingAsPicture();
389}
390
391TEST_P(AiksTest, CanRenderClips) {
392 Canvas canvas;
393 Paint paint;
394 paint.color = Color::Fuchsia();
395 canvas.ClipPath(
396 PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).TakePath());
397 canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint);
398 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
399}
400
401TEST_P(AiksTest, CanRenderOverlappingMultiContourPath) {
402 Canvas canvas;
403
404 Paint paint;
405 paint.color = Color::Red();
406
408 radii.top_left = {50, 50};
409 radii.top_right = {50, 50};
410 radii.bottom_right = {50, 50};
411 radii.bottom_left = {50, 50};
412
413 const Scalar kTriangleHeight = 100;
414 canvas.Translate(Vector2(200, 200));
415 // Form a path similar to the Material drop slider value indicator. Both
416 // shapes should render identically side-by-side.
417 {
418 auto path =
420 .MoveTo({0, kTriangleHeight})
421 .LineTo({-kTriangleHeight / 2.0f, 0})
422 .LineTo({kTriangleHeight / 2.0f, 0})
423 .Close()
425 Rect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
426 kTriangleHeight, kTriangleHeight),
427 radii)
428 .TakePath();
429
430 canvas.DrawPath(path, paint);
431 }
432 canvas.Translate(Vector2(100, 0));
433 {
434 auto path =
436 .MoveTo({0, kTriangleHeight})
437 .LineTo({-kTriangleHeight / 2.0f, 0})
438 .LineTo({0, -10})
439 .LineTo({kTriangleHeight / 2.0f, 0})
440 .Close()
442 Rect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
443 kTriangleHeight, kTriangleHeight),
444 radii)
445 .TakePath();
446
447 canvas.DrawPath(path, paint);
448 }
449
450 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
451}
452
453} // namespace testing
454} // namespace impeller
SkColor4f color
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition aaclip.cpp:27
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
void DrawLine(const Point &p0, const Point &p1, const Paint &paint)
Definition canvas.cc:483
const Matrix & GetCurrentTransform() const
Definition canvas.cc:296
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition canvas.cc:587
void Concat(const Matrix &transform)
Definition canvas.cc:280
void DrawImage(const std::shared_ptr< Image > &image, Point offset, const Paint &paint, SamplerDescriptor sampler={})
Definition canvas.cc:750
void DrawPaint(const Paint &paint)
Definition canvas.cc:350
void Scale(const Vector2 &scale)
Definition canvas.cc:313
Picture EndRecordingAsPicture()
Definition canvas.cc:802
void DrawPath(const Path &path, const Paint &paint)
Definition canvas.cc:341
void Translate(const Vector3 &offset)
Definition canvas.cc:309
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition canvas.cc:564
static std::shared_ptr< ColorFilter > MakeBlend(BlendMode blend_mode, Color color)
static ColorSource MakeRadialGradient(Point center, Scalar radius, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
static ColorSource MakeImage(std::shared_ptr< Texture > texture, Entity::TileMode x_tile_mode, Entity::TileMode y_tile_mode, SamplerDescriptor sampler_descriptor, Matrix effect_transform)
static ColorSource MakeSweepGradient(Point center, Degrees start_angle, Degrees end_angle, std::vector< Color > colors, std::vector< Scalar > stops, Entity::TileMode tile_mode, Matrix effect_transform)
@ kNormal
Blurred inside and outside.
PathBuilder & AddRect(Rect rect)
Path TakePath(FillType fill=FillType::kNonZero)
PathBuilder & MoveTo(Point point, bool relative=false)
PathBuilder & AddCircle(const Point &center, Scalar radius)
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition path.h:51
const Paint & paint
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlTexture * texture
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
Point Vector2
Definition point.h:320
float Scalar
Definition scalar.h:18
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition widgets.cc:50
TPoint< Scalar > Point
Definition point.h:316
constexpr float kPiOver2
Definition constants.h:32
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
void Close(PathBuilder *builder)
const Scalar scale
static constexpr Color Fuchsia()
Definition color.h:458
static constexpr Color Black()
Definition color.h:258
static constexpr Color White()
Definition color.h:256
constexpr Color WithAlpha(Scalar new_alpha) const
Definition color.h:270
static constexpr Color Red()
Definition color.h:264
static constexpr Color AliceBlue()
Definition color.h:274
static constexpr Color Blue()
Definition color.h:268
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
Matrix Invert() const
Definition matrix.cc:97
static Matrix MakeRotationZ(Radians r)
Definition matrix.h:213
FilterContents::BlurStyle style
Definition paint.h:40
Scalar stroke_width
Definition paint.h:59
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition sigma.h:32
constexpr Type GetDistance(const TPoint &p) const
Definition point.h:200
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:146