Flutter Engine
 
Loading...
Searching...
No Matches
dl_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 <array>
6#include <cmath>
7#include <memory>
8#include <vector>
9
22#include "gtest/gtest.h"
34#include "third_party/imgui/imgui.h"
35
36namespace impeller {
37namespace testing {
38
39flutter::DlColor toColor(const float* components) {
41 Color(components[0], components[1], components[2], components[3])));
42}
43
46
47TEST_P(DisplayListTest, CanDrawRect) {
49 builder.DrawRect(DlRect::MakeXYWH(10, 10, 100, 100),
51 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
52}
53
54TEST_P(DisplayListTest, CanDrawTextBlob) {
57 SkTextBlob::MakeFromString("Hello", CreateTestFont())),
59 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
60}
61
62TEST_P(DisplayListTest, CanDrawTextBlobWithGradient) {
64
65 std::vector<flutter::DlColor> colors = {flutter::DlColor::kBlue(),
67 const float stops[2] = {0.0, 1.0};
68
69 auto linear = flutter::DlColorSource::MakeLinear({0.0, 0.0}, {300.0, 300.0},
70 2, colors.data(), stops,
72 flutter::DlPaint paint;
73 paint.setColorSource(linear);
74
75 builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
76 "Hello World", CreateTestFont())),
77 100, 100, paint);
78 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
79}
80
81TEST_P(DisplayListTest, CanDrawTextWithSaveLayer) {
84 SkTextBlob::MakeFromString("Hello", CreateTestFont())),
86
87 flutter::DlPaint save_paint;
88 float alpha = 0.5;
89 save_paint.setAlpha(static_cast<uint8_t>(255 * alpha));
90 builder.SaveLayer(std::nullopt, &save_paint);
91 builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
92 "Hello with half alpha", CreateTestFontOfSize(100))),
94 builder.Restore();
95 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
96}
97
98TEST_P(DisplayListTest, CanDrawImage) {
99 auto texture = CreateTextureForFixture("embarcadero.jpg");
103 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
104}
105
106TEST_P(DisplayListTest, CanDrawCapsAndJoins) {
108 flutter::DlPaint paint;
109
111 paint.setStrokeWidth(30);
113
114 flutter::DlPathBuilder path_builder;
115 path_builder.MoveTo(DlPoint(-50, 0));
116 path_builder.LineTo(DlPoint(0, -50));
117 path_builder.LineTo(DlPoint(50, 0));
118 flutter::DlPath path = path_builder.TakePath();
119
120 builder.Translate(100, 100);
121 {
124 paint.setStrokeMiter(4);
125 builder.DrawPath(path, paint);
126 }
127
128 {
129 builder.Save();
130 builder.Translate(0, 100);
131 // The joint in the path is 45 degrees. A miter length of 1 convert to a
132 // bevel in this case.
133 paint.setStrokeMiter(1);
134 builder.DrawPath(path, paint);
135 builder.Restore();
136 }
137
138 builder.Translate(150, 0);
139 {
142 builder.DrawPath(path, paint);
143 }
144
145 builder.Translate(150, 0);
146 {
149 builder.DrawPath(path, paint);
150 }
151
152 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
153}
154
156 auto callback = [&]() {
157 static float start_angle = 45;
158 static float sweep_angle = 270;
159 static float stroke_width = 10;
160 static bool use_center = true;
161
162 static int selected_cap = 0;
163 const char* cap_names[] = {"Butt", "Round", "Square"};
165
166 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
167 ImGui::SliderFloat("Start angle", &start_angle, -360, 360);
168 ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360);
169 ImGui::SliderFloat("Stroke width", &stroke_width, 0, 300);
170 ImGui::Combo("Cap", &selected_cap, cap_names,
171 sizeof(cap_names) / sizeof(char*));
172 ImGui::Checkbox("Use center", &use_center);
173 ImGui::End();
174
175 switch (selected_cap) {
176 case 0:
178 break;
179 case 1:
181 break;
182 case 2:
184 break;
185 default:
187 break;
188 }
189
190 static PlaygroundPoint point_a(Point(200, 200), 20, Color::White());
191 static PlaygroundPoint point_b(Point(400, 400), 20, Color::White());
192 auto [p1, p2] = DrawPlaygroundLine(point_a, point_b);
193
195 flutter::DlPaint paint;
196
197 Vector2 scale = GetContentScale();
198 builder.Scale(scale.x, scale.y);
200 paint.setStrokeCap(cap);
202 paint.setStrokeMiter(10);
203 auto rect = DlRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
205 paint.setStrokeWidth(2);
206 builder.DrawRect(rect, paint);
208 paint.setStrokeWidth(stroke_width);
209 builder.DrawArc(rect, start_angle, sweep_angle, use_center, paint);
210
211 return builder.Build();
212 };
213 ASSERT_TRUE(OpenPlaygroundHere(callback));
214}
215
216TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) {
217 auto callback = [&]() {
219 flutter::DlPaint paint;
220
223
224 static float stroke_width = 10.0f;
225 static int selected_stroke_type = 0;
226 static int selected_join_type = 0;
227 const char* stroke_types[] = {"Butte", "Round", "Square"};
228 const char* join_type[] = {"kMiter", "Round", "kBevel"};
229
230 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
231 ImGui::Combo("Cap", &selected_stroke_type, stroke_types,
232 sizeof(stroke_types) / sizeof(char*));
233 ImGui::Combo("Join", &selected_join_type, join_type,
234 sizeof(join_type) / sizeof(char*));
235 ImGui::SliderFloat("Stroke Width", &stroke_width, 10.0f, 50.0f);
236 ImGui::End();
237
240 switch (selected_stroke_type) {
241 case 0:
243 break;
244 case 1:
246 break;
247 case 2:
249 break;
250 default:
252 break;
253 }
254 switch (selected_join_type) {
255 case 0:
257 break;
258 case 1:
260 break;
261 case 2:
263 break;
264 default:
266 break;
267 }
268 paint.setStrokeCap(cap);
269 paint.setStrokeJoin(join);
270 paint.setStrokeWidth(stroke_width);
271
272 // Make rendering better to watch.
273 builder.Scale(1.5f, 1.5f);
274
275 // Rectangle
276 builder.Translate(100, 100);
277 builder.DrawRect(DlRect::MakeWH(100, 100), paint);
278
279 // Rounded rectangle
280 builder.Translate(150, 0);
281 builder.DrawRoundRect(
282 DlRoundRect::MakeRectXY(DlRect::MakeWH(100, 50), 10, 10), paint);
283
284 // Double rounded rectangle
285 builder.Translate(150, 0);
286 builder.DrawDiffRoundRect(
287 DlRoundRect::MakeRectXY(DlRect::MakeWH(100, 50), 10, 10),
288 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(10, 10, 80, 30), 10, 10),
289 paint);
290
291 // Contour with duplicate join points
292 {
293 builder.Translate(150, 0);
294 flutter::DlPathBuilder path_builder;
295 path_builder.MoveTo(DlPoint(0, 0));
296 path_builder.LineTo(DlPoint(0, 0));
297 path_builder.LineTo(DlPoint(100, 0));
298 path_builder.LineTo(DlPoint(100, 0));
299 path_builder.LineTo(DlPoint(100, 100));
300 builder.DrawPath(path_builder.TakePath(), paint);
301 }
302
303 // Contour with duplicate start and end points
304
305 // Line.
306 builder.Translate(200, 0);
307 {
308 builder.Save();
309
310 flutter::DlPathBuilder line_path_builder;
311 line_path_builder.MoveTo(DlPoint(0, 0));
312 line_path_builder.MoveTo(DlPoint(0, 0));
313 line_path_builder.LineTo(DlPoint(0, 0));
314 line_path_builder.LineTo(DlPoint(0, 0));
315 line_path_builder.LineTo(DlPoint(50, 50));
316 line_path_builder.LineTo(DlPoint(50, 50));
317 line_path_builder.LineTo(DlPoint(100, 0));
318 line_path_builder.LineTo(DlPoint(100, 0));
319 DlPath line_path = line_path_builder.TakePath();
320 builder.DrawPath(line_path, paint);
321
322 builder.Translate(0, 100);
323 builder.DrawPath(line_path, paint);
324
325 builder.Translate(0, 100);
326 flutter::DlPathBuilder line_path_builder2;
327 line_path_builder2.MoveTo(DlPoint(0, 0));
328 line_path_builder2.LineTo(DlPoint(0, 0));
329 line_path_builder2.LineTo(DlPoint(0, 0));
330 builder.DrawPath(line_path_builder2.TakePath(), paint);
331
332 builder.Restore();
333 }
334
335 // Cubic.
336 builder.Translate(150, 0);
337 {
338 builder.Save();
339
340 flutter::DlPathBuilder cubic_path;
341 cubic_path.MoveTo(DlPoint(0, 0));
342 cubic_path.CubicCurveTo(DlPoint(0, 0), //
343 DlPoint(140.0, 100.0), //
344 DlPoint(140, 20));
345 builder.DrawPath(cubic_path.TakePath(), paint);
346
347 builder.Translate(0, 100);
348 flutter::DlPathBuilder cubic_path2;
349 cubic_path2.MoveTo(DlPoint(0, 0));
350 cubic_path2.CubicCurveTo(DlPoint(0, 0), //
351 DlPoint(0, 0), //
352 DlPoint(150, 150));
353 builder.DrawPath(cubic_path2.TakePath(), paint);
354
355 builder.Translate(0, 100);
356 flutter::DlPathBuilder cubic_path3;
357 cubic_path3.MoveTo(DlPoint(0, 0));
358 cubic_path3.CubicCurveTo(DlPoint(0, 0), //
359 DlPoint(0, 0), //
360 DlPoint(0, 0));
361 builder.DrawPath(cubic_path3.TakePath(), paint);
362
363 builder.Restore();
364 }
365
366 // Quad.
367 builder.Translate(200, 0);
368 {
369 builder.Save();
370
371 flutter::DlPathBuilder quad_path;
372 quad_path.MoveTo(DlPoint(0, 0));
373 quad_path.MoveTo(DlPoint(0, 0));
374 quad_path.QuadraticCurveTo(DlPoint(100, 40), DlPoint(50, 80));
375 builder.DrawPath(quad_path.TakePath(), paint);
376
377 builder.Translate(0, 150);
378 flutter::DlPathBuilder quad_path2;
379 quad_path2.MoveTo(DlPoint(0, 0));
380 quad_path2.MoveTo(DlPoint(0, 0));
381 quad_path2.QuadraticCurveTo(DlPoint(0, 0), DlPoint(100, 100));
382 builder.DrawPath(quad_path2.TakePath(), paint);
383
384 builder.Translate(0, 100);
385 flutter::DlPathBuilder quad_path3;
386 quad_path3.MoveTo(DlPoint(0, 0));
387 quad_path3.QuadraticCurveTo(DlPoint(0, 0), DlPoint(0, 0));
388 builder.DrawPath(quad_path3.TakePath(), paint);
389
390 builder.Restore();
391 }
392 return builder.Build();
393 };
394 ASSERT_TRUE(OpenPlaygroundHere(callback));
395}
396
397TEST_P(DisplayListTest, CanDrawWithOddPathWinding) {
399 flutter::DlPaint paint;
400
403
404 builder.Translate(300, 300);
405 flutter::DlPathBuilder path_builder;
406 path_builder.AddCircle(DlPoint(0, 0), 100);
407 path_builder.AddCircle(DlPoint(0, 0), 50);
408 path_builder.SetFillType(flutter::DlPathFillType::kOdd);
409 builder.DrawPath(path_builder.TakePath(), paint);
410
411 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
412}
413
414// Regression test for https://github.com/flutter/flutter/issues/134816.
415//
416// It should be possible to draw 3 lines, and not have an implicit close path.
417TEST_P(DisplayListTest, CanDrawAnOpenPath) {
419 flutter::DlPaint paint;
420
423 paint.setStrokeWidth(10);
424
425 builder.Translate(300, 300);
426
427 // Move to (50, 50) and draw lines from:
428 // 1. (50, height)
429 // 2. (width, height)
430 // 3. (width, 50)
431 flutter::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 builder.DrawPath(path_builder.TakePath(), paint);
437
438 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
439}
440
441TEST_P(DisplayListTest, CanDrawWithMaskBlur) {
442 auto texture = CreateTextureForFixture("embarcadero.jpg");
444 flutter::DlPaint paint;
445
446 // Mask blurred image.
447 {
448 auto filter =
450 paint.setMaskFilter(&filter);
453 }
454
455 // Mask blurred filled path.
456 {
458 auto filter =
460 paint.setMaskFilter(&filter);
461 builder.DrawArc(DlRect::MakeXYWH(410, 110, 100, 100), 45, 270, true, paint);
462 }
463
464 // Mask blurred text.
465 {
466 auto filter =
468 paint.setMaskFilter(&filter);
469 builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
470 "Testing", CreateTestFont())),
471 220, 170, paint);
472 }
473
474 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
475}
476
477TEST_P(DisplayListTest, CanDrawStrokedText) {
479 flutter::DlPaint paint;
480
483 builder.DrawText(flutter::DlTextSkia::Make(SkTextBlob::MakeFromString(
484 "stoked about stroked text", CreateTestFont())),
485 250, 250, paint);
486
487 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
488}
489
490// Regression test for https://github.com/flutter/flutter/issues/133157.
491TEST_P(DisplayListTest, StrokedTextNotOffsetFromNormalText) {
493 flutter::DlPaint paint;
494 auto const& text_blob = SkTextBlob::MakeFromString("00000", CreateTestFont());
495 auto text = flutter::DlTextSkia::Make(text_blob);
496
497 // https://api.flutter.dev/flutter/material/Colors/blue-constant.html.
498 auto const& mat_blue = flutter::DlColor(0xFF2196f3);
499
500 // Draw a blue filled rectangle so the text is easier to see.
502 paint.setColor(mat_blue);
503 builder.DrawRect(DlRect::MakeXYWH(0, 0, 500, 500), paint);
504
505 // Draw stacked text, with stroked text on top.
508 builder.DrawText(text, 250, 250, paint);
509
512 builder.DrawText(text, 250, 250, paint);
513
514 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
515}
516
517TEST_P(DisplayListTest, IgnoreMaskFilterWhenSavingLayer) {
518 auto texture = CreateTextureForFixture("embarcadero.jpg");
521 flutter::DlPaint paint;
522 paint.setMaskFilter(&filter);
523 builder.SaveLayer(std::nullopt, &paint);
526 builder.Restore();
527 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
528}
529
530TEST_P(DisplayListTest, CanDrawWithBlendColorFilter) {
531 auto texture = CreateTextureForFixture("embarcadero.jpg");
533 flutter::DlPaint paint;
534
535 // Pipeline blended image.
536 {
538 flutter::DlColor::kYellow(), flutter::DlBlendMode::kModulate);
539 paint.setColorFilter(filter);
542 }
543
544 // Advanced blended image.
545 {
547 flutter::DlColor::kRed(), flutter::DlBlendMode::kScreen);
548 paint.setColorFilter(filter);
551 }
552
553 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
554}
555
556TEST_P(DisplayListTest, CanDrawWithColorFilterImageFilter) {
557 const float invert_color_matrix[20] = {
558 -1, 0, 0, 0, 1, //
559 0, -1, 0, 0, 1, //
560 0, 0, -1, 0, 1, //
561 0, 0, 0, 1, 0, //
562 };
563 auto texture = CreateTextureForFixture("boston.jpg");
565 flutter::DlPaint paint;
566
567 auto color_filter = flutter::DlColorFilter::MakeMatrix(invert_color_matrix);
568 auto image_filter = flutter::DlImageFilter::MakeColorFilter(color_filter);
569
570 paint.setImageFilter(image_filter);
573
574 builder.Translate(0, 700);
575 paint.setColorFilter(color_filter);
578 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
579}
580
581TEST_P(DisplayListTest, CanDrawWithImageBlurFilter) {
582 auto texture = CreateTextureForFixture("embarcadero.jpg");
583
584 auto callback = [&]() {
585 static float sigma[] = {10, 10};
586
587 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
588 ImGui::SliderFloat2("Sigma", sigma, 0, 100);
589 ImGui::End();
590
592 flutter::DlPaint paint;
593
594 auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
596 paint.setImageFilter(&filter);
599
600 return builder.Build();
601 };
602
603 ASSERT_TRUE(OpenPlaygroundHere(callback));
604}
605
606TEST_P(DisplayListTest, CanDrawWithComposeImageFilter) {
607 auto texture = CreateTextureForFixture("boston.jpg");
609 flutter::DlPaint paint;
610
611 auto dilate = std::make_shared<flutter::DlDilateImageFilter>(10.0, 10.0);
612 auto erode = std::make_shared<flutter::DlErodeImageFilter>(10.0, 10.0);
613 auto open = std::make_shared<flutter::DlComposeImageFilter>(dilate, erode);
614 auto close = std::make_shared<flutter::DlComposeImageFilter>(erode, dilate);
615
616 paint.setImageFilter(open.get());
619 builder.Translate(0, 700);
620 paint.setImageFilter(close.get());
623 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
624}
625
626TEST_P(DisplayListTest, CanClampTheResultingColorOfColorMatrixFilter) {
627 auto texture = CreateTextureForFixture("boston.jpg");
628 const float inner_color_matrix[20] = {
629 1, 0, 0, 0, 0, //
630 0, 1, 0, 0, 0, //
631 0, 0, 1, 0, 0, //
632 0, 0, 0, 2, 0, //
633 };
634 const float outer_color_matrix[20] = {
635 1, 0, 0, 0, 0, //
636 0, 1, 0, 0, 0, //
637 0, 0, 1, 0, 0, //
638 0, 0, 0, 0.5, 0, //
639 };
640 auto inner_color_filter =
641 flutter::DlColorFilter::MakeMatrix(inner_color_matrix);
642 auto outer_color_filter =
643 flutter::DlColorFilter::MakeMatrix(outer_color_matrix);
644 auto inner = flutter::DlImageFilter::MakeColorFilter(inner_color_filter);
645 auto outer = flutter::DlImageFilter::MakeColorFilter(outer_color_filter);
646 auto compose = std::make_shared<flutter::DlComposeImageFilter>(outer, inner);
647
649 flutter::DlPaint paint;
650 paint.setImageFilter(compose.get());
653 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
654}
655
656TEST_P(DisplayListTest, CanDrawBackdropFilter) {
657 auto texture = CreateTextureForFixture("embarcadero.jpg");
658
659 auto callback = [&]() {
660 static float sigma[] = {10, 10};
661 static float ctm_scale = 1;
662 static bool use_bounds = true;
663 static bool draw_circle = true;
664 static bool add_clip = true;
665
666 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
667 ImGui::SliderFloat2("Sigma", sigma, 0, 100);
668 ImGui::SliderFloat("Scale", &ctm_scale, 0, 10);
669 ImGui::NewLine();
670 ImGui::TextWrapped(
671 "If everything is working correctly, none of the options below should "
672 "impact the filter's appearance.");
673 ImGui::Checkbox("Use SaveLayer bounds", &use_bounds);
674 ImGui::Checkbox("Draw child element", &draw_circle);
675 ImGui::Checkbox("Add pre-clip", &add_clip);
676 ImGui::End();
677
679
680 Vector2 scale = ctm_scale * GetContentScale();
681 builder.Scale(scale.x, scale.y);
682
683 auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
685
686 std::optional<DlRect> bounds;
687 if (use_bounds) {
688 static PlaygroundPoint point_a(Point(350, 150), 20, Color::White());
689 static PlaygroundPoint point_b(Point(800, 600), 20, Color::White());
690 auto [p1, p2] = DrawPlaygroundLine(point_a, point_b);
691 bounds = DlRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
692 }
693
694 // Insert a clip to test that the backdrop filter handles stencil depths > 0
695 // correctly.
696 if (add_clip) {
697 builder.ClipRect(DlRect::MakeLTRB(0, 0, 99999, 99999),
699 }
700
703 builder.SaveLayer(bounds, nullptr, &filter);
704
705 if (draw_circle) {
706 static PlaygroundPoint center_point(Point(500, 400), 20, Color::Red());
707 auto circle_center = DrawPlaygroundPoint(center_point);
708
709 flutter::DlPaint paint;
713 paint.setStrokeWidth(10);
714 paint.setColor(flutter::DlColor::kRed().withAlpha(100));
715 builder.DrawCircle(DlPoint(circle_center.x, circle_center.y), 100, paint);
716 }
717
718 return builder.Build();
719 };
720
721 ASSERT_TRUE(OpenPlaygroundHere(callback));
722}
723
724TEST_P(DisplayListTest, CanDrawNinePatchImage) {
725 // Image is drawn with corners to scale and center pieces stretched to fit.
726 auto texture = CreateTextureForFixture("embarcadero.jpg");
728 auto size = texture->GetSize();
729 builder.DrawImageNine(
731 DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
732 size.height * 3 / 4),
733 DlRect::MakeLTRB(0, 0, size.width * 2, size.height * 2),
735 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
736}
737
738TEST_P(DisplayListTest, CanDrawNinePatchImageCenterWidthBiggerThanDest) {
739 // Edge case, the width of the corners does not leave any room for the
740 // center slice. The center (across the vertical axis) is folded out of the
741 // resulting image.
742 auto texture = CreateTextureForFixture("embarcadero.jpg");
744 auto size = texture->GetSize();
745 builder.DrawImageNine(
747 DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
748 size.height * 3 / 4),
749 DlRect::MakeLTRB(0, 0, size.width / 2, size.height),
751 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
752}
753
754TEST_P(DisplayListTest, CanDrawNinePatchImageCenterHeightBiggerThanDest) {
755 // Edge case, the height of the corners does not leave any room for the
756 // center slice. The center (across the horizontal axis) is folded out of the
757 // resulting image.
758 auto texture = CreateTextureForFixture("embarcadero.jpg");
760 auto size = texture->GetSize();
761 builder.DrawImageNine(
763 DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
764 size.height * 3 / 4),
765 DlRect::MakeLTRB(0, 0, size.width, size.height / 2),
767 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
768}
769
770TEST_P(DisplayListTest, CanDrawNinePatchImageCenterBiggerThanDest) {
771 // Edge case, the width and height of the corners does not leave any
772 // room for the center slices. Only the corners are displayed.
773 auto texture = CreateTextureForFixture("embarcadero.jpg");
775 auto size = texture->GetSize();
776 builder.DrawImageNine(
778 DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
779 size.height * 3 / 4),
780 DlRect::MakeLTRB(0, 0, size.width / 2, size.height / 2),
782 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
783}
784
785TEST_P(DisplayListTest, CanDrawNinePatchImageCornersScaledDown) {
786 // Edge case, there is not enough room for the corners to be drawn
787 // without scaling them down.
788 auto texture = CreateTextureForFixture("embarcadero.jpg");
790 auto size = texture->GetSize();
791 builder.DrawImageNine(
793 DlIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
794 size.height * 3 / 4),
795 DlRect::MakeLTRB(0, 0, size.width / 4, size.height / 4),
797 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
798}
799
800TEST_P(DisplayListTest, NinePatchImagePrecision) {
801 // Draw a nine patch image with colored corners and verify that the corner
802 // color does not leak outside the intended region.
803 auto texture = CreateTextureForFixture("nine_patch_corners.png");
806 DlIRect::MakeXYWH(10, 10, 1, 1),
807 DlRect::MakeXYWH(0, 0, 200, 100),
809 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
810}
811
812TEST_P(DisplayListTest, NinePatchImageColorFilter) {
813 auto texture = CreateTextureForFixture("nine_patch2.png");
814
816 flutter::DlBlendMode::kSrcIn);
817 flutter::DlPaint paint;
818 paint.setColorFilter(filter);
819
822 DlIRect::MakeXYWH(10, 10, 1, 1),
823 DlRect::MakeXYWH(0, 0, 200, 100),
825 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
826}
827
828TEST_P(DisplayListTest, CanDrawPoints) {
830 DlPoint points[7] = {
831 {0, 0}, //
832 {100, 100}, //
833 {100, 0}, //
834 {0, 100}, //
835 {0, 0}, //
836 {48, 48}, //
837 {52, 52}, //
838 };
839 std::vector<flutter::DlStrokeCap> caps = {
843 };
844 flutter::DlPaint paint =
846 .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
847 .setStrokeWidth(20);
848 builder.Translate(50, 50);
849 for (auto cap : caps) {
850 paint.setStrokeCap(cap);
851 builder.Save();
853 builder.Translate(150, 0);
855 builder.Translate(150, 0);
857 builder.Restore();
858 builder.Translate(0, 150);
859 }
860 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
861}
862
863TEST_P(DisplayListTest, CanDrawZeroLengthLine) {
865 std::vector<flutter::DlStrokeCap> caps = {
869 };
870 flutter::DlPaint paint =
872 .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
875 .setStrokeWidth(20);
876 DlPath path = DlPath::MakeLine({150, 50}, {150, 50});
877 for (auto cap : caps) {
878 paint.setStrokeCap(cap);
879 builder.DrawLine(DlPoint(50, 50), DlPoint(50, 50), paint);
880 builder.DrawPath(path, paint);
881 builder.Translate(0, 150);
882 }
883 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
884}
885
886TEST_P(DisplayListTest, CanDrawShadow) {
888 flutter::DlPaint paint;
889
890 auto content_scale = GetContentScale() * 0.8;
891 builder.Scale(content_scale.x, content_scale.y);
892
893 constexpr size_t star_spikes = 5;
894 constexpr DlScalar half_spike_rotation = kPi / star_spikes;
895 constexpr DlScalar radius = 40;
896 constexpr DlScalar spike_size = 10;
897 constexpr DlScalar outer_radius = radius + spike_size;
898 constexpr DlScalar inner_radius = radius - spike_size;
899 std::array<DlPoint, star_spikes * 2> star;
900 for (size_t i = 0; i < star_spikes; i++) {
901 const DlScalar rotation = half_spike_rotation * i * 2;
902 star[i * 2] = DlPoint(50 + std::sin(rotation) * outer_radius,
903 50 - std::cos(rotation) * outer_radius);
904 star[i * 2 + 1] =
905 DlPoint(50 + std::sin(rotation + half_spike_rotation) * inner_radius,
906 50 - std::cos(rotation + half_spike_rotation) * inner_radius);
907 }
908
909 std::array<DlPath, 4> paths = {
910 DlPath::MakeRect(DlRect::MakeXYWH(0, 0, 200, 100)),
911 DlPath::MakeRoundRectXY(DlRect::MakeXYWH(20, 0, 200, 100), 30, 30),
912 DlPath::MakeCircle(DlPoint(100, 50), 50),
913 DlPath::MakePoly(star.data(), star.size(), true),
914 };
916 builder.DrawPaint(paint);
918 builder.Translate(100, 50);
919 for (size_t x = 0; x < paths.size(); x++) {
920 builder.Save();
921 for (size_t y = 0; y < 6; y++) {
922 builder.DrawShadow(paths[x], flutter::DlColor::kBlack(), 3 + y * 8, false,
923 1);
924 builder.DrawPath(paths[x], paint);
925 builder.Translate(0, 150);
926 }
927 builder.Restore();
928 builder.Translate(250, 0);
929 }
930
931 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
932}
933
934TEST_P(DisplayListTest, CanDrawZeroWidthLine) {
936 std::vector<flutter::DlStrokeCap> caps = {
940 };
941 flutter::DlPaint paint = //
945 .setStrokeWidth(0);
946 flutter::DlPaint outline_paint = //
951 .setStrokeWidth(1);
952 DlPath path = DlPath::MakeLine({150, 50}, {160, 50});
953 for (auto cap : caps) {
954 paint.setStrokeCap(cap);
955 builder.DrawLine(DlPoint(50, 50), DlPoint(60, 50), paint);
956 builder.DrawRect(DlRect::MakeLTRB(45, 45, 65, 55), outline_paint);
957 builder.DrawLine(DlPoint{100, 50}, DlPoint{100, 50}, paint);
958 if (cap != flutter::DlStrokeCap::kButt) {
959 builder.DrawRect(DlRect::MakeLTRB(95, 45, 105, 55), outline_paint);
960 }
961 builder.DrawPath(path, paint);
962 builder.DrawRect(path.GetBounds().Expand(5, 5), outline_paint);
963 builder.Translate(0, 150);
964 }
965 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
966}
967
968TEST_P(DisplayListTest, CanDrawWithMatrixFilter) {
969 auto boston = CreateTextureForFixture("boston.jpg");
970
971 auto callback = [&]() {
972 static int selected_matrix_type = 0;
973 const char* matrix_type_names[] = {"Matrix", "Local Matrix"};
974
975 static float ctm_translation[2] = {200, 200};
976 static float ctm_scale[2] = {0.65, 0.65};
977 static float ctm_skew[2] = {0, 0};
978
979 static bool enable = true;
980 static float translation[2] = {100, 100};
981 static float scale[2] = {0.8, 0.8};
982 static float skew[2] = {0.2, 0.2};
983
984 static bool enable_savelayer = true;
985
986 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
987 {
988 ImGui::Combo("Filter type", &selected_matrix_type, matrix_type_names,
989 sizeof(matrix_type_names) / sizeof(char*));
990
991 ImGui::TextWrapped("Current Transform");
992 ImGui::SliderFloat2("CTM Translation", ctm_translation, 0, 1000);
993 ImGui::SliderFloat2("CTM Scale", ctm_scale, 0, 3);
994 ImGui::SliderFloat2("CTM Skew", ctm_skew, -3, 3);
995
996 ImGui::TextWrapped(
997 "MatrixFilter and LocalMatrixFilter modify the CTM in the same way. "
998 "The only difference is that MatrixFilter doesn't affect the effect "
999 "transform, whereas LocalMatrixFilter does.");
1000 // Note: See this behavior in:
1001 // https://fiddle.skia.org/c/6cbb551ab36d06f163db8693972be954
1002 ImGui::Checkbox("Enable", &enable);
1003 ImGui::SliderFloat2("Filter Translation", translation, 0, 1000);
1004 ImGui::SliderFloat2("Filter Scale", scale, 0, 3);
1005 ImGui::SliderFloat2("Filter Skew", skew, -3, 3);
1006
1007 ImGui::TextWrapped(
1008 "Rendering the filtered image within a layer can expose bounds "
1009 "issues. If the rendered image gets cut off when this setting is "
1010 "enabled, there's a coverage bug in the filter.");
1011 ImGui::Checkbox("Render in layer", &enable_savelayer);
1012 }
1013 ImGui::End();
1014
1016 flutter::DlPaint paint;
1017
1018 if (enable_savelayer) {
1019 builder.SaveLayer(std::nullopt, nullptr);
1020 }
1021 {
1022 auto content_scale = GetContentScale();
1023 builder.Scale(content_scale.x, content_scale.y);
1024
1025 // Set the current transform
1026 auto ctm_matrix = Matrix::MakeRow(
1027 ctm_scale[0], ctm_skew[0], 0.0f, ctm_translation[0], //
1028 ctm_skew[1], ctm_scale[1], 0.0f, ctm_translation[1], //
1029 0, 0, 1, 0, //
1030 0, 0, 0, 1);
1031 builder.Transform(ctm_matrix);
1032
1033 // Set the matrix filter
1034 auto filter_matrix =
1035 Matrix::MakeRow(scale[0], skew[0], 0.0f, translation[0], //
1036 skew[1], scale[1], 0.0f, translation[1], //
1037 0.0f, 0.0f, 1.0f, 0.0f, //
1038 0.0f, 0.0f, 0.0f, 1.0f);
1039
1040 if (enable) {
1041 switch (selected_matrix_type) {
1042 case 0: {
1043 auto filter = flutter::DlMatrixImageFilter(
1044 filter_matrix, flutter::DlImageSampling::kLinear);
1045 paint.setImageFilter(&filter);
1046 break;
1047 }
1048 case 1: {
1049 auto internal_filter =
1051 .shared();
1052 auto filter = flutter::DlLocalMatrixImageFilter(filter_matrix,
1053 internal_filter);
1054 paint.setImageFilter(&filter);
1055 break;
1056 }
1057 }
1058 }
1059
1060 builder.DrawImage(DlImageImpeller::Make(boston), DlPoint(),
1062 }
1063 if (enable_savelayer) {
1064 builder.Restore();
1065 }
1066
1067 return builder.Build();
1068 };
1069
1070 ASSERT_TRUE(OpenPlaygroundHere(callback));
1071}
1072
1073TEST_P(DisplayListTest, CanDrawWithMatrixFilterWhenSavingLayer) {
1074 auto callback = [&]() {
1075 static float translation[2] = {0, 0};
1076 static bool enable_save_layer = true;
1077
1078 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1079 ImGui::SliderFloat2("Translation", translation, -130, 130);
1080 ImGui::Checkbox("Enable save layer", &enable_save_layer);
1081 ImGui::End();
1082
1084 builder.Save();
1085 builder.Scale(2.0, 2.0);
1086 flutter::DlPaint paint;
1088 builder.DrawRect(DlRect::MakeWH(300, 300), paint);
1089 paint.setStrokeWidth(1.0);
1091 paint.setColor(flutter::DlColor::kBlack().withAlpha(0x80));
1092 builder.DrawLine(DlPoint(150, 0), DlPoint(150, 300), paint);
1093 builder.DrawLine(DlPoint(0, 150), DlPoint(300, 150), paint);
1094
1095 flutter::DlPaint save_paint;
1096 DlRect bounds = DlRect::MakeXYWH(100, 100, 100, 100);
1097 Matrix translate_matrix =
1098 Matrix::MakeTranslation({translation[0], translation[1]});
1099 if (enable_save_layer) {
1100 auto filter = flutter::DlMatrixImageFilter(
1102 save_paint.setImageFilter(filter.shared());
1103 builder.SaveLayer(bounds, &save_paint);
1104 } else {
1105 builder.Save();
1106 builder.Transform(translate_matrix);
1107 }
1108
1109 Matrix filter_matrix;
1110 filter_matrix.Translate({150, 150});
1111 filter_matrix.Scale({0.2f, 0.2f});
1112 filter_matrix.Translate({-150, -150});
1113 auto filter = flutter::DlMatrixImageFilter(
1115
1116 save_paint.setImageFilter(filter.shared());
1117
1118 builder.SaveLayer(bounds, &save_paint);
1119 flutter::DlPaint paint2;
1121 builder.DrawRect(bounds, paint2);
1122 builder.Restore();
1123 builder.Restore();
1124 return builder.Build();
1125 };
1126
1127 ASSERT_TRUE(OpenPlaygroundHere(callback));
1128}
1129
1130TEST_P(DisplayListTest, CanDrawRectWithLinearToSrgbColorFilter) {
1131 flutter::DlPaint paint;
1132 paint.setColor(flutter::DlColor(0xFF2196F3).withAlpha(128));
1135 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), paint);
1136 builder.Translate(0, 200);
1137
1139 builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), paint);
1140
1141 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1142}
1143
1144TEST_P(DisplayListTest, CanDrawPaintWithColorSource) {
1145 const flutter::DlColor colors[2] = {
1146 flutter::DlColor(0xFFF44336),
1147 flutter::DlColor(0xFF2196F3),
1148 };
1149 const float stops[2] = {0.0, 1.0};
1150 flutter::DlPaint paint;
1152 auto clip_bounds = DlRect::MakeWH(300.0, 300.0);
1153 builder.Save();
1154 builder.Translate(100, 100);
1155 builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1156 auto linear =
1157 flutter::DlColorSource::MakeLinear({0.0, 0.0}, {100.0, 100.0}, 2, colors,
1159 paint.setColorSource(linear);
1160 builder.DrawPaint(paint);
1161 builder.Restore();
1162
1163 builder.Save();
1164 builder.Translate(500, 100);
1165 builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1167 {100.0, 100.0}, 100.0, 2, colors, stops, flutter::DlTileMode::kRepeat);
1168 paint.setColorSource(radial);
1169 builder.DrawPaint(paint);
1170 builder.Restore();
1171
1172 builder.Save();
1173 builder.Translate(100, 500);
1174 builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1175 auto sweep =
1176 flutter::DlColorSource::MakeSweep({100.0, 100.0}, 180.0, 270.0, 2, colors,
1178 paint.setColorSource(sweep);
1179 builder.DrawPaint(paint);
1180 builder.Restore();
1181
1182 builder.Save();
1183 builder.Translate(500, 500);
1184 builder.ClipRect(clip_bounds, flutter::DlClipOp::kIntersect, false);
1185 auto texture = CreateTextureForFixture("table_mountain_nx.png");
1189 paint.setColorSource(image);
1190 builder.DrawPaint(paint);
1191 builder.Restore();
1192
1193 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1194}
1195
1196TEST_P(DisplayListTest, CanBlendDstOverAndDstCorrectly) {
1198
1199 {
1200 builder.SaveLayer(std::nullopt, nullptr);
1201 builder.Translate(100, 100);
1202 flutter::DlPaint paint;
1204 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1205 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1206 paint.setBlendMode(flutter::DlBlendMode::kSrcOver);
1207 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1208 builder.Restore();
1209 }
1210 {
1211 builder.SaveLayer(std::nullopt, nullptr);
1212 builder.Translate(300, 100);
1213 flutter::DlPaint paint;
1214 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1215 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1217 paint.setBlendMode(flutter::DlBlendMode::kDstOver);
1218 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1219 builder.Restore();
1220 }
1221 {
1222 builder.SaveLayer(std::nullopt, nullptr);
1223 builder.Translate(100, 300);
1224 flutter::DlPaint paint;
1226 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1227 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1228 paint.setBlendMode(flutter::DlBlendMode::kSrc);
1229 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1230 builder.Restore();
1231 }
1232 {
1233 builder.SaveLayer(std::nullopt, nullptr);
1234 builder.Translate(300, 300);
1235 flutter::DlPaint paint;
1236 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1237 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1239 paint.setBlendMode(flutter::DlBlendMode::kDst);
1240 builder.DrawRect(DlRect::MakeWH(200, 200), paint);
1241 builder.Restore();
1242 }
1243
1244 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1245}
1246
1247TEST_P(DisplayListTest, CanDrawCorrectlyWithColorFilterAndImageFilter) {
1249 const float green_color_matrix[20] = {
1250 0, 0, 0, 0, 0, //
1251 0, 0, 0, 0, 1, //
1252 0, 0, 0, 0, 0, //
1253 0, 0, 0, 1, 0, //
1254 };
1255 const float blue_color_matrix[20] = {
1256 0, 0, 0, 0, 0, //
1257 0, 0, 0, 0, 0, //
1258 0, 0, 0, 0, 1, //
1259 0, 0, 0, 1, 0, //
1260 };
1261 auto green_color_filter =
1262 flutter::DlColorFilter::MakeMatrix(green_color_matrix);
1263 auto blue_color_filter =
1264 flutter::DlColorFilter::MakeMatrix(blue_color_matrix);
1265 auto blue_image_filter =
1266 flutter::DlImageFilter::MakeColorFilter(blue_color_filter);
1267
1268 flutter::DlPaint paint;
1270 paint.setColorFilter(green_color_filter);
1271 paint.setImageFilter(blue_image_filter);
1272 builder.DrawRect(DlRect::MakeLTRB(100, 100, 500, 500), paint);
1273 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1274}
1275
1276TEST_P(DisplayListTest, MaskBlursApplyCorrectlyToColorSources) {
1277 auto blur_filter = std::make_shared<flutter::DlBlurMaskFilter>(
1279
1281
1282 std::array<flutter::DlColor, 2> colors = {flutter::DlColor::kBlue(),
1284 std::array<float, 2> stops = {0, 1};
1285 auto texture = CreateTextureForFixture("airplane.jpg");
1286 auto matrix = flutter::DlMatrix::MakeTranslation({-300, -110});
1287 std::array<std::shared_ptr<flutter::DlColorSource>, 2> color_sources = {
1291 &matrix),
1293 flutter::DlPoint(0, 0), flutter::DlPoint(100, 50), 2, colors.data(),
1294 stops.data(), flutter::DlTileMode::kClamp),
1295 };
1296
1297 builder.Save();
1298 builder.Translate(0, 100);
1299 for (const auto& color_source : color_sources) {
1300 flutter::DlPaint paint;
1301 paint.setColorSource(color_source);
1302 paint.setMaskFilter(blur_filter);
1303
1304 builder.Save();
1305 builder.Translate(100, 0);
1307 builder.DrawRoundRect(
1308 DlRoundRect::MakeRectXY(DlRect::MakeWH(100, 50), 30, 30), paint);
1309
1311 paint.setStrokeWidth(10);
1312 builder.Translate(200, 0);
1313 builder.DrawRoundRect(
1314 DlRoundRect::MakeRectXY(DlRect::MakeWH(100, 50), 30, 30), paint);
1315
1316 builder.Restore();
1317 builder.Translate(0, 100);
1318 }
1319 builder.Restore();
1320
1321 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1322}
1323
1326 std::vector<flutter::DlStrokeJoin> joins = {
1330 };
1331 flutter::DlPaint paint = //
1332 flutter::DlPaint() //
1335 .setStrokeWidth(10);
1336 flutter::DlPaint stroke_paint = //
1337 flutter::DlPaint() //
1340 .setStrokeWidth(10);
1341 DlPath path = DlPath::MakeLine({150, 50}, {160, 50});
1342
1343 builder.Translate(300, 50);
1344 builder.Scale(0.8, 0.8);
1345 for (auto join : joins) {
1346 paint.setStrokeJoin(join);
1347 stroke_paint.setStrokeJoin(join);
1348 builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), paint);
1349 builder.DrawRect(DlRect::MakeXYWH(0, 150, 100, 100), stroke_paint);
1350 builder.DrawRoundRect(
1351 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(150, 0, 100, 100), 30, 30),
1352 paint);
1353 builder.DrawRoundRect(
1354 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(150, 150, 100, 100), 30, 30),
1355 stroke_paint);
1356 builder.DrawCircle(DlPoint(350, 50), 50, paint);
1357 builder.DrawCircle(DlPoint(350, 200), 50, stroke_paint);
1358 builder.Translate(0, 300);
1359 }
1360 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1361}
1362
1363TEST_P(DisplayListTest, DrawCirclesWithTransformations) {
1364 auto callback = [&]() {
1365 static float filled_radius = 100.0;
1366 static float filled_alpha = 255.0;
1367 static float filled_scale[2] = {1.0, 1.0};
1368 static float stroked_radius = 20.0;
1369 static float stroke_width = 10.0;
1370 static float stroked_alpha = 255.0;
1371 static float stroked_scale[2] = {1.0, 1.0};
1372
1373 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1374 {
1375 ImGui::SliderFloat("Filled Radius", &filled_radius, 0, 500);
1376 ImGui::SliderFloat("Filled Alpha", &filled_alpha, 0, 255);
1377 ImGui::SliderFloat2("Filled Scale", filled_scale, 0, 10.0);
1378 ImGui::SliderFloat("Stroked Radius", &stroked_radius, 0, 10.0);
1379 ImGui::SliderFloat("Stroked Width", &stroke_width, 0, 500);
1380 ImGui::SliderFloat("Stroked Alpha", &stroked_alpha, 0, 10.0);
1381 ImGui::SliderFloat2("Stroked Scale", stroked_scale, 0, 10.0);
1382 }
1383 ImGui::End();
1384
1386 flutter::DlPaint paint;
1387
1388 paint.setColor(flutter::DlColor::kBlue().withAlpha(filled_alpha));
1390 builder.Save();
1391 builder.Scale(filled_scale[0], filled_scale[1]);
1392 builder.DrawCircle(DlPoint(500, 750), filled_radius, paint);
1393 builder.Restore();
1394
1395 paint.setColor(flutter::DlColor::kRed().withAlpha(stroked_alpha));
1397 paint.setStrokeWidth(stroke_width);
1398 builder.Save();
1399 builder.Scale(stroked_scale[0], stroked_scale[1]);
1400 builder.DrawCircle(DlPoint(1250, 750), stroked_radius, paint);
1401 builder.Restore();
1402 return builder.Build();
1403 };
1404
1405 ASSERT_TRUE(OpenPlaygroundHere(callback));
1406}
1407
1408TEST_P(DisplayListTest, ClipDrawRRectWithNonCircularRadii) {
1410
1411 flutter::DlPaint fill_paint = //
1412 flutter::DlPaint() //
1415 .setStrokeWidth(10);
1416 flutter::DlPaint stroke_paint = //
1417 flutter::DlPaint() //
1420 .setStrokeWidth(10);
1421
1422 builder.DrawRoundRect(
1423 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1424 fill_paint);
1425 builder.DrawRoundRect(
1426 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1427 stroke_paint);
1428
1429 builder.DrawRoundRect(
1430 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1431 fill_paint);
1432 builder.DrawRoundRect(
1433 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1434 stroke_paint);
1435
1436 flutter::DlPaint reference_paint = //
1437 flutter::DlPaint() //
1440 .setStrokeWidth(10);
1441
1442 builder.DrawRoundRect(
1443 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(500, 500, 300, 300), 40, 40),
1444 reference_paint);
1445 builder.DrawRoundRect(
1446 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 100, 300, 300), 120, 120),
1447 reference_paint);
1448
1449 flutter::DlPaint clip_fill_paint = //
1450 flutter::DlPaint() //
1453 .setStrokeWidth(10);
1454
1455 builder.Save();
1456 builder.ClipRoundRect(
1457 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(900, 100, 300, 300), 120, 40));
1458 builder.DrawPaint(clip_fill_paint);
1459 builder.Restore();
1460
1461 builder.Save();
1462 builder.ClipRoundRect(
1463 DlRoundRect::MakeRectXY(DlRect::MakeXYWH(100, 900, 300, 300), 40, 120));
1464 builder.DrawPaint(clip_fill_paint);
1465 builder.Restore();
1466
1467 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1468}
1469
1470TEST_P(DisplayListTest, DrawVerticesBlendModes) {
1471 std::vector<const char*> blend_mode_names;
1472 std::vector<flutter::DlBlendMode> blend_mode_values;
1473 {
1474 const std::vector<std::tuple<const char*, flutter::DlBlendMode>> blends = {
1475 // Pipeline blends (Porter-Duff alpha compositing)
1476 {"Clear", flutter::DlBlendMode::kClear},
1477 {"Source", flutter::DlBlendMode::kSrc},
1478 {"Destination", flutter::DlBlendMode::kDst},
1479 {"SourceOver", flutter::DlBlendMode::kSrcOver},
1480 {"DestinationOver", flutter::DlBlendMode::kDstOver},
1481 {"SourceIn", flutter::DlBlendMode::kSrcIn},
1482 {"DestinationIn", flutter::DlBlendMode::kDstIn},
1483 {"SourceOut", flutter::DlBlendMode::kSrcOut},
1484 {"DestinationOut", flutter::DlBlendMode::kDstOut},
1485 {"SourceATop", flutter::DlBlendMode::kSrcATop},
1486 {"DestinationATop", flutter::DlBlendMode::kDstATop},
1487 {"Xor", flutter::DlBlendMode::kXor},
1488 {"Plus", flutter::DlBlendMode::kPlus},
1489 {"Modulate", flutter::DlBlendMode::kModulate},
1490 // Advanced blends (color component blends)
1491 {"Screen", flutter::DlBlendMode::kScreen},
1492 {"Overlay", flutter::DlBlendMode::kOverlay},
1493 {"Darken", flutter::DlBlendMode::kDarken},
1494 {"Lighten", flutter::DlBlendMode::kLighten},
1495 {"ColorDodge", flutter::DlBlendMode::kColorDodge},
1496 {"ColorBurn", flutter::DlBlendMode::kColorBurn},
1497 {"HardLight", flutter::DlBlendMode::kHardLight},
1498 {"SoftLight", flutter::DlBlendMode::kSoftLight},
1499 {"Difference", flutter::DlBlendMode::kDifference},
1500 {"Exclusion", flutter::DlBlendMode::kExclusion},
1501 {"Multiply", flutter::DlBlendMode::kMultiply},
1502 {"Hue", flutter::DlBlendMode::kHue},
1503 {"Saturation", flutter::DlBlendMode::kSaturation},
1504 {"Color", flutter::DlBlendMode::kColor},
1505 {"Luminosity", flutter::DlBlendMode::kLuminosity},
1506 };
1507 assert(blends.size() ==
1508 static_cast<size_t>(flutter::DlBlendMode::kLastMode) + 1);
1509 for (const auto& [name, mode] : blends) {
1510 blend_mode_names.push_back(name);
1511 blend_mode_values.push_back(mode);
1512 }
1513 }
1514
1515 auto callback = [&]() {
1516 static int current_blend_index = 3;
1517 static float dst_alpha = 1;
1518 static float src_alpha = 1;
1519 static float color0[4] = {1.0f, 0.0f, 0.0f, 1.0f};
1520 static float color1[4] = {0.0f, 1.0f, 0.0f, 1.0f};
1521 static float color2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
1522 static float src_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
1523
1524 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1525 {
1526 ImGui::ListBox("Blending mode", &current_blend_index,
1527 blend_mode_names.data(), blend_mode_names.size());
1528 ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
1529 ImGui::ColorEdit4("Color A", color0);
1530 ImGui::ColorEdit4("Color B", color1);
1531 ImGui::ColorEdit4("Color C", color2);
1532 ImGui::ColorEdit4("Source Color", src_color);
1533 ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
1534 }
1535 ImGui::End();
1536
1537 std::vector<DlPoint> positions = {DlPoint(100, 300), //
1538 DlPoint(200, 100), //
1539 DlPoint(300, 300)};
1540 std::vector<flutter::DlColor> colors = {
1541 toColor(color0).modulateOpacity(dst_alpha),
1542 toColor(color1).modulateOpacity(dst_alpha),
1543 toColor(color2).modulateOpacity(dst_alpha)};
1544
1545 auto vertices = flutter::DlVertices::Make(
1546 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1547 /*texture_coordinates=*/nullptr, colors.data());
1548
1550 flutter::DlPaint paint;
1551
1552 paint.setColor(toColor(src_color).modulateOpacity(src_alpha));
1553 builder.DrawVertices(vertices, blend_mode_values[current_blend_index],
1554 paint);
1555 return builder.Build();
1556 };
1557
1558 ASSERT_TRUE(OpenPlaygroundHere(callback));
1559}
1560
1561TEST_P(DisplayListTest, DrawPaintIgnoresMaskFilter) {
1564
1566 builder.DrawCircle(DlPoint(300, 300), 200,
1567 flutter::DlPaint().setMaskFilter(&filter));
1568
1569 std::vector<flutter::DlColor> colors = {flutter::DlColor::kGreen(),
1571 const float stops[2] = {0.0, 1.0};
1573 {100.0, 100.0}, {300.0, 300.0}, 2, colors.data(), stops,
1575 flutter::DlPaint blend_paint =
1576 flutter::DlPaint() //
1577 .setColorSource(linear) //
1578 .setBlendMode(flutter::DlBlendMode::kScreen);
1579 builder.DrawPaint(blend_paint);
1580
1581 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1582}
1583
1584TEST_P(DisplayListTest, DrawMaskBlursThatMightUseSaveLayers) {
1586 builder.DrawColor(flutter::DlColor::kWhite(), flutter::DlBlendMode::kSrc);
1587 Vector2 scale = GetContentScale();
1588 builder.Scale(scale.x, scale.y);
1589
1590 builder.Save();
1591 // We need a small transform op to avoid a deferred save
1592 builder.Translate(1.0f, 1.0f);
1593 auto solid_filter =
1595 flutter::DlPaint solid_alpha_paint =
1596 flutter::DlPaint() //
1597 .setMaskFilter(solid_filter) //
1599 .setAlpha(0x7f);
1600 for (int x = 1; x <= 4; x++) {
1601 for (int y = 1; y <= 4; y++) {
1602 builder.DrawRect(DlRect::MakeXYWH(x * 100, y * 100, 80, 80),
1603 solid_alpha_paint);
1604 }
1605 }
1606 builder.Restore();
1607
1608 builder.Save();
1609 builder.Translate(500.0f, 0.0f);
1610 auto normal_filter =
1612 auto rotate_if = flutter::DlMatrixImageFilter::Make(
1614 flutter::DlPaint normal_if_paint =
1615 flutter::DlPaint() //
1616 .setMaskFilter(solid_filter) //
1617 .setImageFilter(rotate_if) //
1619 .setAlpha(0x7f);
1620 for (int x = 1; x <= 4; x++) {
1621 for (int y = 1; y <= 4; y++) {
1622 builder.DrawRect(DlRect::MakeXYWH(x * 100, y * 100, 80, 80),
1623 normal_if_paint);
1624 }
1625 }
1626 builder.Restore();
1627
1628 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1629}
1630
1631} // namespace testing
1632} // namespace impeller
void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawVertices(const std::shared_ptr< DlVertices > &vertices, DlBlendMode mode, const DlPaint &paint) override
void DrawImageNine(const sk_sp< DlImage > &image, const DlIRect &center, const DlRect &dst, DlFilterMode filter, const DlPaint *paint=nullptr) override
void DrawRoundRect(const DlRoundRect &rrect, const DlPaint &paint) override
void DrawArc(const DlRect &bounds, DlScalar start, DlScalar sweep, bool useCenter, const DlPaint &paint) override
void DrawShadow(const DlPath &path, const DlColor color, const DlScalar elevation, bool transparent_occluder, DlScalar dpr) override
Draws the shadow of the given |path| rendered in the provided |color| (which is only consulted for it...
void DrawImage(const sk_sp< DlImage > &image, const DlPoint &point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
void DrawColor(DlColor color, DlBlendMode mode) override
void DrawCircle(const DlPoint &center, DlScalar radius, const DlPaint &paint) override
void SaveLayer(const std::optional< DlRect > &bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt) override
void DrawLine(const DlPoint &p0, const DlPoint &p1, const DlPaint &paint) override
void ClipRoundRect(const DlRoundRect &rrect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawText(const std::shared_ptr< DlText > &text, DlScalar x, DlScalar y, const DlPaint &paint) override
void Scale(DlScalar sx, DlScalar sy) override
void Translate(DlScalar tx, DlScalar ty) override
void DrawPaint(const DlPaint &paint) override
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawPath(const DlPath &path, const DlPaint &paint) override
void DrawPoints(DlPointMode mode, uint32_t count, const DlPoint pts[], const DlPaint &paint) override
void DrawDiffRoundRect(const DlRoundRect &outer, const DlRoundRect &inner, const DlPaint &paint) override
void Transform(const DlMatrix &matrix) override
void DrawRect(const DlRect &rect, const DlPaint &paint) override
std::shared_ptr< DlImageFilter > shared() const override
static std::shared_ptr< DlMaskFilter > Make(DlBlurStyle style, SkScalar sigma, bool respect_ctm=true)
static std::shared_ptr< const DlColorFilter > MakeBlend(DlColor color, DlBlendMode mode)
static std::shared_ptr< const DlColorFilter > MakeLinearToSrgbGamma()
static std::shared_ptr< const DlColorFilter > MakeMatrix(const float matrix[20])
static std::shared_ptr< const DlColorFilter > MakeSrgbToLinearGamma()
static std::shared_ptr< DlColorSource > MakeSweep(DlPoint center, DlScalar start, DlScalar end, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const DlMatrix *matrix=nullptr)
static std::shared_ptr< DlColorSource > MakeImage(const sk_sp< const DlImage > &image, DlTileMode horizontal_tile_mode, DlTileMode vertical_tile_mode, DlImageSampling sampling=DlImageSampling::kLinear, const DlMatrix *matrix=nullptr)
static std::shared_ptr< DlColorSource > MakeLinear(const DlPoint start_point, const DlPoint end_point, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const DlMatrix *matrix=nullptr)
static std::shared_ptr< DlColorSource > MakeRadial(DlPoint center, DlScalar radius, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const DlMatrix *matrix=nullptr)
static std::shared_ptr< DlImageFilter > MakeColorFilter(const std::shared_ptr< const DlColorFilter > &filter)
static std::shared_ptr< DlImageFilter > Make(const DlMatrix &matrix, DlImageSampling sampling)
DlPaint & setColor(DlColor color)
Definition dl_paint.h:70
DlPaint & setStrokeCap(DlStrokeCap cap)
Definition dl_paint.h:101
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:115
DlPaint & setAlpha(uint8_t alpha)
Definition dl_paint.h:76
DlPaint & setStrokeMiter(float miter)
Definition dl_paint.h:121
DlPaint & setBlendMode(DlBlendMode mode)
Definition dl_paint.h:85
DlPaint & setImageFilter(std::nullptr_t filter)
Definition dl_paint.h:167
DlPaint & setMaskFilter(std::nullptr_t filter)
Definition dl_paint.h:185
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition dl_paint.h:109
DlPaint & setColorFilter(std::nullptr_t filter)
Definition dl_paint.h:149
DlPaint & setColorSource(std::nullptr_t source)
Definition dl_paint.h:131
DlPathBuilder & LineTo(DlPoint p2)
Draw a line from the current point to the indicated point p2.
DlPathBuilder & MoveTo(DlPoint p2)
Start a new contour that will originate at the indicated point p2.
DlPathBuilder & SetFillType(DlPathFillType fill_type)
Set the fill type that should be used to determine the interior of this path to the indicated |fill_t...
const DlPath TakePath()
Returns the path constructed by this path builder and resets its internal state to the default state ...
DlPathBuilder & AddCircle(DlPoint center, DlScalar radius)
Append a closed circular contour to the path centered on the provided point at the provided radius.
DlPathBuilder & QuadraticCurveTo(DlPoint cp, DlPoint p2)
Draw a quadratic bezier curve from the current point to the indicated point p2, using the indicated p...
DlPathBuilder & CubicCurveTo(DlPoint cp1, DlPoint cp2, DlPoint p2)
Draw a cubic bezier curve from the current point to the indicated point p2, using the indicated point...
static DlPath MakeLine(const DlPoint a, const DlPoint b)
Definition dl_path.cc:89
static DlPath MakeCircle(const DlPoint center, DlScalar radius)
Definition dl_path.cc:68
static DlPath MakeRect(const DlRect &rect)
Definition dl_path.cc:39
static DlPath MakeRoundRectXY(const DlRect &rect, DlScalar x_radius, DlScalar y_radius, bool counter_clock_wise=false)
Definition dl_path.cc:76
static DlPath MakePoly(const DlPoint pts[], int count, bool close, DlPathFillType fill_type=DlPathFillType::kNonZero)
Definition dl_path.cc:93
static std::shared_ptr< DlTextSkia > Make(const sk_sp< SkTextBlob > &blob)
static std::shared_ptr< DlVertices > Make(DlVertexMode mode, int vertex_count, const DlPoint vertices[], const DlPoint texture_coordinates[], const DlColor colors[], int index_count=0, const uint16_t indices[]=nullptr, const DlRect *bounds=nullptr)
Constructs a DlVector with compact inline storage for all of its required and optional lists of data.
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
int32_t x
FlutterVulkanImage * image
FlutterDesktopBinaryReply callback
std::u16string text
FlTexture * texture
double y
impeller::Scalar DlScalar
DlStrokeJoin
Definition dl_paint.h:37
@ kMiter
extends to miter limit
@ kBevel
connects outside edges
DlStrokeCap
Definition dl_paint.h:28
@ kRound
adds circle
@ kButt
no stroke extension
@ kSquare
adds square
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
@ kLines
draw each separate pair of points as a line segment
@ kPolygon
draw each pair of overlapping points as a line segment
@ kPoints
draw each point separately
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
DEF_SWITCHES_START aot vmservice shared library name
Definition switch_defs.h:27
@ kTriangles
The vertices are taken 3 at a time to form a triangle.
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
static constexpr DlScalar kPi
@ kNormal
fuzzy inside and outside
@ kOuter
nothing inside, fuzzy outside
@ kSolid
solid inside, fuzzy outside
impeller::Point DlPoint
TEST_P(AiksTest, DrawAtlasNoColor)
flutter::DlColor toColor(const float *components)
Point DrawPlaygroundPoint(PlaygroundPoint &point)
Definition widgets.cc:11
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition widgets.cc:51
#define INSTANTIATE_PLAYGROUND_SUITE(playground)
static constexpr DlColor kWhite()
Definition dl_color.h:70
static constexpr DlColor kBlue()
Definition dl_color.h:73
static constexpr DlColor kBlack()
Definition dl_color.h:69
static constexpr DlColor kYellow()
Definition dl_color.h:76
static constexpr DlColor kMidGrey()
Definition dl_color.h:78
static constexpr DlColor kRed()
Definition dl_color.h:71
static constexpr DlColor kGreen()
Definition dl_color.h:72
static constexpr DlColor kCyan()
Definition dl_color.h:74
constexpr DlColor modulateOpacity(DlScalar opacity) const
Definition dl_color.h:150
static uint32_t ToIColor(Color color)
Convert this color to a 32-bit representation.
Definition color.h:159
static constexpr Color White()
Definition color.h:264
static constexpr Color Red()
Definition color.h:272
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
constexpr Matrix Translate(const Vector3 &t) const
Definition matrix.h:263
static constexpr Matrix MakeRow(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition matrix.h:83
constexpr Matrix Scale(const Vector3 &s) const
Definition matrix.h:275
static Matrix MakeRotationZ(Radians r)
Definition matrix.h:223
static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition round_rect.h:31
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
std::vector< Point > points