Flutter Engine
The Flutter Engine
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
10#include "flutter/display_list/dl_blend_mode.h"
11#include "flutter/display_list/dl_builder.h"
12#include "flutter/display_list/dl_color.h"
13#include "flutter/display_list/dl_paint.h"
14#include "flutter/display_list/dl_tile_mode.h"
15#include "flutter/display_list/effects/dl_color_filter.h"
16#include "flutter/display_list/effects/dl_color_source.h"
17#include "flutter/display_list/effects/dl_image_filter.h"
18#include "flutter/display_list/effects/dl_mask_filter.h"
19#include "flutter/testing/testing.h"
20#include "gtest/gtest.h"
31#include "impeller/scene/node.h"
32#include "third_party/imgui/imgui.h"
37
38namespace impeller {
39namespace testing {
40
41flutter::DlColor toColor(const float* components) {
43 Color(components[0], components[1], components[2], components[3])));
44}
45
48
49TEST_P(DisplayListTest, CanDrawRect) {
51 builder.DrawRect(SkRect::MakeXYWH(10, 10, 100, 100),
53 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
54}
55
56TEST_P(DisplayListTest, CanDrawTextBlob) {
58 builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
60 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
61}
62
63TEST_P(DisplayListTest, CanDrawTextBlobWithGradient) {
65
66 std::vector<flutter::DlColor> colors = {flutter::DlColor::kBlue(),
68 const float stops[2] = {0.0, 1.0};
69
70 auto linear = flutter::DlColorSource::MakeLinear({0.0, 0.0}, {300.0, 300.0},
71 2, colors.data(), stops,
74 paint.setColorSource(linear);
75
76 builder.DrawTextBlob(
77 SkTextBlob::MakeFromString("Hello World", CreateTestFont()), 100, 100,
78 paint);
79 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
80}
81
82TEST_P(DisplayListTest, CanDrawTextWithSaveLayer) {
84 builder.DrawTextBlob(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(nullptr, &save_paint);
91 builder.DrawTextBlob(SkTextBlob::MakeFromString("Hello with half alpha",
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) {
109
111 paint.setStrokeWidth(30);
112 paint.setColor(flutter::DlColor::kRed());
113
114 auto path =
115 SkPathBuilder{}.moveTo(-50, 0).lineTo(0, -50).lineTo(50, 0).snapshot();
116
117 builder.Translate(100, 100);
118 {
121 paint.setStrokeMiter(4);
122 builder.DrawPath(path, paint);
123 }
124
125 {
126 builder.Save();
127 builder.Translate(0, 100);
128 // The joint in the path is 45 degrees. A miter length of 1 convert to a
129 // bevel in this case.
130 paint.setStrokeMiter(1);
131 builder.DrawPath(path, paint);
132 builder.Restore();
133 }
134
135 builder.Translate(150, 0);
136 {
139 builder.DrawPath(path, paint);
140 }
141
142 builder.Translate(150, 0);
143 {
146 builder.DrawPath(path, paint);
147 }
148
149 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
150}
151
153 auto callback = [&]() {
154 static float start_angle = 45;
155 static float sweep_angle = 270;
156 static float stroke_width = 10;
157 static bool use_center = true;
158
159 static int selected_cap = 0;
160 const char* cap_names[] = {"Butt", "Round", "Square"};
162
163 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
164 ImGui::SliderFloat("Start angle", &start_angle, -360, 360);
165 ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360);
166 ImGui::SliderFloat("Stroke width", &stroke_width, 0, 300);
167 ImGui::Combo("Cap", &selected_cap, cap_names,
168 sizeof(cap_names) / sizeof(char*));
169 ImGui::Checkbox("Use center", &use_center);
170 ImGui::End();
171
172 switch (selected_cap) {
173 case 0:
175 break;
176 case 1:
178 break;
179 case 2:
181 break;
182 default:
184 break;
185 }
186
187 static PlaygroundPoint point_a(Point(200, 200), 20, Color::White());
188 static PlaygroundPoint point_b(Point(400, 400), 20, Color::White());
189 auto [p1, p2] = DrawPlaygroundLine(point_a, point_b);
190
193
194 Vector2 scale = GetContentScale();
195 builder.Scale(scale.x, scale.y);
197 paint.setStrokeCap(cap);
199 paint.setStrokeMiter(10);
200 auto rect = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
202 paint.setStrokeWidth(2);
203 builder.DrawRect(rect, paint);
204 paint.setColor(flutter::DlColor::kRed());
205 paint.setStrokeWidth(stroke_width);
206 builder.DrawArc(rect, start_angle, sweep_angle, use_center, paint);
207
208 return builder.Build();
209 };
210 ASSERT_TRUE(OpenPlaygroundHere(callback));
211}
212
213TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) {
214 auto callback = [&]() {
217
218 paint.setColor(flutter::DlColor::kRed());
220
221 static float stroke_width = 10.0f;
222 static int selected_stroke_type = 0;
223 static int selected_join_type = 0;
224 const char* stroke_types[] = {"Butte", "Round", "Square"};
225 const char* join_type[] = {"kMiter", "Round", "kBevel"};
226
227 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
228 ImGui::Combo("Cap", &selected_stroke_type, stroke_types,
229 sizeof(stroke_types) / sizeof(char*));
230 ImGui::Combo("Join", &selected_join_type, join_type,
231 sizeof(join_type) / sizeof(char*));
232 ImGui::SliderFloat("Stroke Width", &stroke_width, 10.0f, 50.0f);
233 ImGui::End();
234
237 switch (selected_stroke_type) {
238 case 0:
240 break;
241 case 1:
243 break;
244 case 2:
246 break;
247 default:
249 break;
250 }
251 switch (selected_join_type) {
252 case 0:
254 break;
255 case 1:
257 break;
258 case 2:
260 break;
261 default:
263 break;
264 }
265 paint.setStrokeCap(cap);
266 paint.setStrokeJoin(join);
267 paint.setStrokeWidth(stroke_width);
268
269 // Make rendering better to watch.
270 builder.Scale(1.5f, 1.5f);
271
272 // Rectangle
273 builder.Translate(100, 100);
274 builder.DrawRect(SkRect::MakeSize({100, 100}), paint);
275
276 // Rounded rectangle
277 builder.Translate(150, 0);
278 builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10),
279 paint);
280
281 // Double rounded rectangle
282 builder.Translate(150, 0);
283 builder.DrawDRRect(
284 SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10),
285 SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10), paint);
286
287 // Contour with duplicate join points
288 {
289 builder.Translate(150, 0);
290 SkPath path;
291 path.moveTo(0, 0);
292 path.lineTo(0, 0);
293 path.lineTo({100, 0});
294 path.lineTo({100, 0});
295 path.lineTo({100, 100});
296 builder.DrawPath(path, paint);
297 }
298
299 // Contour with duplicate start and end points
300
301 // Line.
302 builder.Translate(200, 0);
303 {
304 builder.Save();
305
306 SkPath line_path;
307 line_path.moveTo(0, 0);
308 line_path.moveTo(0, 0);
309 line_path.lineTo({0, 0});
310 line_path.lineTo({0, 0});
311 line_path.lineTo({50, 50});
312 line_path.lineTo({50, 50});
313 line_path.lineTo({100, 0});
314 line_path.lineTo({100, 0});
315 builder.DrawPath(line_path, paint);
316
317 builder.Translate(0, 100);
318 builder.DrawPath(line_path, paint);
319
320 builder.Translate(0, 100);
321 SkPath line_path2;
322 line_path2.moveTo(0, 0);
323 line_path2.lineTo(0, 0);
324 line_path2.lineTo(0, 0);
325 builder.DrawPath(line_path2, paint);
326
327 builder.Restore();
328 }
329
330 // Cubic.
331 builder.Translate(150, 0);
332 {
333 builder.Save();
334
336 cubic_path.moveTo({0, 0});
337 cubic_path.cubicTo(0, 0, 140.0, 100.0, 140, 20);
338 builder.DrawPath(cubic_path, paint);
339
340 builder.Translate(0, 100);
341 SkPath cubic_path2;
342 cubic_path2.moveTo({0, 0});
343 cubic_path2.cubicTo(0, 0, 0, 0, 150, 150);
344 builder.DrawPath(cubic_path2, paint);
345
346 builder.Translate(0, 100);
347 SkPath cubic_path3;
348 cubic_path3.moveTo({0, 0});
349 cubic_path3.cubicTo(0, 0, 0, 0, 0, 0);
350 builder.DrawPath(cubic_path3, paint);
351
352 builder.Restore();
353 }
354
355 // Quad.
356 builder.Translate(200, 0);
357 {
358 builder.Save();
359
361 quad_path.moveTo(0, 0);
362 quad_path.moveTo(0, 0);
363 quad_path.quadTo({100, 40}, {50, 80});
364 builder.DrawPath(quad_path, paint);
365
366 builder.Translate(0, 150);
367 SkPath quad_path2;
368 quad_path2.moveTo(0, 0);
369 quad_path2.moveTo(0, 0);
370 quad_path2.quadTo({0, 0}, {100, 100});
371 builder.DrawPath(quad_path2, paint);
372
373 builder.Translate(0, 100);
374 SkPath quad_path3;
375 quad_path3.moveTo(0, 0);
376 quad_path3.quadTo({0, 0}, {0, 0});
377 builder.DrawPath(quad_path3, paint);
378
379 builder.Restore();
380 }
381 return builder.Build();
382 };
383 ASSERT_TRUE(OpenPlaygroundHere(callback));
384}
385
386TEST_P(DisplayListTest, CanDrawWithOddPathWinding) {
389
390 paint.setColor(flutter::DlColor::kRed());
392
393 builder.Translate(300, 300);
394 SkPath path;
395 path.setFillType(SkPathFillType::kEvenOdd);
396 path.addCircle(0, 0, 100);
397 path.addCircle(0, 0, 50);
398 builder.DrawPath(path, paint);
399
400 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
401}
402
403// Regression test for https://github.com/flutter/flutter/issues/134816.
404//
405// It should be possible to draw 3 lines, and not have an implicit close path.
406TEST_P(DisplayListTest, CanDrawAnOpenPath) {
409
410 paint.setColor(flutter::DlColor::kRed());
412 paint.setStrokeWidth(10);
413
414 builder.Translate(300, 300);
415
416 // Move to (50, 50) and draw lines from:
417 // 1. (50, height)
418 // 2. (width, height)
419 // 3. (width, 50)
420 SkPath path;
421 path.moveTo(50, 50);
422 path.lineTo(50, 100);
423 path.lineTo(100, 100);
424 path.lineTo(100, 50);
425 builder.DrawPath(path, paint);
426
427 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
428}
429
430TEST_P(DisplayListTest, CanDrawWithMaskBlur) {
431 auto texture = CreateTextureForFixture("embarcadero.jpg");
434
435 // Mask blurred image.
436 {
437 auto filter =
439 paint.setMaskFilter(&filter);
442 }
443
444 // Mask blurred filled path.
445 {
447 auto filter =
449 paint.setMaskFilter(&filter);
450 builder.DrawArc(SkRect::MakeXYWH(410, 110, 100, 100), 45, 270, true, paint);
451 }
452
453 // Mask blurred text.
454 {
455 auto filter =
457 paint.setMaskFilter(&filter);
458 builder.DrawTextBlob(
459 SkTextBlob::MakeFromString("Testing", CreateTestFont()), 220, 170,
460 paint);
461 }
462
463 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
464}
465
466TEST_P(DisplayListTest, CanDrawStrokedText) {
469
471 paint.setColor(flutter::DlColor::kRed());
472 builder.DrawTextBlob(
473 SkTextBlob::MakeFromString("stoked about stroked text", CreateTestFont()),
474 250, 250, paint);
475
476 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
477}
478
479// Regression test for https://github.com/flutter/flutter/issues/133157.
480TEST_P(DisplayListTest, StrokedTextNotOffsetFromNormalText) {
483 auto const& text_blob = SkTextBlob::MakeFromString("00000", CreateTestFont());
484
485 // https://api.flutter.dev/flutter/material/Colors/blue-constant.html.
486 auto const& mat_blue = flutter::DlColor(0xFF2196f3);
487
488 // Draw a blue filled rectangle so the text is easier to see.
490 paint.setColor(mat_blue);
491 builder.DrawRect(SkRect::MakeXYWH(0, 0, 500, 500), paint);
492
493 // Draw stacked text, with stroked text on top.
496 builder.DrawTextBlob(text_blob, 250, 250, paint);
497
500 builder.DrawTextBlob(text_blob, 250, 250, paint);
501
502 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
503}
504
505TEST_P(DisplayListTest, IgnoreMaskFilterWhenSavingLayer) {
506 auto texture = CreateTextureForFixture("embarcadero.jpg");
510 paint.setMaskFilter(&filter);
511 builder.SaveLayer(nullptr, &paint);
514 builder.Restore();
515 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
516}
517
518TEST_P(DisplayListTest, CanDrawWithBlendColorFilter) {
519 auto texture = CreateTextureForFixture("embarcadero.jpg");
522
523 // Pipeline blended image.
524 {
527 paint.setColorFilter(&filter);
530 }
531
532 // Advanced blended image.
533 {
536 paint.setColorFilter(&filter);
539 }
540
541 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
542}
543
544TEST_P(DisplayListTest, CanDrawWithColorFilterImageFilter) {
545 const float invert_color_matrix[20] = {
546 -1, 0, 0, 0, 1, //
547 0, -1, 0, 0, 1, //
548 0, 0, -1, 0, 1, //
549 0, 0, 0, 1, 0, //
550 };
551 auto texture = CreateTextureForFixture("boston.jpg");
554
555 auto color_filter =
556 std::make_shared<flutter::DlMatrixColorFilter>(invert_color_matrix);
557 auto image_filter =
558 std::make_shared<flutter::DlColorFilterImageFilter>(color_filter);
559
560 paint.setImageFilter(image_filter.get());
563
564 builder.Translate(0, 700);
565 paint.setColorFilter(color_filter.get());
568 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
569}
570
571TEST_P(DisplayListTest, CanDrawWithImageBlurFilter) {
572 auto texture = CreateTextureForFixture("embarcadero.jpg");
573
574 auto callback = [&]() {
575 static float sigma[] = {10, 10};
576
577 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
578 ImGui::SliderFloat2("Sigma", sigma, 0, 100);
579 ImGui::End();
580
583
584 auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
586 paint.setImageFilter(&filter);
589
590 return builder.Build();
591 };
592
593 ASSERT_TRUE(OpenPlaygroundHere(callback));
594}
595
596TEST_P(DisplayListTest, CanDrawWithComposeImageFilter) {
597 auto texture = CreateTextureForFixture("boston.jpg");
600
601 auto dilate = std::make_shared<flutter::DlDilateImageFilter>(10.0, 10.0);
602 auto erode = std::make_shared<flutter::DlErodeImageFilter>(10.0, 10.0);
603 auto open = std::make_shared<flutter::DlComposeImageFilter>(dilate, erode);
604 auto close = std::make_shared<flutter::DlComposeImageFilter>(erode, dilate);
605
606 paint.setImageFilter(open.get());
609 builder.Translate(0, 700);
610 paint.setImageFilter(close.get());
613 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
614}
615
616TEST_P(DisplayListTest, CanClampTheResultingColorOfColorMatrixFilter) {
617 auto texture = CreateTextureForFixture("boston.jpg");
618 const float inner_color_matrix[20] = {
619 1, 0, 0, 0, 0, //
620 0, 1, 0, 0, 0, //
621 0, 0, 1, 0, 0, //
622 0, 0, 0, 2, 0, //
623 };
624 const float outer_color_matrix[20] = {
625 1, 0, 0, 0, 0, //
626 0, 1, 0, 0, 0, //
627 0, 0, 1, 0, 0, //
628 0, 0, 0, 0.5, 0, //
629 };
630 auto inner_color_filter =
631 std::make_shared<flutter::DlMatrixColorFilter>(inner_color_matrix);
632 auto outer_color_filter =
633 std::make_shared<flutter::DlMatrixColorFilter>(outer_color_matrix);
634 auto inner =
635 std::make_shared<flutter::DlColorFilterImageFilter>(inner_color_filter);
636 auto outer =
637 std::make_shared<flutter::DlColorFilterImageFilter>(outer_color_filter);
638 auto compose = std::make_shared<flutter::DlComposeImageFilter>(outer, inner);
639
642 paint.setImageFilter(compose.get());
645 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
646}
647
648TEST_P(DisplayListTest, CanDrawBackdropFilter) {
649 auto texture = CreateTextureForFixture("embarcadero.jpg");
650
651 auto callback = [&]() {
652 static float sigma[] = {10, 10};
653 static float ctm_scale = 1;
654 static bool use_bounds = true;
655 static bool draw_circle = true;
656 static bool add_clip = true;
657
658 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
659 ImGui::SliderFloat2("Sigma", sigma, 0, 100);
660 ImGui::SliderFloat("Scale", &ctm_scale, 0, 10);
661 ImGui::NewLine();
662 ImGui::TextWrapped(
663 "If everything is working correctly, none of the options below should "
664 "impact the filter's appearance.");
665 ImGui::Checkbox("Use SaveLayer bounds", &use_bounds);
666 ImGui::Checkbox("Draw child element", &draw_circle);
667 ImGui::Checkbox("Add pre-clip", &add_clip);
668 ImGui::End();
669
671
672 Vector2 scale = ctm_scale * GetContentScale();
673 builder.Scale(scale.x, scale.y);
674
675 auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
677
678 std::optional<SkRect> bounds;
679 if (use_bounds) {
680 static PlaygroundPoint point_a(Point(350, 150), 20, Color::White());
681 static PlaygroundPoint point_b(Point(800, 600), 20, Color::White());
682 auto [p1, p2] = DrawPlaygroundLine(point_a, point_b);
683 bounds = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
684 }
685
686 // Insert a clip to test that the backdrop filter handles stencil depths > 0
687 // correctly.
688 if (add_clip) {
689 builder.ClipRect(SkRect::MakeLTRB(0, 0, 99999, 99999),
691 }
692
695 builder.SaveLayer(bounds.has_value() ? &bounds.value() : nullptr, nullptr,
696 &filter);
697
698 if (draw_circle) {
699 static PlaygroundPoint center_point(Point(500, 400), 20, Color::Red());
700 auto circle_center = DrawPlaygroundPoint(center_point);
701
706 paint.setStrokeWidth(10);
707 paint.setColor(flutter::DlColor::kRed().withAlpha(100));
708 builder.DrawCircle({circle_center.x, circle_center.y}, 100, paint);
709 }
710
711 return builder.Build();
712 };
713
714 ASSERT_TRUE(OpenPlaygroundHere(callback));
715}
716
717TEST_P(DisplayListTest, CanDrawNinePatchImage) {
718 // Image is drawn with corners to scale and center pieces stretched to fit.
719 auto texture = CreateTextureForFixture("embarcadero.jpg");
721 auto size = texture->GetSize();
722 builder.DrawImageNine(
724 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
725 size.height * 3 / 4),
726 SkRect::MakeLTRB(0, 0, size.width * 2, size.height * 2),
728 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
729}
730
731TEST_P(DisplayListTest, CanDrawNinePatchImageCenterWidthBiggerThanDest) {
732 // Edge case, the width of the corners does not leave any room for the
733 // center slice. The center (across the vertical axis) is folded out of the
734 // resulting image.
735 auto texture = CreateTextureForFixture("embarcadero.jpg");
737 auto size = texture->GetSize();
738 builder.DrawImageNine(
740 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
741 size.height * 3 / 4),
742 SkRect::MakeLTRB(0, 0, size.width / 2, size.height),
744 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
745}
746
747TEST_P(DisplayListTest, CanDrawNinePatchImageCenterHeightBiggerThanDest) {
748 // Edge case, the height of the corners does not leave any room for the
749 // center slice. The center (across the horizontal axis) is folded out of the
750 // resulting image.
751 auto texture = CreateTextureForFixture("embarcadero.jpg");
753 auto size = texture->GetSize();
754 builder.DrawImageNine(
756 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
757 size.height * 3 / 4),
758 SkRect::MakeLTRB(0, 0, size.width, size.height / 2),
760 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
761}
762
763TEST_P(DisplayListTest, CanDrawNinePatchImageCenterBiggerThanDest) {
764 // Edge case, the width and height of the corners does not leave any
765 // room for the center slices. Only the corners are displayed.
766 auto texture = CreateTextureForFixture("embarcadero.jpg");
768 auto size = texture->GetSize();
769 builder.DrawImageNine(
771 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
772 size.height * 3 / 4),
773 SkRect::MakeLTRB(0, 0, size.width / 2, size.height / 2),
775 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
776}
777
778TEST_P(DisplayListTest, CanDrawNinePatchImageCornersScaledDown) {
779 // Edge case, there is not enough room for the corners to be drawn
780 // without scaling them down.
781 auto texture = CreateTextureForFixture("embarcadero.jpg");
783 auto size = texture->GetSize();
784 builder.DrawImageNine(
786 SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
787 size.height * 3 / 4),
788 SkRect::MakeLTRB(0, 0, size.width / 4, size.height / 4),
790 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
791}
792
793TEST_P(DisplayListTest, NinePatchImagePrecision) {
794 // Draw a nine patch image with colored corners and verify that the corner
795 // color does not leak outside the intended region.
796 auto texture = CreateTextureForFixture("nine_patch_corners.png");
799 SkIRect::MakeXYWH(10, 10, 1, 1),
800 SkRect::MakeXYWH(0, 0, 200, 100),
802 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
803}
804
805TEST_P(DisplayListTest, CanDrawPoints) {
807 SkPoint points[7] = {
808 {0, 0}, //
809 {100, 100}, //
810 {100, 0}, //
811 {0, 100}, //
812 {0, 0}, //
813 {48, 48}, //
814 {52, 52}, //
815 };
816 std::vector<flutter::DlStrokeCap> caps = {
820 };
823 .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
824 .setStrokeWidth(20);
825 builder.Translate(50, 50);
826 for (auto cap : caps) {
827 paint.setStrokeCap(cap);
828 builder.Save();
830 builder.Translate(150, 0);
832 builder.Translate(150, 0);
834 paint);
835 builder.Restore();
836 builder.Translate(0, 150);
837 }
838 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
839}
840
841TEST_P(DisplayListTest, CanDrawZeroLengthLine) {
843 std::vector<flutter::DlStrokeCap> caps = {
847 };
850 .setColor(flutter::DlColor::kYellow().withAlpha(127)) //
853 .setStrokeWidth(20);
854 SkPath path = SkPath().addPoly({{150, 50}, {150, 50}}, false);
855 for (auto cap : caps) {
856 paint.setStrokeCap(cap);
857 builder.DrawLine({50, 50}, {50, 50}, paint);
858 builder.DrawPath(path, paint);
859 builder.Translate(0, 150);
860 }
861 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
862}
863
864TEST_P(DisplayListTest, CanDrawShadow) {
867
868 auto content_scale = GetContentScale() * 0.8;
869 builder.Scale(content_scale.x, content_scale.y);
870
871 constexpr size_t star_spikes = 5;
872 constexpr SkScalar half_spike_rotation = kPi / star_spikes;
873 constexpr SkScalar radius = 40;
874 constexpr SkScalar spike_size = 10;
875 constexpr SkScalar outer_radius = radius + spike_size;
876 constexpr SkScalar inner_radius = radius - spike_size;
877 std::array<SkPoint, star_spikes * 2> star;
878 for (size_t i = 0; i < star_spikes; i++) {
879 const SkScalar rotation = half_spike_rotation * i * 2;
880 star[i * 2] = SkPoint::Make(50 + std::sin(rotation) * outer_radius,
881 50 - std::cos(rotation) * outer_radius);
882 star[i * 2 + 1] = SkPoint::Make(
883 50 + std::sin(rotation + half_spike_rotation) * inner_radius,
884 50 - std::cos(rotation + half_spike_rotation) * inner_radius);
885 }
886
887 std::array<SkPath, 4> paths = {
888 SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100)),
890 SkRRect::MakeRectXY(SkRect::MakeXYWH(20, 0, 200, 100), 30, 30)),
891 SkPath{}.addCircle(100, 50, 50),
892 SkPath{}.addPoly(star.data(), star.size(), true),
893 };
895 builder.DrawPaint(paint);
896 paint.setColor(flutter::DlColor::kCyan());
897 builder.Translate(100, 50);
898 for (size_t x = 0; x < paths.size(); x++) {
899 builder.Save();
900 for (size_t y = 0; y < 6; y++) {
901 builder.DrawShadow(paths[x], flutter::DlColor::kBlack(), 3 + y * 8, false,
902 1);
903 builder.DrawPath(paths[x], paint);
904 builder.Translate(0, 150);
905 }
906 builder.Restore();
907 builder.Translate(250, 0);
908 }
909
910 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
911}
912
914 DispatcherDoesNotCullPerspectiveTransformedChildDisplayLists) {
915 // Regression test for https://github.com/flutter/flutter/issues/130613
916 flutter::DisplayListBuilder sub_builder(true);
917 sub_builder.DrawRect(SkRect::MakeXYWH(0, 0, 50, 50),
919 auto display_list = sub_builder.Build();
920
921 DlDispatcher dispatcher(Rect::MakeLTRB(0, 0, 2400, 1800));
922 dispatcher.scale(2.0, 2.0);
923 dispatcher.translate(-93.0, 0.0);
924 // clang-format off
925 dispatcher.transformFullPerspective(
926 0.8, -0.2, -0.1, -0.0,
927 0.0, 1.0, 0.0, 0.0,
928 1.4, 1.3, 1.0, 0.0,
929 63.2, 65.3, 48.6, 1.1
930 );
931 // clang-format on
932 dispatcher.translate(35.0, 75.0);
933 dispatcher.drawDisplayList(display_list, 1.0f);
934 auto picture = dispatcher.EndRecordingAsPicture();
935
936 bool found = false;
937 picture.pass->IterateAllEntities([&found](Entity& entity) {
938 if (std::static_pointer_cast<SolidColorContents>(entity.GetContents())
939 ->GetColor() == Color::Red()) {
940 found = true;
941 return false;
942 }
943
944 return true;
945 });
946 EXPECT_TRUE(found);
947}
948
949TEST_P(DisplayListTest, TransparentShadowProducesCorrectColor) {
950 DlDispatcher dispatcher;
951 dispatcher.save();
952 dispatcher.scale(1.618, 1.618);
953 SkPath path = SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100));
955 dispatcher.drawShadow(cache, flutter::DlColor::kTransparent(), 15, false, 1);
956 dispatcher.restore();
957 auto picture = dispatcher.EndRecordingAsPicture();
958
959 std::shared_ptr<SolidRRectBlurContents> rrect_blur;
960 picture.pass->IterateAllEntities([&rrect_blur](Entity& entity) {
961 if (ScalarNearlyEqual(entity.GetTransform().GetScale().x, 1.618f)) {
962 rrect_blur = std::static_pointer_cast<SolidRRectBlurContents>(
963 entity.GetContents());
964 return false;
965 }
966 return true;
967 });
968
969 ASSERT_NE(rrect_blur, nullptr);
970 ASSERT_EQ(rrect_blur->GetColor().red, 0);
971 ASSERT_EQ(rrect_blur->GetColor().green, 0);
972 ASSERT_EQ(rrect_blur->GetColor().blue, 0);
973 ASSERT_EQ(rrect_blur->GetColor().alpha, 0);
974}
975
976TEST_P(DisplayListTest, CanDrawZeroWidthLine) {
978 std::vector<flutter::DlStrokeCap> caps = {
982 };
987 .setStrokeWidth(0);
988 flutter::DlPaint outline_paint = //
993 .setStrokeWidth(1);
994 SkPath path = SkPath().addPoly({{150, 50}, {160, 50}}, false);
995 for (auto cap : caps) {
996 paint.setStrokeCap(cap);
997 builder.DrawLine({50, 50}, {60, 50}, paint);
998 builder.DrawRect({45, 45, 65, 55}, outline_paint);
999 builder.DrawLine({100, 50}, {100, 50}, paint);
1000 if (cap != flutter::DlStrokeCap::kButt) {
1001 builder.DrawRect({95, 45, 105, 55}, outline_paint);
1002 }
1003 builder.DrawPath(path, paint);
1004 builder.DrawRect(path.getBounds().makeOutset(5, 5), outline_paint);
1005 builder.Translate(0, 150);
1006 }
1007 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1008}
1009
1010TEST_P(DisplayListTest, CanDrawWithMatrixFilter) {
1011 auto boston = CreateTextureForFixture("boston.jpg");
1012
1013 auto callback = [&]() {
1014 static int selected_matrix_type = 0;
1015 const char* matrix_type_names[] = {"Matrix", "Local Matrix"};
1016
1017 static float ctm_translation[2] = {200, 200};
1018 static float ctm_scale[2] = {0.65, 0.65};
1019 static float ctm_skew[2] = {0, 0};
1020
1021 static bool enable = true;
1022 static float translation[2] = {100, 100};
1023 static float scale[2] = {0.8, 0.8};
1024 static float skew[2] = {0.2, 0.2};
1025
1026 static bool enable_savelayer = true;
1027
1028 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1029 {
1030 ImGui::Combo("Filter type", &selected_matrix_type, matrix_type_names,
1031 sizeof(matrix_type_names) / sizeof(char*));
1032
1033 ImGui::TextWrapped("Current Transform");
1034 ImGui::SliderFloat2("CTM Translation", ctm_translation, 0, 1000);
1035 ImGui::SliderFloat2("CTM Scale", ctm_scale, 0, 3);
1036 ImGui::SliderFloat2("CTM Skew", ctm_skew, -3, 3);
1037
1038 ImGui::TextWrapped(
1039 "MatrixFilter and LocalMatrixFilter modify the CTM in the same way. "
1040 "The only difference is that MatrixFilter doesn't affect the effect "
1041 "transform, whereas LocalMatrixFilter does.");
1042 // Note: See this behavior in:
1043 // https://fiddle.skia.org/c/6cbb551ab36d06f163db8693972be954
1044 ImGui::Checkbox("Enable", &enable);
1045 ImGui::SliderFloat2("Filter Translation", translation, 0, 1000);
1046 ImGui::SliderFloat2("Filter Scale", scale, 0, 3);
1047 ImGui::SliderFloat2("Filter Skew", skew, -3, 3);
1048
1049 ImGui::TextWrapped(
1050 "Rendering the filtered image within a layer can expose bounds "
1051 "issues. If the rendered image gets cut off when this setting is "
1052 "enabled, there's a coverage bug in the filter.");
1053 ImGui::Checkbox("Render in layer", &enable_savelayer);
1054 }
1055 ImGui::End();
1056
1059
1060 if (enable_savelayer) {
1061 builder.SaveLayer(nullptr, nullptr);
1062 }
1063 {
1064 auto content_scale = GetContentScale();
1065 builder.Scale(content_scale.x, content_scale.y);
1066
1067 // Set the current transform
1068 auto ctm_matrix =
1069 SkMatrix::MakeAll(ctm_scale[0], ctm_skew[0], ctm_translation[0], //
1070 ctm_skew[1], ctm_scale[1], ctm_translation[1], //
1071 0, 0, 1);
1072 builder.Transform(ctm_matrix);
1073
1074 // Set the matrix filter
1075 auto filter_matrix =
1076 SkMatrix::MakeAll(scale[0], skew[0], translation[0], //
1077 skew[1], scale[1], translation[1], //
1078 0, 0, 1);
1079
1080 if (enable) {
1081 switch (selected_matrix_type) {
1082 case 0: {
1083 auto filter = flutter::DlMatrixImageFilter(
1084 filter_matrix, flutter::DlImageSampling::kLinear);
1085 paint.setImageFilter(&filter);
1086 break;
1087 }
1088 case 1: {
1089 auto internal_filter =
1091 .shared();
1092 auto filter = flutter::DlLocalMatrixImageFilter(filter_matrix,
1093 internal_filter);
1094 paint.setImageFilter(&filter);
1095 break;
1096 }
1097 }
1098 }
1099
1100 builder.DrawImage(DlImageImpeller::Make(boston), {},
1102 }
1103 if (enable_savelayer) {
1104 builder.Restore();
1105 }
1106
1107 return builder.Build();
1108 };
1109
1110 ASSERT_TRUE(OpenPlaygroundHere(callback));
1111}
1112
1113TEST_P(DisplayListTest, CanDrawWithMatrixFilterWhenSavingLayer) {
1114 auto callback = [&]() {
1115 static float translation[2] = {0, 0};
1116 static bool enable_save_layer = true;
1117
1118 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1119 ImGui::SliderFloat2("Translation", translation, -130, 130);
1120 ImGui::Checkbox("Enable save layer", &enable_save_layer);
1121 ImGui::End();
1122
1124 builder.Save();
1125 builder.Scale(2.0, 2.0);
1127 paint.setColor(flutter::DlColor::kYellow());
1128 builder.DrawRect(SkRect::MakeWH(300, 300), paint);
1129 paint.setStrokeWidth(1.0);
1131 paint.setColor(flutter::DlColor::kBlack().withAlpha(0x80));
1132 builder.DrawLine(SkPoint::Make(150, 0), SkPoint::Make(150, 300), paint);
1133 builder.DrawLine(SkPoint::Make(0, 150), SkPoint::Make(300, 150), paint);
1134
1135 flutter::DlPaint save_paint;
1136 SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
1137 SkMatrix translate_matrix =
1138 SkMatrix::Translate(translation[0], translation[1]);
1139 if (enable_save_layer) {
1140 auto filter = flutter::DlMatrixImageFilter(
1142 save_paint.setImageFilter(filter.shared());
1143 builder.SaveLayer(&bounds, &save_paint);
1144 } else {
1145 builder.Save();
1146 builder.Transform(translate_matrix);
1147 }
1148
1149 SkMatrix filter_matrix = SkMatrix::I();
1150 filter_matrix.postTranslate(-150, -150);
1151 filter_matrix.postScale(0.2f, 0.2f);
1152 filter_matrix.postTranslate(150, 150);
1153 auto filter = flutter::DlMatrixImageFilter(
1155
1156 save_paint.setImageFilter(filter.shared());
1157
1158 builder.SaveLayer(&bounds, &save_paint);
1159 flutter::DlPaint paint2;
1161 builder.DrawRect(bounds, paint2);
1162 builder.Restore();
1163 builder.Restore();
1164 return builder.Build();
1165 };
1166
1167 ASSERT_TRUE(OpenPlaygroundHere(callback));
1168}
1169
1170TEST_P(DisplayListTest, CanDrawRectWithLinearToSrgbColorFilter) {
1172 paint.setColor(flutter::DlColor(0xFF2196F3).withAlpha(128));
1174 paint.setColorFilter(
1176 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), paint);
1177 builder.Translate(0, 200);
1178
1179 paint.setColorFilter(
1181 builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), paint);
1182
1183 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1184}
1185
1186TEST_P(DisplayListTest, CanDrawPaintWithColorSource) {
1187 const flutter::DlColor colors[2] = {
1188 flutter::DlColor(0xFFF44336),
1189 flutter::DlColor(0xFF2196F3),
1190 };
1191 const float stops[2] = {0.0, 1.0};
1194 auto clip_bounds = SkRect::MakeWH(300.0, 300.0);
1195 builder.Save();
1196 builder.Translate(100, 100);
1197 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1198 auto linear =
1199 flutter::DlColorSource::MakeLinear({0.0, 0.0}, {100.0, 100.0}, 2, colors,
1201 paint.setColorSource(linear);
1202 builder.DrawPaint(paint);
1203 builder.Restore();
1204
1205 builder.Save();
1206 builder.Translate(500, 100);
1207 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1209 {100.0, 100.0}, 100.0, 2, colors, stops, flutter::DlTileMode::kRepeat);
1210 paint.setColorSource(radial);
1211 builder.DrawPaint(paint);
1212 builder.Restore();
1213
1214 builder.Save();
1215 builder.Translate(100, 500);
1216 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1217 auto sweep =
1218 flutter::DlColorSource::MakeSweep({100.0, 100.0}, 180.0, 270.0, 2, colors,
1220 paint.setColorSource(sweep);
1221 builder.DrawPaint(paint);
1222 builder.Restore();
1223
1224 builder.Save();
1225 builder.Translate(500, 500);
1226 builder.ClipRect(clip_bounds, flutter::DlCanvas::ClipOp::kIntersect, false);
1227 auto texture = CreateTextureForFixture("table_mountain_nx.png");
1228 auto image = std::make_shared<flutter::DlImageColorSource>(
1231 paint.setColorSource(image);
1232 builder.DrawPaint(paint);
1233 builder.Restore();
1234
1235 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1236}
1237
1238TEST_P(DisplayListTest, CanBlendDstOverAndDstCorrectly) {
1240
1241 {
1242 builder.SaveLayer(nullptr, nullptr);
1243 builder.Translate(100, 100);
1245 paint.setColor(flutter::DlColor::kRed());
1246 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1247 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1249 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1250 builder.Restore();
1251 }
1252 {
1253 builder.SaveLayer(nullptr, nullptr);
1254 builder.Translate(300, 100);
1256 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1257 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1258 paint.setColor(flutter::DlColor::kRed());
1260 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1261 builder.Restore();
1262 }
1263 {
1264 builder.SaveLayer(nullptr, nullptr);
1265 builder.Translate(100, 300);
1267 paint.setColor(flutter::DlColor::kRed());
1268 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1269 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1270 paint.setBlendMode(flutter::DlBlendMode::kSrc);
1271 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1272 builder.Restore();
1273 }
1274 {
1275 builder.SaveLayer(nullptr, nullptr);
1276 builder.Translate(300, 300);
1278 paint.setColor(flutter::DlColor::kBlue().withAlpha(127));
1279 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1280 paint.setColor(flutter::DlColor::kRed());
1281 paint.setBlendMode(flutter::DlBlendMode::kDst);
1282 builder.DrawRect(SkRect::MakeSize({200, 200}), paint);
1283 builder.Restore();
1284 }
1285
1286 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1287}
1288
1289TEST_P(DisplayListTest, CanDrawCorrectlyWithColorFilterAndImageFilter) {
1291 const float green_color_matrix[20] = {
1292 0, 0, 0, 0, 0, //
1293 0, 0, 0, 0, 1, //
1294 0, 0, 0, 0, 0, //
1295 0, 0, 0, 1, 0, //
1296 };
1297 const float blue_color_matrix[20] = {
1298 0, 0, 0, 0, 0, //
1299 0, 0, 0, 0, 0, //
1300 0, 0, 0, 0, 1, //
1301 0, 0, 0, 1, 0, //
1302 };
1303 auto green_color_filter =
1304 std::make_shared<flutter::DlMatrixColorFilter>(green_color_matrix);
1305 auto blue_color_filter =
1306 std::make_shared<flutter::DlMatrixColorFilter>(blue_color_matrix);
1307 auto blue_image_filter =
1308 std::make_shared<flutter::DlColorFilterImageFilter>(blue_color_filter);
1309
1311 paint.setColor(flutter::DlColor::kRed());
1312 paint.setColorFilter(green_color_filter);
1313 paint.setImageFilter(blue_image_filter);
1314 builder.DrawRect(SkRect::MakeLTRB(100, 100, 500, 500), paint);
1315 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1316}
1317
1318TEST_P(DisplayListTest, MaskBlursApplyCorrectlyToColorSources) {
1319 auto blur_filter = std::make_shared<flutter::DlBlurMaskFilter>(
1321
1323
1324 std::array<flutter::DlColor, 2> colors = {flutter::DlColor::kBlue(),
1326 std::array<float, 2> stops = {0, 1};
1327 std::array<std::shared_ptr<flutter::DlColorSource>, 2> color_sources = {
1328 std::make_shared<flutter::DlColorColorSource>(flutter::DlColor::kWhite()),
1330 SkPoint::Make(0, 0), SkPoint::Make(100, 50), 2, colors.data(),
1331 stops.data(), flutter::DlTileMode::kClamp)};
1332
1333 int offset = 100;
1334 for (const auto& color_source : color_sources) {
1336 paint.setColorSource(color_source);
1337 paint.setMaskFilter(blur_filter);
1338
1339 paint.setDrawStyle(flutter::DlDrawStyle::kFill);
1340 builder.DrawRRect(
1341 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, offset, 100, 50), 30, 30),
1342 paint);
1344 paint.setStrokeWidth(10);
1345 builder.DrawRRect(
1346 SkRRect::MakeRectXY(SkRect::MakeXYWH(300, offset, 100, 50), 30, 30),
1347 paint);
1348
1349 offset += 100;
1350 }
1351
1352 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1353}
1354
1357 std::vector<flutter::DlStrokeJoin> joins = {
1361 };
1363 flutter::DlPaint() //
1366 .setStrokeWidth(10);
1367 flutter::DlPaint stroke_paint = //
1368 flutter::DlPaint() //
1371 .setStrokeWidth(10);
1372 SkPath path = SkPath().addPoly({{150, 50}, {160, 50}}, false);
1373
1374 builder.Translate(300, 50);
1375 builder.Scale(0.8, 0.8);
1376 for (auto join : joins) {
1377 paint.setStrokeJoin(join);
1378 stroke_paint.setStrokeJoin(join);
1379 builder.DrawRect(SkRect::MakeXYWH(0, 0, 100, 100), paint);
1380 builder.DrawRect(SkRect::MakeXYWH(0, 150, 100, 100), stroke_paint);
1381 builder.DrawRRect(
1382 SkRRect::MakeRectXY(SkRect::MakeXYWH(150, 0, 100, 100), 30, 30), paint);
1383 builder.DrawRRect(
1384 SkRRect::MakeRectXY(SkRect::MakeXYWH(150, 150, 100, 100), 30, 30),
1385 stroke_paint);
1386 builder.DrawCircle({350, 50}, 50, paint);
1387 builder.DrawCircle({350, 200}, 50, stroke_paint);
1388 builder.Translate(0, 300);
1389 }
1390 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1391}
1392
1393TEST_P(DisplayListTest, ClipDrawRRectWithNonCircularRadii) {
1395
1396 flutter::DlPaint fill_paint = //
1397 flutter::DlPaint() //
1400 .setStrokeWidth(10);
1401 flutter::DlPaint stroke_paint = //
1402 flutter::DlPaint() //
1405 .setStrokeWidth(10);
1406
1407 builder.DrawRRect(
1408 SkRRect::MakeRectXY(SkRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1409 fill_paint);
1410 builder.DrawRRect(
1411 SkRRect::MakeRectXY(SkRect::MakeXYWH(500, 100, 300, 300), 120, 40),
1412 stroke_paint);
1413
1414 builder.DrawRRect(
1415 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1416 fill_paint);
1417 builder.DrawRRect(
1418 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 500, 300, 300), 40, 120),
1419 stroke_paint);
1420
1421 flutter::DlPaint reference_paint = //
1422 flutter::DlPaint() //
1425 .setStrokeWidth(10);
1426
1427 builder.DrawRRect(
1428 SkRRect::MakeRectXY(SkRect::MakeXYWH(500, 500, 300, 300), 40, 40),
1429 reference_paint);
1430 builder.DrawRRect(
1431 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 100, 300, 300), 120, 120),
1432 reference_paint);
1433
1434 flutter::DlPaint clip_fill_paint = //
1435 flutter::DlPaint() //
1438 .setStrokeWidth(10);
1439
1440 builder.Save();
1441 builder.ClipRRect(
1442 SkRRect::MakeRectXY(SkRect::MakeXYWH(900, 100, 300, 300), 120, 40));
1443 builder.DrawPaint(clip_fill_paint);
1444 builder.Restore();
1445
1446 builder.Save();
1447 builder.ClipRRect(
1448 SkRRect::MakeRectXY(SkRect::MakeXYWH(100, 900, 300, 300), 40, 120));
1449 builder.DrawPaint(clip_fill_paint);
1450 builder.Restore();
1451
1452 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1453}
1454
1455TEST_P(DisplayListTest, DrawVerticesBlendModes) {
1456 std::vector<const char*> blend_mode_names;
1457 std::vector<flutter::DlBlendMode> blend_mode_values;
1458 {
1459 const std::vector<std::tuple<const char*, flutter::DlBlendMode>> blends = {
1460 // Pipeline blends (Porter-Duff alpha compositing)
1462 {"Source", flutter::DlBlendMode::kSrc},
1463 {"Destination", flutter::DlBlendMode::kDst},
1464 {"SourceOver", flutter::DlBlendMode::kSrcOver},
1465 {"DestinationOver", flutter::DlBlendMode::kDstOver},
1466 {"SourceIn", flutter::DlBlendMode::kSrcIn},
1467 {"DestinationIn", flutter::DlBlendMode::kDstIn},
1468 {"SourceOut", flutter::DlBlendMode::kSrcOut},
1469 {"DestinationOut", flutter::DlBlendMode::kDstOut},
1470 {"SourceATop", flutter::DlBlendMode::kSrcATop},
1471 {"DestinationATop", flutter::DlBlendMode::kDstATop},
1474 {"Modulate", flutter::DlBlendMode::kModulate},
1475 // Advanced blends (color component blends)
1477 {"Overlay", flutter::DlBlendMode::kOverlay},
1479 {"Lighten", flutter::DlBlendMode::kLighten},
1480 {"ColorDodge", flutter::DlBlendMode::kColorDodge},
1481 {"ColorBurn", flutter::DlBlendMode::kColorBurn},
1482 {"HardLight", flutter::DlBlendMode::kHardLight},
1483 {"SoftLight", flutter::DlBlendMode::kSoftLight},
1484 {"Difference", flutter::DlBlendMode::kDifference},
1485 {"Exclusion", flutter::DlBlendMode::kExclusion},
1486 {"Multiply", flutter::DlBlendMode::kMultiply},
1488 {"Saturation", flutter::DlBlendMode::kSaturation},
1490 {"Luminosity", flutter::DlBlendMode::kLuminosity},
1491 };
1492 assert(blends.size() ==
1493 static_cast<size_t>(flutter::DlBlendMode::kLastMode) + 1);
1494 for (const auto& [name, mode] : blends) {
1495 blend_mode_names.push_back(name);
1496 blend_mode_values.push_back(mode);
1497 }
1498 }
1499
1500 auto callback = [&]() {
1501 static int current_blend_index = 3;
1502 static float dst_alpha = 1;
1503 static float src_alpha = 1;
1504 static float color0[4] = {1.0f, 0.0f, 0.0f, 1.0f};
1505 static float color1[4] = {0.0f, 1.0f, 0.0f, 1.0f};
1506 static float color2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
1507 static float src_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
1508
1509 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1510 {
1511 ImGui::ListBox("Blending mode", &current_blend_index,
1512 blend_mode_names.data(), blend_mode_names.size());
1513 ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
1514 ImGui::ColorEdit4("Color A", color0);
1515 ImGui::ColorEdit4("Color B", color1);
1516 ImGui::ColorEdit4("Color C", color2);
1517 ImGui::ColorEdit4("Source Color", src_color);
1518 ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
1519 }
1520 ImGui::End();
1521
1522 std::vector<SkPoint> positions = {SkPoint::Make(100, 300),
1523 SkPoint::Make(200, 100),
1524 SkPoint::Make(300, 300)};
1525 std::vector<flutter::DlColor> colors = {
1526 toColor(color0).modulateOpacity(dst_alpha),
1527 toColor(color1).modulateOpacity(dst_alpha),
1528 toColor(color2).modulateOpacity(dst_alpha)};
1529
1530 auto vertices = flutter::DlVertices::Make(
1531 flutter::DlVertexMode::kTriangles, 3, positions.data(),
1532 /*texture_coordinates=*/nullptr, colors.data());
1533
1536
1537 paint.setColor(toColor(src_color).modulateOpacity(src_alpha));
1538 builder.DrawVertices(vertices, blend_mode_values[current_blend_index],
1539 paint);
1540 return builder.Build();
1541 };
1542
1543 ASSERT_TRUE(OpenPlaygroundHere(callback));
1544}
1545
1546template <typename Contents>
1547static std::optional<Rect> GetCoverageOfFirstEntity(const Picture& picture) {
1548 std::optional<Rect> coverage;
1549 picture.pass->IterateAllEntities([&coverage](Entity& entity) {
1550 if (std::static_pointer_cast<Contents>(entity.GetContents())) {
1551 auto contents = std::static_pointer_cast<Contents>(entity.GetContents());
1552 Entity entity;
1553 coverage = contents->GetCoverage(entity);
1554 return false;
1555 }
1556 return true;
1557 });
1558 return coverage;
1559}
1560
1561TEST(DisplayListTest, RRectBoundsComputation) {
1562 SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeLTRB(0, 0, 100, 100), 4, 4);
1564
1567
1568 builder.DrawPath(path, paint);
1569 auto display_list = builder.Build();
1570
1571 DlDispatcher dispatcher;
1572 display_list->Dispatch(dispatcher);
1573 auto picture = dispatcher.EndRecordingAsPicture();
1574
1575 std::optional<Rect> coverage =
1576 GetCoverageOfFirstEntity<SolidColorContents>(picture);
1577
1578 // Validate that the RRect coverage is _exactly_ the same as the input rect.
1579 ASSERT_TRUE(coverage.has_value());
1580 ASSERT_EQ(coverage.value_or(Rect::MakeMaximum()),
1581 Rect::MakeLTRB(0, 0, 100, 100));
1582}
1583
1584TEST(DisplayListTest, CircleBoundsComputation) {
1585 SkPath path = SkPath().addCircle(0, 0, 5);
1586
1589
1590 builder.DrawPath(path, paint);
1591 auto display_list = builder.Build();
1592
1593 DlDispatcher dispatcher;
1594 display_list->Dispatch(dispatcher);
1595 auto picture = dispatcher.EndRecordingAsPicture();
1596
1597 std::optional<Rect> coverage =
1598 GetCoverageOfFirstEntity<SolidColorContents>(picture);
1599
1600 ASSERT_TRUE(coverage.has_value());
1601 ASSERT_EQ(coverage.value_or(Rect::MakeMaximum()),
1602 Rect::MakeLTRB(-5, -5, 5, 5));
1603}
1604
1605#ifdef IMPELLER_ENABLE_3D
1606TEST_P(DisplayListTest, SceneColorSource) {
1607 // Load up the scene.
1608 auto mapping =
1609 flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene");
1610 ASSERT_NE(mapping, nullptr);
1611
1612 std::shared_ptr<scene::Node> gltf_scene =
1614 *mapping, *GetContext()->GetResourceAllocator());
1615 ASSERT_NE(gltf_scene, nullptr);
1616
1618
1619 auto color_source = std::make_shared<flutter::DlSceneColorSource>(
1620 gltf_scene,
1621 Matrix::MakePerspective(Degrees(45), GetWindowSize(), 0.1, 1000) *
1622 Matrix::MakeLookAt({3, 2, -5}, {0, 0, 0}, {0, 1, 0}));
1623
1625
1626 builder.DrawPaint(paint);
1627
1628 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1629}
1630#endif
1631
1632TEST_P(DisplayListTest, DrawPaintIgnoresMaskFilter) {
1634 builder.DrawPaint(flutter::DlPaint().setColor(flutter::DlColor::kWhite()));
1635
1637 builder.DrawCircle({300, 300}, 200,
1638 flutter::DlPaint().setMaskFilter(&filter));
1639
1640 std::vector<flutter::DlColor> colors = {flutter::DlColor::kGreen(),
1642 const float stops[2] = {0.0, 1.0};
1644 {100.0, 100.0}, {300.0, 300.0}, 2, colors.data(), stops,
1646 flutter::DlPaint blend_paint =
1647 flutter::DlPaint() //
1650 builder.DrawPaint(blend_paint);
1651
1652 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1653}
1654
1655TEST_P(DisplayListTest, DrawMaskBlursThatMightUseSaveLayers) {
1658 Vector2 scale = GetContentScale();
1659 builder.Scale(scale.x, scale.y);
1660
1661 builder.Save();
1662 // We need a small transform op to avoid a deferred save
1663 builder.Translate(1.0f, 1.0f);
1664 auto solid_filter =
1666 flutter::DlPaint solid_alpha_paint =
1667 flutter::DlPaint() //
1668 .setMaskFilter(solid_filter) //
1670 .setAlpha(0x7f);
1671 for (int x = 1; x <= 4; x++) {
1672 for (int y = 1; y <= 4; y++) {
1673 builder.DrawRect(SkRect::MakeXYWH(x * 100, y * 100, 80, 80),
1674 solid_alpha_paint);
1675 }
1676 }
1677 builder.Restore();
1678
1679 builder.Save();
1680 builder.Translate(500.0f, 0.0f);
1681 auto normal_filter =
1683 auto rotate_if = flutter::DlMatrixImageFilter::Make(
1685 flutter::DlPaint normal_if_paint =
1686 flutter::DlPaint() //
1687 .setMaskFilter(solid_filter) //
1688 .setImageFilter(rotate_if) //
1690 .setAlpha(0x7f);
1691 for (int x = 1; x <= 4; x++) {
1692 for (int y = 1; y <= 4; y++) {
1693 builder.DrawRect(SkRect::MakeXYWH(x * 100, y * 100, 80, 80),
1694 normal_if_paint);
1695 }
1696 }
1697 builder.Restore();
1698
1699 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1700}
1701
1702} // namespace testing
1703} // namespace impeller
static const int points[]
static sk_sp< SkImage > color_filter(const SkImage *image, SkColorFilter *colorFilter)
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
static SkMatrix RotateDeg(SkScalar deg)
Definition: SkMatrix.h:104
SkMatrix & postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:360
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition: SkMatrix.h:179
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkPathBuilder & lineTo(SkPoint pt)
SkPathBuilder & moveTo(SkPoint pt)
SkPath snapshot() const
Definition: SkPath.h:59
SkPath & addCircle(SkScalar x, SkScalar y, SkScalar radius, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1213
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
SkPath & addPoly(const SkPoint pts[], int count, bool close)
Definition: SkPath.cpp:890
SkPath & lineTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:728
SkPath & addRRect(const SkRRect &rrect, SkPathDirection dir=SkPathDirection::kCW)
Definition: SkPath.cpp:1000
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Definition: SkPath.cpp:746
SkPath & cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3)
Definition: SkPath.cpp:799
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition: SkPath.cpp:864
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition: SkRRect.h:180
static sk_sp< SkTextBlob > MakeFromString(const char *string, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.h:115
void DrawRect(const SkRect &rect, const DlPaint &paint) override
Definition: dl_builder.cc:1116
sk_sp< DisplayList > Build()
Definition: dl_builder.cc:67
std::shared_ptr< DlImageFilter > shared() const override
static std::shared_ptr< DlMaskFilter > Make(DlBlurStyle style, SkScalar sigma, bool respect_ctm=true)
@ 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
static std::shared_ptr< DlLinearGradientColorSource > MakeLinear(const SkPoint start_point, const SkPoint end_point, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const SkMatrix *matrix=nullptr)
static std::shared_ptr< DlSweepGradientColorSource > MakeSweep(SkPoint center, SkScalar start, SkScalar end, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const SkMatrix *matrix=nullptr)
static std::shared_ptr< DlRadialGradientColorSource > MakeRadial(SkPoint center, SkScalar radius, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const SkMatrix *matrix=nullptr)
static const std::shared_ptr< DlLinearToSrgbGammaColorFilter > kInstance
static std::shared_ptr< DlImageFilter > Make(const SkMatrix &matrix, DlImageSampling sampling)
DlPaint & setColor(DlColor color)
Definition: dl_paint.h:70
DlPaint & setMaskFilter(const std::shared_ptr< DlMaskFilter > &filter)
Definition: dl_paint.h:170
DlPaint & setStrokeCap(DlStrokeCap cap)
Definition: dl_paint.h:102
DlPaint & setStrokeWidth(float width)
Definition: dl_paint.h:116
DlPaint & setAlpha(uint8_t alpha)
Definition: dl_paint.h:76
DlPaint & setBlendMode(DlBlendMode mode)
Definition: dl_paint.h:86
DlPaint & setImageFilter(const std::shared_ptr< const DlImageFilter > &filter)
Definition: dl_paint.h:157
DlPaint & setDrawStyle(DlDrawStyle style)
Definition: dl_paint.h:94
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition: dl_paint.h:110
DlPaint & setColorSource(std::shared_ptr< const DlColorSource > source)
Definition: dl_paint.h:131
static const std::shared_ptr< DlSrgbToLinearGammaColorFilter > kInstance
static std::shared_ptr< DlVertices > Make(DlVertexMode mode, int vertex_count, const SkPoint vertices[], const SkPoint texture_coordinates[], const DlColor colors[], int index_count=0, const uint16_t indices[]=nullptr)
Constructs a DlVector with compact inline storage for all of its required and optional lists of data.
Definition: dl_vertices.cc:39
void transformFullPerspective(SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override
void translate(SkScalar tx, SkScalar ty) override
void drawShadow(const SkPath &path, const flutter::DlColor color, const SkScalar elevation, bool transparent_occluder, SkScalar dpr) override
void scale(SkScalar sx, SkScalar sy) override
void drawDisplayList(const sk_sp< flutter::DisplayList > display_list, SkScalar opacity) override
void save() override
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:94
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition: node.cc:47
T * get() const
Definition: SkRefCnt.h:303
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlTexture * texture
double y
double x
SK_API GrDirectContext * GetContext(const SkImage *src)
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
PODArray< SkColor > colors
Definition: SkRecords.h:276
SK_API sk_sp< SkShader > Color(SkColor)
SkFont CreateTestFontOfSize(SkScalar scalar)
std::unique_ptr< fml::Mapping > OpenFixtureAsMapping(const std::string &fixture_name)
Opens a fixture of the given file name and returns a mapping to its contents.
Definition: testing.cc:59
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
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
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: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
@ kTriangles
The vertices are taken 3 at a time to form a triangle.
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes
@ kNormal
fuzzy inside and outside
@ kOuter
nothing inside, fuzzy outside
@ kSolid
solid inside, fuzzy outside
it will be possible to load the file into Perfetto s trace viewer 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
Definition: switches.h:228
it will be possible to load the file into Perfetto s trace viewer 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
Definition: switches.h:259
@ kSrcOut
r = s * (1-da)
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
@ kSaturation
saturation of source with hue and luminosity of destination
@ kColorBurn
darken destination to reflect source
@ kPlus
r = min(s + d, 1)
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kHue
hue of source with saturation and luminosity of destination
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kColorDodge
brighten destination to reflect source
@ kScreen
r = s + d - s*d
@ kSrcOver
r = s + (1-sa)*d
@ kXor
r = s*(1-da) + d*(1-sa)
@ kLuminosity
luminosity of source with hue and saturation of destination
@ kSoftLight
lighten or darken, depending on source
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kOverlay
multiply or screen, depending on destination
@ kSrcATop
r = s*da + d*(1-sa)
@ kDstATop
r = d*sa + s*(1-da)
@ kDstOver
r = d + (1-da)*s
@ kLastMode
last valid value
@ kColor
hue and saturation of source with luminosity of destination
@ kHardLight
multiply or screen, depending on source
@ kDstOut
r = d * (1-sa)
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
TEST(AiksCanvasTest, EmptyCullRect)
DlPlayground DisplayListTest
Definition: dl_unittests.cc:46
flutter::DlColor toColor(const float *components)
Definition: dl_unittests.cc:41
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
static std::optional< Rect > GetCoverageOfFirstEntity(const Picture &picture)
constexpr float kPi
Definition: constants.h:26
Point DrawPlaygroundPoint(PlaygroundPoint &point)
Definition: widgets.cc:9
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
TPoint< Scalar > Point
Definition: point.h:322
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
const myers::Point & get(const myers::Segment &)
flutter::DlColor DlColor
flutter::DlPaint DlPaint
SkPath quad_path()
Definition: overstroke.cpp:60
SkPath cubic_path()
Definition: overstroke.cpp:68
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
const Scalar stroke_width
const Scalar scale
SeparatedVector2 offset
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition: SkRect.h:91
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
static constexpr SkRect MakeSize(const SkSize &size)
Definition: SkRect.h:633
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
constexpr DlColor modulateOpacity(float opacity) const
Definition: dl_color.h:76
static constexpr DlColor kWhite()
Definition: dl_color.h:23
static constexpr DlColor kBlue()
Definition: dl_color.h:26
static constexpr DlColor kBlack()
Definition: dl_color.h:22
static constexpr DlColor kYellow()
Definition: dl_color.h:29
static constexpr DlColor kMidGrey()
Definition: dl_color.h:31
static constexpr DlColor kTransparent()
Definition: dl_color.h:21
static constexpr DlColor kRed()
Definition: dl_color.h:24
static constexpr DlColor kGreen()
Definition: dl_color.h:25
static constexpr DlColor kCyan()
Definition: dl_color.h:27
static constexpr uint32_t ToIColor(Color color)
Convert this color to a 32-bit representation.
Definition: color.h:161
static constexpr Color White()
Definition: color.h:266
static constexpr Color Red()
Definition: color.h:274
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:506
constexpr Vector3 GetScale() const
Definition: matrix.h:311
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:532
static constexpr TRect MakeMaximum()
Definition: rect.h:174
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678
static sk_sp< SkShader > linear(sk_sp< SkShader > shader)