| 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 #include "include/core/SkMatrix.h"
21 #include "include/core/SkPath.h"
22 #include "include/core/SkPathTypes.h"
23 #include "include/core/SkRRect.h"
24
25 namespace impeller {
26 namespace testing {
27
28 using namespace flutter;
29
30 TEST_P(AiksTest, RotateColorFilteredPath) {
31 DisplayListBuilder builder;
32 builder.Transform(SkMatrix::Translate(300, 300) * SkMatrix::RotateDeg(90));
33
34 SkPath arrow_stem;
35 SkPath arrow_head;
36
37 arrow_stem.moveTo({120, 190}).lineTo({120, 50});
38 arrow_head.moveTo({50, 120}).lineTo({120, 190}).lineTo({190, 120});
39
40 auto filter =
41 DlBlendColorFilter::Make(DlColor::kAliceBlue(), DlBlendMode::kSrcIn);
42
43 DlPaint paint;
44 paint.setStrokeWidth(15.0);
45 paint.setStrokeCap(DlStrokeCap::kRound);
46 paint.setStrokeJoin(DlStrokeJoin::kRound);
47 paint.setDrawStyle(DlDrawStyle::kStroke);
48 paint.setColorFilter(filter);
49 paint.setColor(DlColor::kBlack());
50
51 builder.DrawPath(arrow_stem, paint);
52 builder.DrawPath(arrow_head, paint);
53
54 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
55 }
56
57 TEST_P(AiksTest, CanRenderStrokes) {
58 DisplayListBuilder builder;
59 DlPaint paint;
60 paint.setColor(DlColor::kRed());
61 paint.setStrokeWidth(20);
62 paint.setDrawStyle(DlDrawStyle::kStroke);
63
64 builder.DrawPath(SkPath::Line({200, 100}, {800, 100}), paint);
65
66 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
67 }
68
69 TEST_P(AiksTest, CanRenderCurvedStrokes) {
70 DisplayListBuilder builder;
71 DlPaint paint;
72 paint.setColor(DlColor::kRed());
73 paint.setStrokeWidth(25);
74 paint.setDrawStyle(DlDrawStyle::kStroke);
75
76 builder.DrawPath(SkPath::Circle(500, 500, 250), paint);
77
78 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
79 }
80
81 TEST_P(AiksTest, CanRenderThickCurvedStrokes) {
82 DisplayListBuilder builder;
83 DlPaint paint;
84 paint.setColor(DlColor::kRed());
85 paint.setStrokeWidth(100);
86 paint.setDrawStyle(DlDrawStyle::kStroke);
87
88 builder.DrawPath(SkPath::Circle(100, 100, 50), paint);
89
90 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
91 }
92
93 TEST_P(AiksTest, CanRenderThinCurvedStrokes) {
94 DisplayListBuilder builder;
95 DlPaint paint;
96 paint.setColor(DlColor::kRed());
97 paint.setStrokeWidth(0.01);
98 paint.setDrawStyle(DlDrawStyle::kStroke);
99
100 builder.DrawPath(SkPath::Circle(100, 100, 50), paint);
101
102 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
103 }
104
105 TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) {
106 DisplayListBuilder builder;
107 DlPaint paint;
108 paint.setColor(DlColor::kRed());
109 paint.setStrokeWidth(200);
110 paint.setDrawStyle(DlDrawStyle::kStroke);
111
112 SkPath path;
113 path.arcTo(SkRect::MakeXYWH(100, 100, 200, 200), 0, 90, false);
114
115 builder.DrawPath(path, paint);
116
117 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
118 }
119
120 TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) {
121 DisplayListBuilder builder;
122
123 DlPaint paint;
124 paint.setColor(DlColor::kRed());
125 paint.setStrokeWidth(20);
126 paint.setDrawStyle(DlDrawStyle::kStroke);
127
128 SkPath path;
129 path.moveTo(0, 200);
130 path.cubicTo(50, 400, 350, 0, 400, 200);
131
132 builder.DrawPath(path, 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 SkPath path;
148 path.moveTo(250, 250);
149 path.quadTo(100, 100, 250, 250);
150
151 builder.DrawPath(path, paint);
152
153 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
154 }
155
156 TEST_P(AiksTest, CanRenderDifferencePaths) {
157 DisplayListBuilder builder;
158
159 DlPaint paint;
160 paint.setColor(DlColor::kRed());
161
162 SkPoint radii[4] = {{50, 25}, {25, 50}, {50, 25}, {25, 50}};
163 SkPath path;
164 SkRRect rrect;
165 rrect.setRectRadii(SkRect::MakeXYWH(100, 100, 200, 200), radii);
166 path.addRRect(rrect);
167 path.addCircle(200, 200, 50);
168 path.setFillType(SkPathFillType::kEvenOdd);
169
170 builder.DrawImage(
171 DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")), {10, 10},
172 {});
173 builder.DrawPath(path, paint);
174
175 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
176 }
177
178 // Regression test for https://github.com/flutter/flutter/issues/134816.
179 //
180 // It should be possible to draw 3 lines, and not have an implicit close path.
181 TEST_P(AiksTest, CanDrawAnOpenPath) {
182 DisplayListBuilder builder;
183
184 // Starting at (50, 50), draw lines from:
185 // 1. (50, height)
186 // 2. (width, height)
187 // 3. (width, 50)
188 SkPath path;
189 path.moveTo(50, 50);
190 path.lineTo(50, 100);
191 path.lineTo(100, 100);
192 path.lineTo(100, 50);
193
194 DlPaint paint;
195 paint.setColor(DlColor::kRed());
196 paint.setDrawStyle(DlDrawStyle::kStroke);
197 paint.setStrokeWidth(10);
198
199 builder.DrawPath(path, paint);
200
201 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
202 }
203
204 TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) {
205 DisplayListBuilder builder;
206
207 // Draw a stroked path that is explicitly closed to verify
208 // It doesn't become a rectangle.
209 SkPath path;
210 // PathBuilder builder;
211 path.moveTo(50, 50);
212 path.lineTo(520, 120);
213 path.lineTo(300, 310);
214 path.lineTo(100, 50);
215 path.close();
216
217 DlPaint paint;
218 paint.setColor(DlColor::kRed());
219 paint.setDrawStyle(DlDrawStyle::kStroke);
220 paint.setStrokeWidth(10);
221
222 builder.DrawPath(path, paint);
223
224 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
225 }
226
227 TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
228 // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
229 auto callback = [&]() -> sk_sp<DisplayList> {
230 static Color color = Color::Black().WithAlpha(0.5);
231 static float scale = 3;
232 static bool add_circle_clip = true;
233
234 if (AiksTest::ImGuiBegin("Controls", nullptr,
235 ImGuiWindowFlags_AlwaysAutoResize)) {
236 ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
237 ImGui::SliderFloat("Scale", &scale, 0, 6);
238 ImGui::Checkbox("Circle clip", &add_circle_clip);
239 ImGui::End();
240 }
241
242 DisplayListBuilder builder;
243 builder.Scale(GetContentScale().x, GetContentScale().y);
244 DlPaint paint;
245
246 paint.setColor(DlColor::kWhite());
247 builder.DrawPaint(paint);
248
249 paint.setColor(
250 DlColor::ARGB(color.alpha, color.red, color.green, color.blue));
251 paint.setDrawStyle(DlDrawStyle::kStroke);
252 paint.setStrokeWidth(10);
253
254 SkPath path;
255 path.moveTo({20, 20});
256 path.quadTo({60, 20}, {60, 60});
257 path.close();
258 path.moveTo({60, 20});
259 path.quadTo({60, 60}, {20, 60});
260
261 builder.Scale(scale, scale);
262
263 if (add_circle_clip) {
264 static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
265 Color::Red());
266 static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
267 Color::Red());
268 auto [handle_a, handle_b] =
269 DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
270
271 SkMatrix screen_to_canvas = SkMatrix::I();
272 if (!builder.GetTransform().invert(&screen_to_canvas)) {
273 return nullptr;
274 }
275
276 SkPoint point_a =
277 screen_to_canvas.mapPoint(SkPoint::Make(handle_a.x, handle_a.y));
278 SkPoint point_b =
279 screen_to_canvas.mapPoint(SkPoint::Make(handle_b.x, handle_b.y));
280
281 SkPoint middle = point_a + point_b;
282 middle.scale(GetContentScale().x / 2);
283
284 auto radius = SkPoint::Distance(point_a, middle);
285
286 builder.ClipPath(SkPath::Circle(middle.x(), middle.y(), radius));
287 }
288
289 for (auto join :
290 {DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) {
291 paint.setStrokeJoin(join);
292 for (auto cap :
293 {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
294 paint.setStrokeCap(cap);
295 builder.DrawPath(path, paint);
296 builder.Translate(80, 0);
297 }
298 builder.Translate(-240, 60);
299 }
300
301 return builder.Build();
302 };
303
304 ASSERT_TRUE(OpenPlaygroundHere(callback));
305 }
306
307 TEST_P(AiksTest, DrawLinesRenderCorrectly) {
308 DisplayListBuilder builder;
309 builder.Scale(GetContentScale().x, GetContentScale().y);
310
311 DlPaint paint;
312 paint.setColor(DlColor::kBlue());
313 paint.setStrokeWidth(10);
314
315 auto draw = [&builder](DlPaint& paint) {
316 for (auto cap :
317 {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
318 paint.setStrokeCap(cap);
319 SkPoint origin = {100, 100};
320 builder.DrawLine({150, 100}, {250, 100}, paint);
321 for (int d = 15; d < 90; d += 15) {
322 Matrix m = Matrix::MakeRotationZ(Degrees(d));
323 Point origin = {100, 100};
324 Point p0 = {50, 0};
325 Point p1 = {150, 0};
326 auto a = origin + m * p0;
327 auto b = origin + m * p1;
328
329 builder.DrawLine(SkPoint::Make(a.x, a.y), SkPoint::Make(b.x, b.y),
330 paint);
331 }
332 builder.DrawLine({100, 150}, {100, 250}, paint);
333 builder.DrawCircle({origin}, 35, paint);
334
335 builder.DrawLine({250, 250}, {250, 250}, paint);
336
337 builder.Translate(250, 0);
338 }
339 builder.Translate(-750, 250);
340 };
341
342 std::vector<DlColor> colors = {
343 DlColor::ARGB(1, 0x1f / 255.0, 0.0, 0x5c / 255.0),
344 DlColor::ARGB(1, 0x5b / 255.0, 0.0, 0x60 / 255.0),
345 DlColor::ARGB(1, 0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0),
346 DlColor::ARGB(1, 0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0),
347 DlColor::ARGB(1, 0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0),
348 DlColor::ARGB(1, 0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0),
349 DlColor::ARGB(1, 0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0)};
350 std::vector<Scalar> stops = {
351 0.0,
352 (1.0 / 6.0) * 1,
353 (1.0 / 6.0) * 2,
354 (1.0 / 6.0) * 3,
355 (1.0 / 6.0) * 4,
356 (1.0 / 6.0) * 5,
357 1.0,
358 };
359
360 auto texture = DlImageImpeller::Make(
361 CreateTextureForFixture("airplane.jpg",
362 /*enable_mipmapping=*/true));
363
364 draw(paint);
365
366 paint.setColorSource(DlColorSource::MakeRadial({100, 100}, 200, stops.size(),
367 colors.data(), stops.data(),
368 DlTileMode::kMirror));
369 draw(paint);
370
371 SkMatrix matrix = SkMatrix::Translate(-150, 75);
372 paint.setColorSource(std::make_shared<DlImageColorSource>(
373 texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
374 DlImageSampling::kMipmapLinear, &matrix));
375 draw(paint);
376
377 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
378 }
379
380 TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
381 DisplayListBuilder builder;
382 DlPaint paint;
383 paint.setColor(DlColor::kRed());
384 paint.setDrawStyle(DlDrawStyle::kStroke);
385 paint.setStrokeWidth(10);
386
387 builder.Translate(100, 100);
388 builder.DrawPath(SkPath::Rect(SkRect::MakeSize(SkSize{100, 100})), {paint});
389
390 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
391 }
392
393 TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) {
394 DisplayListBuilder builder;
395 DlPaint paint;
396 paint.setColor(DlColor::kRed());
397 paint.setDrawStyle(DlDrawStyle::kStroke);
398 paint.setStrokeWidth(10);
399 paint.setStrokeJoin(DlStrokeJoin::kBevel);
400
401 builder.Translate(100, 100);
402 builder.DrawPath(SkPath::Rect(SkRect::MakeSize(SkSize{100, 100})), paint);
403
404 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
405 }
406
407 TEST_P(AiksTest, CanDrawMultiContourConvexPath) {
408 SkPath path;
409 for (auto i = 0; i < 10; i++) {
410 if (i % 2 == 0) {
411 path.addCircle(100 + 50 * i, 100 + 50 * i, 100);
412 path.close();
413 } else {
414 path.moveTo({100.f + 50.f * i - 100, 100.f + 50.f * i});
415 path.lineTo({100.f + 50.f * i, 100.f + 50.f * i - 100});
416 path.lineTo({100.f + 50.f * i - 100, 100.f + 50.f * i - 100});
417 path.close();
418 }
419 }
420
421 DisplayListBuilder builder;
422 DlPaint paint;
423 paint.setColor(DlColor::kRed().withAlpha(102));
424 builder.DrawPath(path, paint);
425
426 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
427 }
428
429 TEST_P(AiksTest, ArcWithZeroSweepAndBlur) {
430 DisplayListBuilder builder;
431 builder.Scale(GetContentScale().x, GetContentScale().y);
432
433 DlPaint paint;
434 paint.setColor(DlColor::kRed());
435
436 std::vector<DlColor> colors = {DlColor::RGBA(1.0, 0.0, 0.0, 1.0),
437 DlColor::RGBA(0.0, 0.0, 0.0, 1.0)};
438 std::vector<Scalar> stops = {0.0, 1.0};
439
440 paint.setColorSource(
441 DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(),
442 stops.data(), DlTileMode::kMirror));
443 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
444
445 SkPath path;
446 path.addArc(SkRect::MakeXYWH(10, 10, 100, 100), 0, 0);
447 builder.DrawPath(path, paint);
448
449 // Check that this empty picture can be created without crashing.
450 builder.Build();
451 }
452
453 TEST_P(AiksTest, CanRenderClips) {
454 DisplayListBuilder builder;
455 DlPaint paint;
456 paint.setColor(DlColor::kFuchsia());
457
458 builder.ClipPath(SkPath::Rect(SkRect::MakeXYWH(0, 0, 500, 500)));
459 builder.DrawPath(SkPath::Circle(500, 500, 250), paint);
460
461 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
462 }
463
464 TEST_P(AiksTest, CanRenderOverlappingMultiContourPath) {
465 DisplayListBuilder builder;
466
467 DlPaint paint;
468 paint.setColor(DlColor::kRed());
469
470 SkPoint radii[4] = {{50, 50}, {50, 50}, {50, 50}, {50, 50}};
471
472 const Scalar kTriangleHeight = 100;
473 SkRRect rrect;
474 rrect.setRectRadii(
475 SkRect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
476 kTriangleHeight, kTriangleHeight),
477 radii //
478 );
479
480 builder.Translate(200, 200);
481 // Form a path similar to the Material drop slider value indicator. Both
482 // shapes should render identically side-by-side.
483 {
484 SkPath path;
485 path.moveTo(0, kTriangleHeight);
486 path.lineTo(-kTriangleHeight / 2.0f, 0);
487 path.lineTo(kTriangleHeight / 2.0f, 0);
488 path.close();
489 path.addRRect(rrect);
490
491 builder.DrawPath(path, paint);
492 }
493 builder.Translate(100, 0);
494
495 {
496 SkPath path;
497 path.moveTo(0, kTriangleHeight);
498 path.lineTo(-kTriangleHeight / 2.0f, 0);
499 path.lineTo(0, -10);
500 path.lineTo(kTriangleHeight / 2.0f, 0);
501 path.close();
502 path.addRRect(rrect);
503
504 builder.DrawPath(path, paint);
505 }
506
507 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
508 }
509
510 } // namespace testing
511 } // namespace impeller
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:271
aiks_unittests.h
impeller::Color
Definition: color.h:123
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::Point
TPoint< Scalar > Point
Definition: point.h:327
widgets.h
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:277
flutter
Definition: dl_golden_blur_unittests.cc:15
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:776
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:78
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
impeller::PlaygroundPoint
Definition: widgets.h:17
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:265
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::Degrees
Definition: scalar.h:51
color
DlColor color
Definition: dl_golden_blur_unittests.cc:24
impeller
Definition: allocation.cc:12
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
dl_image_impeller.h