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