| Flutter Impeller |

aiks_dl_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 "display_list/dl_sampling_options.h"
6 #include "display_list/dl_tile_mode.h"
7 #include "display_list/effects/dl_color_source.h"
8 #include "display_list/effects/dl_mask_filter.h"
9 #include "flutter/impeller/display_list/aiks_unittests.h"
10
11 #include "flutter/display_list/dl_blend_mode.h"
12 #include "flutter/display_list/dl_builder.h"
13 #include "flutter/display_list/dl_color.h"
14 #include "flutter/display_list/dl_paint.h"
15 #include "flutter/display_list/effects/dl_color_filter.h"
16 #include "flutter/testing/testing.h"
17 #include "impeller/display_list/dl_image_impeller.h"
18 #include "impeller/playground/widgets.h"
19
20 namespace impeller {
21 namespace testing {
22
23 using namespace flutter;
24
25 TEST_P(AiksTest, RotateColorFilteredPath) {
26 DisplayListBuilder builder;
27 builder.Transform(DlMatrix::MakeTranslation(DlPoint(300, 300)) *
28 DlMatrix::MakeRotationZ(DlDegrees(90)));
29
30 DlPathBuilder arrow_stem;
31 DlPathBuilder arrow_head;
32
33 arrow_stem.MoveTo(DlPoint(120, 190)).LineTo(DlPoint(120, 50));
34 arrow_head.MoveTo(DlPoint(50, 120))
35 .LineTo(DlPoint(120, 190))
36 .LineTo(DlPoint(190, 120));
37
38 auto filter =
39 DlColorFilter::MakeBlend(DlColor::kAliceBlue(), DlBlendMode::kSrcIn);
40
41 DlPaint paint;
42 paint.setStrokeWidth(15.0);
43 paint.setStrokeCap(DlStrokeCap::kRound);
44 paint.setStrokeJoin(DlStrokeJoin::kRound);
45 paint.setDrawStyle(DlDrawStyle::kStroke);
46 paint.setColorFilter(filter);
47 paint.setColor(DlColor::kBlack());
48
49 builder.DrawPath(DlPath(arrow_stem), paint);
50 builder.DrawPath(DlPath(arrow_head), paint);
51
52 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
53 }
54
55 TEST_P(AiksTest, CanRenderStrokes) {
56 DisplayListBuilder builder;
57 DlPaint paint;
58 paint.setColor(DlColor::kRed());
59 paint.setStrokeWidth(20);
60 paint.setDrawStyle(DlDrawStyle::kStroke);
61
62 builder.DrawPath(DlPath::MakeLine(DlPoint(200, 100), DlPoint(800, 100)),
63 paint);
64
65 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
66 }
67
68 TEST_P(AiksTest, CanRenderCurvedStrokes) {
69 DisplayListBuilder builder;
70 DlPaint paint;
71 paint.setColor(DlColor::kRed());
72 paint.setStrokeWidth(25);
73 paint.setDrawStyle(DlDrawStyle::kStroke);
74
75 builder.DrawPath(DlPath::MakeCircle(DlPoint(500, 500), 250), paint);
76
77 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
78 }
79
80 TEST_P(AiksTest, CanRenderThickCurvedStrokes) {
81 DisplayListBuilder builder;
82 DlPaint paint;
83 paint.setColor(DlColor::kRed());
84 paint.setStrokeWidth(100);
85 paint.setDrawStyle(DlDrawStyle::kStroke);
86
87 builder.DrawPath(DlPath::MakeCircle(DlPoint(100, 100), 50), paint);
88
89 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
90 }
91
92 TEST_P(AiksTest, CanRenderThinCurvedStrokes) {
93 DisplayListBuilder builder;
94 DlPaint paint;
95 paint.setColor(DlColor::kRed());
96 paint.setStrokeWidth(0.01);
97 paint.setDrawStyle(DlDrawStyle::kStroke);
98
99 builder.DrawPath(DlPath::MakeCircle(DlPoint(100, 100), 50), paint);
100
101 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
102 }
103
104 TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) {
105 DisplayListBuilder builder;
106 DlPaint paint;
107 paint.setColor(DlColor::kRed());
108 paint.setStrokeWidth(200);
109 paint.setDrawStyle(DlDrawStyle::kStroke);
110
111 DlPath path = DlPath::MakeArc(DlRect::MakeXYWH(100, 100, 200, 200), //
112 DlDegrees(0), DlDegrees(90), false);
113
114 builder.DrawPath(path, paint);
115
116 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
117 }
118
119 TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) {
120 DisplayListBuilder builder;
121
122 DlPaint paint;
123 paint.setColor(DlColor::kRed());
124 paint.setStrokeWidth(20);
125 paint.setDrawStyle(DlDrawStyle::kStroke);
126
127 DlPathBuilder path_builder;
128 path_builder.MoveTo(DlPoint(0, 200));
129 path_builder.CubicCurveTo(DlPoint(50, 400), DlPoint(350, 0),
130 DlPoint(400, 200));
131
132 builder.DrawPath(DlPath(path_builder), paint);
133 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
134 }
135
136 TEST_P(AiksTest, CanRenderQuadraticStrokeWithInstantTurn) {
137 DisplayListBuilder builder;
138
139 DlPaint paint;
140 paint.setColor(DlColor::kRed());
141 paint.setStrokeWidth(50);
142 paint.setDrawStyle(DlDrawStyle::kStroke);
143 paint.setStrokeCap(DlStrokeCap::kRound);
144
145 // Should draw a diagonal pill shape. If flat on either end, the stroke is
146 // rendering wrong.
147 DlPathBuilder path_builder;
148 path_builder.MoveTo(DlPoint(250, 250));
149 path_builder.QuadraticCurveTo(DlPoint(100, 100), DlPoint(250, 250));
150
151 builder.DrawPath(DlPath(path_builder), paint);
152
153 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
154 }
155
156 TEST_P(AiksTest, CanRenderFilledConicPaths) {
157 DisplayListBuilder builder;
158 builder.Scale(GetContentScale().x, GetContentScale().y);
159
160 DlPaint paint;
161 paint.setColor(DlColor::kRed());
162 paint.setDrawStyle(DlDrawStyle::kFill);
163
164 DlPaint reference_paint;
165 reference_paint.setColor(DlColor::kGreen());
166 reference_paint.setDrawStyle(DlDrawStyle::kFill);
167
168 DlPathBuilder path_builder;
169 DlPathBuilder reference_builder;
170
171 // weight of 1.0 is just a quadratic bezier
172 path_builder.MoveTo(DlPoint(100, 100));
173 path_builder.ConicCurveTo(DlPoint(150, 150), DlPoint(200, 100), 1.0f);
174 reference_builder.MoveTo(DlPoint(300, 100));
175 reference_builder.QuadraticCurveTo(DlPoint(350, 150), DlPoint(400, 100));
176
177 // weight of sqrt(2)/2 is a circular section
178 path_builder.MoveTo(DlPoint(100, 200));
179 path_builder.ConicCurveTo(DlPoint(150, 250), DlPoint(200, 200), kSqrt2Over2);
180 reference_builder.MoveTo(DlPoint(300, 200));
181 auto magic = DlPathBuilder::kArcApproximationMagic;
182 reference_builder.CubicCurveTo(DlPoint(300, 200) + DlPoint(50, 50) * magic,
183 DlPoint(400, 200) + DlPoint(-50, 50) * magic,
184 DlPoint(400, 200));
185
186 // weight of .01 is nearly a straight line
187 path_builder.MoveTo(DlPoint(100, 300));
188 path_builder.ConicCurveTo(DlPoint(150, 350), DlPoint(200, 300), 0.01f);
189 reference_builder.MoveTo(DlPoint(300, 300));
190 reference_builder.LineTo(DlPoint(350, 300.5));
191 reference_builder.LineTo(DlPoint(400, 300));
192
193 // weight of 100.0 is nearly a triangle
194 path_builder.MoveTo(DlPoint(100, 400));
195 path_builder.ConicCurveTo(DlPoint(150, 450), DlPoint(200, 400), 100.0f);
196 reference_builder.MoveTo(DlPoint(300, 400));
197 reference_builder.LineTo(DlPoint(350, 450));
198 reference_builder.LineTo(DlPoint(400, 400));
199
200 builder.DrawPath(DlPath(path_builder), paint);
201 builder.DrawPath(DlPath(reference_builder), reference_paint);
202
203 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
204 }
205
206 TEST_P(AiksTest, CanRenderStrokedConicPaths) {
207 DisplayListBuilder builder;
208 builder.Scale(GetContentScale().x, GetContentScale().y);
209
210 DlPaint paint;
211 paint.setColor(DlColor::kRed());
212 paint.setStrokeWidth(10);
213 paint.setDrawStyle(DlDrawStyle::kStroke);
214 paint.setStrokeCap(DlStrokeCap::kRound);
215 paint.setStrokeJoin(DlStrokeJoin::kRound);
216
217 DlPaint reference_paint;
218 reference_paint.setColor(DlColor::kGreen());
219 reference_paint.setStrokeWidth(10);
220 reference_paint.setDrawStyle(DlDrawStyle::kStroke);
221 reference_paint.setStrokeCap(DlStrokeCap::kRound);
222 reference_paint.setStrokeJoin(DlStrokeJoin::kRound);
223
224 DlPathBuilder path_builder;
225 DlPathBuilder reference_builder;
226
227 // weight of 1.0 is just a quadratic bezier
228 path_builder.MoveTo(DlPoint(100, 100));
229 path_builder.ConicCurveTo(DlPoint(150, 150), DlPoint(200, 100), 1.0f);
230 reference_builder.MoveTo(DlPoint(300, 100));
231 reference_builder.QuadraticCurveTo(DlPoint(350, 150), DlPoint(400, 100));
232
233 // weight of sqrt(2)/2 is a circular section
234 path_builder.MoveTo(DlPoint(100, 200));
235 path_builder.ConicCurveTo(DlPoint(150, 250), DlPoint(200, 200), kSqrt2Over2);
236 reference_builder.MoveTo(DlPoint(300, 200));
237 auto magic = DlPathBuilder::kArcApproximationMagic;
238 reference_builder.CubicCurveTo(DlPoint(300, 200) + DlPoint(50, 50) * magic,
239 DlPoint(400, 200) + DlPoint(-50, 50) * magic,
240 DlPoint(400, 200));
241
242 // weight of .0 is a straight line
243 path_builder.MoveTo(DlPoint(100, 300));
244 path_builder.ConicCurveTo(DlPoint(150, 350), DlPoint(200, 300), 0.0f);
245 reference_builder.MoveTo(DlPoint(300, 300));
246 reference_builder.LineTo(DlPoint(400, 300));
247
248 // weight of 100.0 is nearly a triangle
249 path_builder.MoveTo(DlPoint(100, 400));
250 path_builder.ConicCurveTo(DlPoint(150, 450), DlPoint(200, 400), 100.0f);
251 reference_builder.MoveTo(DlPoint(300, 400));
252 reference_builder.LineTo(DlPoint(350, 450));
253 reference_builder.LineTo(DlPoint(400, 400));
254
255 builder.DrawPath(DlPath(path_builder), paint);
256 builder.DrawPath(DlPath(reference_builder), reference_paint);
257
258 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
259 }
260
261 TEST_P(AiksTest, HairlinePath) {
262 Scalar scale = 1.f;
263 Scalar rotation = 0.f;
264 Scalar offset = 0.f;
265 auto callback = [&]() -> sk_sp<DisplayList> {
266 if (AiksTest::ImGuiBegin("Controls", nullptr,
267 ImGuiWindowFlags_AlwaysAutoResize)) {
268 ImGui::SliderFloat("Scale", &scale, 0, 6);
269 ImGui::SliderFloat("Rotate", &rotation, 0, 90);
270 ImGui::SliderFloat("Offset", &offset, 0, 2);
271 ImGui::End();
272 }
273
274 DisplayListBuilder builder;
275 builder.Scale(GetContentScale().x, GetContentScale().y);
276 builder.DrawPaint(DlPaint(DlColor(0xff111111)));
277
278 DlPaint paint;
279 paint.setStrokeWidth(0.f);
280 paint.setColor(DlColor::kWhite());
281 paint.setStrokeCap(DlStrokeCap::kRound);
282 paint.setStrokeJoin(DlStrokeJoin::kRound);
283 paint.setDrawStyle(DlDrawStyle::kStroke);
284
285 builder.Translate(512, 384);
286 builder.Scale(scale, scale);
287 builder.Rotate(rotation);
288 builder.Translate(-512, -384 + offset);
289
290 for (int i = 0; i < 5; ++i) {
291 Scalar yoffset = i * 25.25f + 300.f;
292 DlPathBuilder path_builder;
293
294 path_builder.MoveTo(DlPoint(100, yoffset));
295 path_builder.LineTo(DlPoint(924, yoffset));
296 builder.DrawPath(DlPath(path_builder), paint);
297 }
298
299 return builder.Build();
300 };
301
302 ASSERT_TRUE(OpenPlaygroundHere(callback));
303 }
304
305 TEST_P(AiksTest, HairlineDrawLine) {
306 Scalar scale = 1.f;
307 Scalar rotation = 0.f;
308 Scalar offset = 0.f;
309 auto callback = [&]() -> sk_sp<DisplayList> {
310 if (AiksTest::ImGuiBegin("Controls", nullptr,
311 ImGuiWindowFlags_AlwaysAutoResize)) {
312 ImGui::SliderFloat("Scale", &scale, 0, 6);
313 ImGui::SliderFloat("Rotate", &rotation, 0, 90);
314 ImGui::SliderFloat("Offset", &offset, 0, 2);
315 ImGui::End();
316 }
317
318 DisplayListBuilder builder;
319 builder.Scale(GetContentScale().x, GetContentScale().y);
320 builder.DrawPaint(DlPaint(DlColor(0xff111111)));
321
322 DlPaint paint;
323 paint.setStrokeWidth(0.f);
324 paint.setColor(DlColor::kWhite());
325
326 builder.Translate(512, 384);
327 builder.Scale(scale, scale);
328 builder.Rotate(rotation);
329 builder.Translate(-512, -384 + offset);
330
331 for (int i = 0; i < 5; ++i) {
332 Scalar yoffset = i * 25.25f + 300.f;
333
334 builder.DrawLine(DlPoint(100, yoffset), DlPoint(924, yoffset), paint);
335 }
336
337 return builder.Build();
338 };
339
340 ASSERT_TRUE(OpenPlaygroundHere(callback));
341 }
342
343 TEST_P(AiksTest, CanRenderTightConicPath) {
344 DisplayListBuilder builder;
345 builder.Scale(GetContentScale().x, GetContentScale().y);
346
347 DlPaint paint;
348 paint.setColor(DlColor::kRed());
349 paint.setDrawStyle(DlDrawStyle::kFill);
350
351 DlPaint reference_paint;
352 reference_paint.setColor(DlColor::kGreen());
353 reference_paint.setDrawStyle(DlDrawStyle::kFill);
354
355 DlPathBuilder path_builder;
356
357 path_builder.MoveTo(DlPoint(100, 100));
358 path_builder.ConicCurveTo(DlPoint(150, 450), DlPoint(200, 100), 5.0f);
359
360 DlPathBuilder reference_builder;
361 ConicPathComponent component(DlPoint(300, 100), //
362 DlPoint(350, 450), //
363 DlPoint(400, 100), //
364 5.0f);
365 reference_builder.MoveTo(component.p1);
366 constexpr int N = 100;
367 for (int i = 1; i < N; i++) {
368 reference_builder.LineTo(component.Solve(static_cast<Scalar>(i) / N));
369 }
370 reference_builder.LineTo(component.p2);
371
372 DlPaint line_paint;
373 line_paint.setColor(DlColor::kYellow());
374 line_paint.setDrawStyle(DlDrawStyle::kStroke);
375 line_paint.setStrokeWidth(1.0f);
376
377 // Draw some lines to provide a spacial reference for the curvature of
378 // the tips of the direct rendering and the manually tessellated versions.
379 builder.DrawLine(DlPoint(145, 100), DlPoint(145, 450), line_paint);
380 builder.DrawLine(DlPoint(155, 100), DlPoint(155, 450), line_paint);
381 builder.DrawLine(DlPoint(345, 100), DlPoint(345, 450), line_paint);
382 builder.DrawLine(DlPoint(355, 100), DlPoint(355, 450), line_paint);
383 builder.DrawLine(DlPoint(100, 392.5f), DlPoint(400, 392.5f), line_paint);
384
385 // Draw the two paths (direct and manually tessellated) on top of the lines.
386 builder.DrawPath(DlPath(path_builder), paint);
387 builder.DrawPath(DlPath(reference_builder), reference_paint);
388
389 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
390 }
391
392 TEST_P(AiksTest, CanRenderDifferencePaths) {
393 DisplayListBuilder builder;
394
395 DlPaint paint;
396 paint.setColor(DlColor::kRed());
397
398 RoundingRadii radii = {
399 .top_left = {50, 25},
400 .top_right = {25, 50},
401 .bottom_left = {25, 50},
402 .bottom_right = {50, 25},
403 };
404 PathBuilder path_builder;
405 DlRoundRect rrect =
406 DlRoundRect::MakeRectRadii(DlRect::MakeXYWH(100, 100, 200, 200), radii);
407 // We use the factory method to convert the rrect and circle to a path so
408 // that they use the legacy conics for legacy golden output.
409 path_builder.AddPath(DlPath::MakeRoundRect(rrect).GetPath());
410 path_builder.AddPath(DlPath::MakeCircle(DlPoint(200, 200), 50).GetPath());
411 DlPath path(path_builder, DlPathFillType::kOdd);
412
413 builder.DrawImage(
414 DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")),
415 DlPoint{10, 10}, {});
416 builder.DrawPath(path, paint);
417
418 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
419 }
420
421 // Regression test for https://github.com/flutter/flutter/issues/134816.
422 //
423 // It should be possible to draw 3 lines, and not have an implicit close path.
424 TEST_P(AiksTest, CanDrawAnOpenPath) {
425 DisplayListBuilder builder;
426
427 // Starting at (50, 50), draw lines from:
428 // 1. (50, height)
429 // 2. (width, height)
430 // 3. (width, 50)
431 DlPathBuilder path_builder;
432 path_builder.MoveTo(DlPoint(50, 50));
433 path_builder.LineTo(DlPoint(50, 100));
434 path_builder.LineTo(DlPoint(100, 100));
435 path_builder.LineTo(DlPoint(100, 50));
436
437 DlPaint paint;
438 paint.setColor(DlColor::kRed());
439 paint.setDrawStyle(DlDrawStyle::kStroke);
440 paint.setStrokeWidth(10);
441
442 builder.DrawPath(DlPath(path_builder), paint);
443
444 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
445 }
446
447 TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) {
448 DisplayListBuilder builder;
449
450 // Draw a stroked path that is explicitly closed to verify
451 // It doesn't become a rectangle.
452 DlPathBuilder path_builder;
453 path_builder.MoveTo(DlPoint(50, 50));
454 path_builder.LineTo(DlPoint(520, 120));
455 path_builder.LineTo(DlPoint(300, 310));
456 path_builder.LineTo(DlPoint(100, 50));
457 path_builder.Close();
458
459 DlPaint paint;
460 paint.setColor(DlColor::kRed());
461 paint.setDrawStyle(DlDrawStyle::kStroke);
462 paint.setStrokeWidth(10);
463
464 builder.DrawPath(DlPath(path_builder), paint);
465
466 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
467 }
468
469 TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
470 // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
471 auto callback = [&]() -> sk_sp<DisplayList> {
472 static Color color = Color::Black().WithAlpha(0.5);
473 static float scale = 3;
474 static bool add_circle_clip = true;
475
476 if (AiksTest::ImGuiBegin("Controls", nullptr,
477 ImGuiWindowFlags_AlwaysAutoResize)) {
478 ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
479 ImGui::SliderFloat("Scale", &scale, 0, 6);
480 ImGui::Checkbox("Circle clip", &add_circle_clip);
481 ImGui::End();
482 }
483
484 DisplayListBuilder builder;
485 builder.Scale(GetContentScale().x, GetContentScale().y);
486 DlPaint paint;
487
488 paint.setColor(DlColor::kWhite());
489 builder.DrawPaint(paint);
490
491 paint.setColor(
492 DlColor::ARGB(color.alpha, color.red, color.green, color.blue));
493 paint.setDrawStyle(DlDrawStyle::kStroke);
494 paint.setStrokeWidth(10);
495
496 DlPathBuilder path_builder;
497 path_builder.MoveTo(DlPoint(20, 20));
498 path_builder.QuadraticCurveTo(DlPoint(60, 20), DlPoint(60, 60));
499 path_builder.Close();
500 path_builder.MoveTo(DlPoint(60, 20));
501 path_builder.QuadraticCurveTo(DlPoint(60, 60), DlPoint(20, 60));
502 DlPath path(path_builder);
503
504 builder.Scale(scale, scale);
505
506 if (add_circle_clip) {
507 static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
508 Color::Red());
509 static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
510 Color::Red());
511 auto [handle_a, handle_b] =
512 DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
513
514 Matrix screen_to_canvas = builder.GetMatrix();
515 if (!screen_to_canvas.IsInvertible()) {
516 return nullptr;
517 }
518 screen_to_canvas = screen_to_canvas.Invert();
519
520 Point point_a = screen_to_canvas * handle_a;
521 Point point_b = screen_to_canvas * handle_b;
522
523 Point middle = point_a + point_b;
524 middle *= GetContentScale().x / 2;
525
526 auto radius = point_a.GetDistance(middle);
527
528 builder.ClipPath(DlPath::MakeCircle(middle, radius));
529 }
530
531 for (auto join :
532 {DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) {
533 paint.setStrokeJoin(join);
534 for (auto cap :
535 {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
536 paint.setStrokeCap(cap);
537 builder.DrawPath(path, paint);
538 builder.Translate(80, 0);
539 }
540 builder.Translate(-240, 60);
541 }
542
543 return builder.Build();
544 };
545
546 ASSERT_TRUE(OpenPlaygroundHere(callback));
547 }
548
549 TEST_P(AiksTest, DrawLinesRenderCorrectly) {
550 DisplayListBuilder builder;
551 builder.Scale(GetContentScale().x, GetContentScale().y);
552
553 DlPaint paint;
554 paint.setColor(DlColor::kBlue());
555 paint.setStrokeWidth(10);
556
557 auto draw = [&builder](DlPaint& paint) {
558 for (auto cap :
559 {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
560 paint.setStrokeCap(cap);
561 DlPoint origin = {100, 100};
562 builder.DrawLine(DlPoint(150, 100), DlPoint(250, 100), paint);
563 for (int d = 15; d < 90; d += 15) {
564 Matrix m = Matrix::MakeRotationZ(Degrees(d));
565 Point origin = {100, 100};
566 Point p0 = {50, 0};
567 Point p1 = {150, 0};
568 auto a = origin + m * p0;
569 auto b = origin + m * p1;
570
571 builder.DrawLine(a, b, paint);
572 }
573 builder.DrawLine(DlPoint(100, 150), DlPoint(100, 250), paint);
574 builder.DrawCircle(origin, 35, paint);
575
576 builder.DrawLine(DlPoint(250, 250), DlPoint(250, 250), paint);
577
578 builder.Translate(250, 0);
579 }
580 builder.Translate(-750, 250);
581 };
582
583 std::vector<DlColor> colors = {
584 DlColor::ARGB(1, 0x1f / 255.0, 0.0, 0x5c / 255.0),
585 DlColor::ARGB(1, 0x5b / 255.0, 0.0, 0x60 / 255.0),
586 DlColor::ARGB(1, 0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0),
587 DlColor::ARGB(1, 0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0),
588 DlColor::ARGB(1, 0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0),
589 DlColor::ARGB(1, 0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0),
590 DlColor::ARGB(1, 0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0)};
591 std::vector<Scalar> stops = {
592 0.0,
593 (1.0 / 6.0) * 1,
594 (1.0 / 6.0) * 2,
595 (1.0 / 6.0) * 3,
596 (1.0 / 6.0) * 4,
597 (1.0 / 6.0) * 5,
598 1.0,
599 };
600
601 auto texture = DlImageImpeller::Make(
602 CreateTextureForFixture("airplane.jpg",
603 /*enable_mipmapping=*/true));
604
605 draw(paint);
606
607 paint.setColorSource(DlColorSource::MakeRadial({100, 100}, 200, stops.size(),
608 colors.data(), stops.data(),
609 DlTileMode::kMirror));
610 draw(paint);
611
612 DlMatrix matrix = DlMatrix::MakeTranslation({-150, 75});
613 paint.setColorSource(DlColorSource::MakeImage(
614 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
615 DlImageSampling::kMipmapLinear, &matrix));
616 draw(paint);
617
618 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
619 }
620
621 // The goal of this test is to show that scaling the lines doesn't also scale
622 // the antialiasing. The amount of blurring should be the same for both
623 // horizontal lines.
624 TEST_P(AiksTest, ScaleExperimentAntialiasLines) {
625 Scalar scale = 5.0;
626 Scalar line_width = 10.f;
627 auto callback = [&]() -> sk_sp<DisplayList> {
628 if (AiksTest::ImGuiBegin("Controls", nullptr,
629 ImGuiWindowFlags_AlwaysAutoResize)) {
630 ImGui::SliderFloat("Scale", &scale, 0.001, 5);
631 ImGui::SliderFloat("Width", &line_width, 1, 20);
632
633 ImGui::End();
634 }
635 DisplayListBuilder builder;
636 builder.Scale(GetContentScale().x, GetContentScale().y);
637
638 builder.DrawPaint(DlPaint(DlColor(0xff111111)));
639
640 {
641 DlPaint paint;
642 paint.setColor(DlColor::kGreenYellow());
643 paint.setStrokeWidth(line_width);
644
645 builder.DrawLine(DlPoint(100, 100), DlPoint(350, 100), paint);
646 builder.DrawLine(DlPoint(100, 100), DlPoint(350, 150), paint);
647
648 builder.Save();
649 builder.Translate(100, 300);
650 builder.Scale(scale, scale);
651 builder.Translate(-100, -300);
652 builder.DrawLine(DlPoint(100, 300), DlPoint(350, 300), paint);
653 builder.DrawLine(DlPoint(100, 300), DlPoint(350, 450), paint);
654 builder.Restore();
655 }
656
657 {
658 DlPaint paint;
659 paint.setColor(DlColor::kGreenYellow());
660 paint.setStrokeWidth(2.0);
661
662 builder.Save();
663 builder.Translate(100, 500);
664 builder.Scale(0.2, 0.2);
665 builder.Translate(-100, -500);
666 builder.DrawLine(DlPoint(100, 500), DlPoint(350, 500), paint);
667 builder.DrawLine(DlPoint(100, 500), DlPoint(350, 650), paint);
668 builder.Restore();
669 }
670
671 return builder.Build();
672 };
673 ASSERT_TRUE(OpenPlaygroundHere(callback));
674 }
675
676 TEST_P(AiksTest, HexagonExperimentAntialiasLines) {
677 float scale = 5.0f;
678 float line_width = 10.f;
679 float rotation = 0.f;
680
681 auto callback = [&]() -> sk_sp<DisplayList> {
682 if (AiksTest::ImGuiBegin("Controls", nullptr,
683 ImGuiWindowFlags_AlwaysAutoResize)) {
684 // Use ImGui::SliderFloat for consistency
685 ImGui::SliderFloat("Scale", &scale, 0.001f, 5.0f);
686 ImGui::SliderFloat("Width", &line_width, 1.0f, 20.0f);
687 ImGui::SliderFloat("Rotation", &rotation, 0.0f, 180.0f);
688
689 ImGui::End();
690 }
691 DisplayListBuilder builder;
692 builder.Scale(static_cast<float>(GetContentScale().x),
693 static_cast<float>(GetContentScale().y));
694
695 builder.DrawPaint(DlPaint(DlColor(0xff111111))); // Background
696
697 {
698 DlPaint hex_paint;
699 hex_paint.setColor(
700 DlColor::kGreen()); // Changed color to Red for visibility
701 hex_paint.setStrokeWidth(line_width); // Use the interactive width
702
703 float cx = 512.0f; // Center X
704 float cy = 384.0f; // Center Y
705 float r = 80.0f; // Radius (distance from center to vertex)
706
707 float r_sin60 = r * std::sqrt(3.0f) / 2.0f;
708 float r_cos60 = r / 2.0f;
709
710 DlPoint v0 = DlPoint(cx + r, cy); // Right vertex
711 DlPoint v1 = DlPoint(cx + r_cos60, cy - r_sin60); // Top-right vertex
712 DlPoint v2 = DlPoint(
713 cx - r_cos60,
714 cy - r_sin60); // Top-left vertex (v1-v2 is top horizontal side)
715 DlPoint v3 = DlPoint(cx - r, cy); // Left vertex
716 DlPoint v4 = DlPoint(cx - r_cos60, cy + r_sin60); // Bottom-left vertex
717 DlPoint v5 =
718 DlPoint(cx + r_cos60, cy + r_sin60); // Bottom-right vertex (v4-v5 is
719 // bottom horizontal side)
720
721 builder.Translate(cx, cy);
722 builder.Scale(scale, scale);
723 builder.Rotate(rotation);
724 builder.Translate(-cx, -cy);
725
726 builder.DrawLine(v0, v1, hex_paint);
727 builder.DrawLine(v1, v2, hex_paint); // Top side
728 builder.DrawLine(v2, v3, hex_paint);
729 builder.DrawLine(v3, v4, hex_paint);
730 builder.DrawLine(v4, v5, hex_paint); // Bottom side
731 builder.DrawLine(v5, v0, hex_paint); // Close the hexagon
732 }
733
734 return builder.Build();
735 };
736 ASSERT_TRUE(OpenPlaygroundHere(callback));
737 }
738
739 TEST_P(AiksTest, SimpleExperimentAntialiasLines) {
740 DisplayListBuilder builder;
741 builder.Scale(GetContentScale().x, GetContentScale().y);
742
743 builder.DrawPaint(DlPaint(DlColor(0xff111111)));
744
745 DlPaint paint;
746 paint.setColor(DlColor::kGreenYellow());
747 paint.setStrokeWidth(10);
748
749 auto draw = [&builder](DlPaint& paint) {
750 for (auto cap :
751 {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
752 paint.setStrokeCap(cap);
753 DlPoint origin = {100, 100};
754 builder.DrawLine(DlPoint(150, 100), DlPoint(250, 100), paint);
755 for (int d = 15; d < 90; d += 15) {
756 Matrix m = Matrix::MakeRotationZ(Degrees(d));
757 Point origin = {100, 100};
758 Point p0 = {50, 0};
759 Point p1 = {150, 0};
760 auto a = origin + m * p0;
761 auto b = origin + m * p1;
762
763 builder.DrawLine(a, b, paint);
764 }
765 builder.DrawLine(DlPoint(100, 150), DlPoint(100, 250), paint);
766 builder.DrawCircle(origin, 35, paint);
767
768 builder.DrawLine(DlPoint(250, 250), DlPoint(250, 250), paint);
769
770 builder.Translate(250, 0);
771 }
772 builder.Translate(-750, 250);
773 };
774
775 draw(paint);
776
777 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
778 }
779
780 TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
781 DisplayListBuilder builder;
782 DlPaint paint;
783 paint.setColor(DlColor::kRed());
784 paint.setDrawStyle(DlDrawStyle::kStroke);
785 paint.setStrokeWidth(10);
786
787 builder.Translate(100, 100);
788 builder.DrawPath(DlPath::MakeRect(DlRect::MakeSize(DlSize(100, 100))), paint);
789
790 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
791 }
792
793 TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) {
794 DisplayListBuilder builder;
795 DlPaint paint;
796 paint.setColor(DlColor::kRed());
797 paint.setDrawStyle(DlDrawStyle::kStroke);
798 paint.setStrokeWidth(10);
799 paint.setStrokeJoin(DlStrokeJoin::kBevel);
800
801 builder.Translate(100, 100);
802 builder.DrawPath(DlPath::MakeRect(DlRect::MakeSize(DlSize(100, 100))), paint);
803
804 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
805 }
806
807 TEST_P(AiksTest, CanDrawMultiContourConvexPath) {
808 DlPathBuilder path_builder;
809 for (auto i = 0; i < 10; i++) {
810 if (i % 2 == 0) {
811 // We use the factory method to convert the circle to a path so that it
812 // uses the legacy conics for legacy golden output.
813 DlPath circle =
814 DlPath::MakeCircle(DlPoint(100 + 50 * i, 100 + 50 * i), 100);
815 path_builder.AddPath(circle.GetPath());
816 path_builder.Close();
817 } else {
818 path_builder.MoveTo(DlPoint(100.f + 50.f * i - 100, 100.f + 50.f * i));
819 path_builder.LineTo(DlPoint(100.f + 50.f * i, 100.f + 50.f * i - 100));
820 path_builder.LineTo(DlPoint(100.f + 50.f * i - 100, //
821 100.f + 50.f * i - 100));
822 path_builder.Close();
823 }
824 }
825 DlPath path(path_builder);
826
827 DisplayListBuilder builder;
828 DlPaint paint;
829 paint.setColor(DlColor::kRed().withAlpha(102));
830 builder.DrawPath(path, paint);
831
832 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
833 }
834
835 TEST_P(AiksTest, ArcWithZeroSweepAndBlur) {
836 DisplayListBuilder builder;
837 builder.Scale(GetContentScale().x, GetContentScale().y);
838
839 DlPaint paint;
840 paint.setColor(DlColor::kRed());
841
842 std::vector<DlColor> colors = {DlColor::RGBA(1.0, 0.0, 0.0, 1.0),
843 DlColor::RGBA(0.0, 0.0, 0.0, 1.0)};
844 std::vector<Scalar> stops = {0.0, 1.0};
845
846 paint.setColorSource(
847 DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(),
848 stops.data(), DlTileMode::kMirror));
849 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
850
851 DlPathBuilder path_builder;
852 path_builder.AddArc(DlRect::MakeXYWH(10, 10, 100, 100), //
853 DlDegrees(0), DlDegrees(0));
854 builder.DrawPath(DlPath(path_builder), paint);
855
856 // Check that this empty picture can be created without crashing.
857 builder.Build();
858 }
859
860 TEST_P(AiksTest, CanRenderClips) {
861 DisplayListBuilder builder;
862 DlPaint paint;
863 paint.setColor(DlColor::kFuchsia());
864
865 builder.ClipPath(DlPath::MakeRect(DlRect::MakeXYWH(0, 0, 500, 500)));
866 builder.DrawPath(DlPath::MakeCircle(DlPoint(500, 500), 250), paint);
867
868 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
869 }
870
871 TEST_P(AiksTest, FatStrokeArc) {
872 DlScalar stroke_width = 300;
873 DlScalar aspect = 1.0;
874 DlScalar start_angle = 0;
875 DlScalar end_angle = 90;
876 auto callback = [&]() -> sk_sp<DisplayList> {
877 if (AiksTest::ImGuiBegin("Controls", nullptr,
878 ImGuiWindowFlags_AlwaysAutoResize)) {
879 ImGui::SliderFloat("Stroke Width", &stroke_width, 1, 300);
880 ImGui::SliderFloat("Aspect", &aspect, 0.5, 2.0);
881 ImGui::SliderFloat("Start Angle", &start_angle, 0, 360);
882 ImGui::SliderFloat("End Angle", &end_angle, 0, 360);
883 ImGui::End();
884 }
885
886 DisplayListBuilder builder;
887 DlPaint grey_paint;
888 grey_paint.setColor(DlColor(0xff111111));
889 builder.DrawPaint(grey_paint);
890
891 DlPaint white_paint;
892 white_paint.setColor(DlColor::kWhite());
893 white_paint.setStrokeWidth(stroke_width);
894 white_paint.setDrawStyle(DlDrawStyle::kStroke);
895 DlPaint red_paint;
896 red_paint.setColor(DlColor::kRed());
897
898 Rect rect = Rect::MakeXYWH(100, 100, 100, aspect * 100);
899 builder.DrawRect(rect, red_paint);
900 builder.DrawArc(rect, start_angle, end_angle,
901 /*useCenter=*/false, white_paint);
902 DlScalar frontier = rect.GetRight() + stroke_width / 2.0;
903 builder.DrawLine(Point(frontier, 0), Point(frontier, 150), red_paint);
904
905 return builder.Build();
906 };
907 ASSERT_TRUE(OpenPlaygroundHere(callback));
908 }
909
910 TEST_P(AiksTest, CanRenderOverlappingMultiContourPath) {
911 DisplayListBuilder builder;
912
913 DlPaint paint;
914 paint.setColor(DlColor::kRed());
915
916 RoundingRadii radii = {
917 .top_left = DlSize(50, 50),
918 .top_right = DlSize(50, 50),
919 .bottom_left = DlSize(50, 50),
920 .bottom_right = DlSize(50, 50),
921 };
922
923 const Scalar kTriangleHeight = 100;
924 DlRoundRect rrect = DlRoundRect::MakeRectRadii(
925 DlRect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
926 kTriangleHeight, kTriangleHeight),
927 radii //
928 );
929 // We use the factory method to convert the rrect to a path so that it
930 // uses the legacy conics for legacy golden output.
931 DlPath rrect_path = DlPath::MakeRoundRect(rrect);
932
933 builder.Translate(200, 200);
934 // Form a path similar to the Material drop slider value indicator. Both
935 // shapes should render identically side-by-side.
936 {
937 DlPathBuilder path_builder;
938 path_builder.MoveTo(DlPoint(0, kTriangleHeight));
939 path_builder.LineTo(DlPoint(-kTriangleHeight / 2.0f, 0));
940 path_builder.LineTo(DlPoint(kTriangleHeight / 2.0f, 0));
941 path_builder.Close();
942 path_builder.AddPath(rrect_path.GetPath());
943
944 builder.DrawPath(DlPath(path_builder), paint);
945 }
946 builder.Translate(100, 0);
947
948 {
949 DlPathBuilder path_builder;
950 path_builder.MoveTo(DlPoint(0, kTriangleHeight));
951 path_builder.LineTo(DlPoint(-kTriangleHeight / 2.0f, 0));
952 path_builder.LineTo(DlPoint(0, -10));
953 path_builder.LineTo(DlPoint(kTriangleHeight / 2.0f, 0));
954 path_builder.Close();
955 path_builder.AddPath(rrect_path.GetPath());
956
957 builder.DrawPath(DlPath(path_builder), paint);
958 }
959
960 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
961 }
962
963 TEST_P(AiksTest, TwoContourPathWithSinglePointContour) {
964 DisplayListBuilder builder;
965
966 DlPaint paint;
967 paint.setColor(DlColor::kRed());
968 paint.setDrawStyle(DlDrawStyle::kStroke);
969 paint.setStrokeWidth(15.0);
970 paint.setStrokeCap(DlStrokeCap::kRound);
971
972 DlPathBuilder path_builder;
973 path_builder.MoveTo(DlPoint(100, 100));
974 path_builder.LineTo(DlPoint(150, 150));
975 path_builder.MoveTo(DlPoint(200, 200));
976 path_builder.LineTo(DlPoint(200, 200));
977
978 builder.DrawPath(DlPath(path_builder), paint);
979
980 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
981 }
982
983 } // namespace testing
984 } // namespace impeller
aiks_unittests.h
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:31
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
impeller::PathBuilder
Definition: path_builder.h:16
impeller::PathBuilder::AddPath
PathBuilder & AddPath(const Path &path)
Definition: path_builder.cc:509
x
int32_t x
Definition: dl_golden_unittests.cc:458
dl_image_impeller.h
flutter
Definition: dl_golden_blur_unittests.cc:16
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:60
impeller
Definition: allocation.cc:12
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
impeller::kSqrt2Over2
constexpr float kSqrt2Over2
Definition: constants.h:51
impeller::DlRoundRect
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::DlPoint
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
impeller::DlPath
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
impeller::DlScalar
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
stroke_width
const Scalar stroke_width
Definition: stroke_path_geometry.cc:315
scale
const Scalar scale
Definition: stroke_path_geometry.cc:319
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:322
impeller::Color
Definition: color.h:124
impeller::Color::blue
Scalar blue
Definition: color.h:138
impeller::Color::alpha
Scalar alpha
Definition: color.h:143
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:266
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:278
impeller::Color::red
Scalar red
Definition: color.h:128
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:272
impeller::Color::green
Scalar green
Definition: color.h:133
impeller::ConicPathComponent
Definition: path_component.h:226
impeller::ConicPathComponent::Solve
Point Solve(Scalar time) const
Definition: path_component.cc:327
impeller::ConicPathComponent::p1
Point p1
Definition: path_component.h:228
impeller::ConicPathComponent::p2
Point p2
Definition: path_component.h:232
impeller::Degrees
Definition: scalar.h:51
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::IsInvertible
bool IsInvertible() const
Definition: matrix.h:321
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223
impeller::PlaygroundPoint
Definition: widgets.h:17
impeller::RoundingRadii
Definition: rounding_radii.h:14
impeller::RoundingRadii::top_left
Size top_left
Definition: rounding_radii.h:15
impeller::TPoint< Scalar >
impeller::TPoint::GetDistance
constexpr Type GetDistance(const TPoint &p) const
Definition: point.h:200
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::TRect< Scalar >
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:359
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
widgets.h