Flutter Impeller
aiks_dl_gradient_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/display_list.h"
6 #include "display_list/dl_blend_mode.h"
7 #include "display_list/dl_tile_mode.h"
8 #include "display_list/effects/dl_color_filter.h"
9 #include "display_list/effects/dl_color_source.h"
10 #include "display_list/effects/dl_mask_filter.h"
11 #include "display_list/geometry/dl_path_builder.h"
13 
14 #include "flutter/display_list/dl_builder.h"
15 #include "flutter/display_list/dl_color.h"
16 #include "flutter/display_list/dl_paint.h"
17 #include "flutter/testing/testing.h"
19 
20 using namespace flutter;
21 ////////////////////////////////////////////////////////////////////////////////
22 // This is for tests of Canvas that are interested the results of rendering
23 // gradients.
24 ////////////////////////////////////////////////////////////////////////////////
25 
26 namespace impeller {
27 namespace testing {
28 
29 namespace {
30 
31 /// Test body for linear gradient tile mode tests (ex.
32 /// CanRenderLinearGradientClamp).
33 void CanRenderLinearGradient(AiksTest* aiks_test, DlTileMode tile_mode) {
34  DisplayListBuilder builder;
35  Point scale = aiks_test->GetContentScale();
36  builder.Scale(scale.x, scale.y);
37  DlPaint paint;
38  builder.Translate(100.0f, 0);
39 
40  std::vector<DlColor> colors = {
41  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
42  DlColor(Color{0.1294, 0.5882, 0.9529, 0.0}.ToARGB())};
43  std::vector<Scalar> stops = {0.0, 1.0};
44 
45  auto gradient = DlColorSource::MakeLinear(
46  {0, 0}, {200, 200}, 2, colors.data(), stops.data(), tile_mode);
47  paint.setColorSource(gradient);
48  paint.setColor(DlColor::kWhite());
49  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
50  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
51 }
52 
53 } // namespace
54 
55 TEST_P(AiksTest, CanRenderLinearGradientClamp) {
56  CanRenderLinearGradient(this, DlTileMode::kClamp);
57 }
58 TEST_P(AiksTest, CanRenderLinearGradientRepeat) {
59  CanRenderLinearGradient(this, DlTileMode::kRepeat);
60 }
61 TEST_P(AiksTest, CanRenderLinearGradientMirror) {
62  CanRenderLinearGradient(this, DlTileMode::kMirror);
63 }
64 TEST_P(AiksTest, CanRenderLinearGradientDecal) {
65  CanRenderLinearGradient(this, DlTileMode::kDecal);
66 }
67 
68 TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) {
69  DisplayListBuilder builder;
70  Point scale = GetContentScale();
71  builder.Scale(scale.x, scale.y);
72  DlPaint paint;
73  builder.Translate(100.0f, 0);
74 
75  std::vector<DlColor> colors = {
76  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
77  DlColor(Color{0.1294, 0.5882, 0.9529, 0.0}.ToARGB())};
78  std::vector<Scalar> stops = {0.0, 1.0};
79 
80  paint.setColorSource(DlColorSource::MakeLinear(
81  {0, 0}, {200, 200}, 2, colors.data(), stops.data(), DlTileMode::kDecal));
82  // Overlay the gradient with 25% green. This should appear as the entire
83  // rectangle being drawn with 25% green, including the border area outside the
84  // decal gradient.
85  paint.setColorFilter(DlColorFilter::MakeBlend(DlColor::kGreen().withAlpha(64),
86  DlBlendMode::kSrcOver));
87  paint.setColor(DlColor::kWhite());
88  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
89  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
90 }
91 
92 TEST_P(AiksTest, CanRenderLinearGradientWithImageFilter) {
93  DisplayListBuilder builder;
94  Point scale = GetContentScale();
95  builder.Scale(scale.x, scale.y);
96  DlPaint paint;
97  builder.Translate(100.0f, 0);
98 
99  std::vector<DlColor> colors = {
100  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
101  DlColor(Color{0.1294, 0.5882, 0.9529, 0.0}.ToARGB())};
102  std::vector<Scalar> stops = {0.0, 1.0};
103 
104  paint.setColorSource(DlColorSource::MakeLinear(
105  {0, 0}, {200, 200}, 2, colors.data(), stops.data(), DlTileMode::kClamp));
106  paint.setImageFilter(DlImageFilter::MakeBlur(20.0, 20.0, DlTileMode::kDecal));
107  paint.setColor(DlColor::kWhite());
108  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
109  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
110 }
111 
113  DisplayListBuilder builder;
114  DlPaint paint;
115  builder.Translate(100.0, 100.0);
116 
117  // 0xffcccccc --> 0xff333333, taken from
118  // https://github.com/flutter/flutter/issues/118073#issue-1521699748
119  std::vector<DlColor> colors = {DlColor(0xFFCCCCCC), DlColor(0xFF333333)};
120  std::vector<Scalar> stops = {0.0, 1.0};
121 
122  paint.setColorSource(DlColorSource::MakeLinear(
123  {0, 0}, {800, 500}, 2, colors.data(), stops.data(), DlTileMode::kClamp));
124  builder.DrawRect(DlRect::MakeXYWH(0, 0, 800, 500), paint);
125  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
126 }
127 
128 TEST_P(AiksTest, CanRenderLinearGradientWithDitheringEnabled) {
130 }
131 
133  DisplayListBuilder builder;
134  DlPaint paint;
135  builder.Translate(100.0, 100.0);
136 
137  // #FFF -> #000
138  std::vector<DlColor> colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()),
139  DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())};
140  std::vector<Scalar> stops = {0.0, 1.0};
141 
142  paint.setColorSource(DlColorSource::MakeRadial(
143  {600, 600}, 600, 2, colors.data(), stops.data(), DlTileMode::kClamp));
144  builder.DrawRect(DlRect::MakeXYWH(0, 0, 1200, 1200), paint);
145  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
146 }
147 
148 TEST_P(AiksTest, CanRenderRadialGradientWithDitheringEnabled) {
150 }
151 
153  DisplayListBuilder builder;
154  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
155  DlPaint paint;
156  builder.Translate(100.0, 100.0);
157 
158  // #FFF -> #000
159  std::vector<DlColor> colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()),
160  DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())};
161  std::vector<Scalar> stops = {0.0, 1.0};
162 
163  paint.setColorSource(DlColorSource::MakeSweep(
164  {100, 100}, /*start=*/45, /*end=*/135, 2, colors.data(), stops.data(),
165  DlTileMode::kMirror));
166 
167  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
168  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
169 }
170 
171 TEST_P(AiksTest, CanRenderSweepGradientWithDitheringEnabled) {
173 }
174 
176  DisplayListBuilder builder;
177  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
178  DlPaint paint;
179  builder.Translate(100.0, 100.0);
180 
181  // #FFF -> #000
182  std::vector<DlColor> colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()),
183  DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())};
184  std::vector<Scalar> stops = {0.0, 1.0};
185 
186  paint.setColorSource(DlColorSource::MakeConical({0, 1}, 0, {100, 100}, 100, 2,
187  colors.data(), stops.data(),
188  DlTileMode::kMirror));
189 
190  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
191  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
192 }
193 
194 TEST_P(AiksTest, CanRenderConicalGradientWithDitheringEnabled) {
196 }
197 
198 namespace {
199 void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test,
200  DlTileMode tile_mode) {
201  DisplayListBuilder builder;
202  DlPaint paint;
203  builder.Translate(100.0, 100.0);
204 
205  std::vector<DlColor> colors = {
206  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
207  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
208  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB()),
209  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
210  std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};
211 
212  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {500, 500},
213  stops.size(), colors.data(),
214  stops.data(), tile_mode));
215 
216  paint.setColor(DlColor::kWhite());
217  builder.DrawRect(DlRect::MakeXYWH(0, 0, 500, 500), paint);
218  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
219 }
220 } // namespace
221 
222 // Only clamp is necessary. All tile modes are the same output.
223 TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) {
224  CanRenderLinearGradientWithOverlappingStops(this, DlTileMode::kClamp);
225 }
226 
227 namespace {
228 void CanRenderGradientWithIncompleteStops(AiksTest* aiks_test,
229  DlColorSourceType type) {
230  const DlTileMode tile_modes[4] = {
231  DlTileMode::kClamp,
232  DlTileMode::kRepeat,
233  DlTileMode::kMirror,
234  DlTileMode::kDecal,
235  };
236  const DlScalar test_size = 250;
237  const DlScalar test_border = 25;
238  const DlScalar gradient_size = 50;
239  const DlScalar quadrant_size = test_size + test_border * 2;
240 
241  DisplayListBuilder builder;
242  builder.DrawRect(DlRect::MakeWH(quadrant_size * 2, quadrant_size * 2),
243  DlPaint().setColor(DlColor::kDarkGrey()));
244 
245  for (int quadrant = 0; quadrant < 4; quadrant++) {
246  builder.Save();
247  builder.Translate((quadrant & 1) * quadrant_size + test_border,
248  (quadrant >> 1) * quadrant_size + test_border);
249 
250  if (type == DlColorSourceType::kLinearGradient) {
251  // Alignment lines for the gradient edges/repeats/mirrors/etc.
252  // (rendered under the gradient so as not to obscure it)
253  DlPoint center = DlPoint(test_size, test_size) * 0.5;
254  DlScalar ten_percent = gradient_size * 0.1;
255  for (int i = gradient_size / 2; i <= test_size / 2; i += gradient_size) {
256  auto draw_at = [=](DlCanvas& canvas, DlScalar offset, DlColor color) {
257  DlPaint line_paint;
258  line_paint.setColor(color);
259  // strokewidth of 2 straddles the dividing line
260  line_paint.setStrokeWidth(2.0f);
261  line_paint.setDrawStyle(DlDrawStyle::kStroke);
262 
263  DlPoint along(offset, offset);
264  DlScalar across_distance = test_size / 2 + 10 - offset;
265  DlPoint across(across_distance, -across_distance);
266 
267  canvas.DrawLine(center - along - across, //
268  center - along + across, //
269  line_paint);
270  canvas.DrawLine(center + along - across, //
271  center + along + across, //
272  line_paint);
273  };
274  // White line is at the edge of the gradient
275  // Grey lines are where the 0.1 and 0.9 color stops land
276  draw_at(builder, i - ten_percent, DlColor::kMidGrey());
277  draw_at(builder, i, DlColor::kWhite());
278  draw_at(builder, i + ten_percent, DlColor::kMidGrey());
279  }
280  }
281 
282  std::vector<DlColor> colors = {
283  DlColor::kGreen(),
284  DlColor::kPurple(),
285  DlColor::kOrange(),
286  DlColor::kBlue(),
287  };
288  std::vector<Scalar> stops = {0.1, 0.3, 0.7, 0.9};
289 
290  DlPaint paint;
291  switch (type) {
292  case DlColorSourceType::kLinearGradient:
293  paint.setColorSource(DlColorSource::MakeLinear(
294  {test_size / 2 - gradient_size / 2,
295  test_size / 2 - gradient_size / 2},
296  {test_size / 2 + gradient_size / 2,
297  test_size / 2 + gradient_size / 2},
298  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
299  break;
300  case DlColorSourceType::kRadialGradient:
301  paint.setColorSource(DlColorSource::MakeRadial(
302  {test_size / 2, test_size / 2}, gradient_size, //
303  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
304  break;
305  case DlColorSourceType::kConicalGradient:
306  paint.setColorSource(DlColorSource::MakeConical(
307  {test_size / 2, test_size / 2}, 0,
308  {test_size / 2 + 20, test_size / 2 - 10}, gradient_size,
309  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
310  break;
311  case DlColorSourceType::kSweepGradient:
312  paint.setColorSource(DlColorSource::MakeSweep(
313  {test_size / 2, test_size / 2}, 0, 45, //
314  stops.size(), colors.data(), stops.data(), tile_modes[quadrant]));
315  break;
316  default:
317  FML_UNREACHABLE();
318  }
319 
320  builder.DrawRect(DlRect::MakeXYWH(0, 0, test_size, test_size), paint);
321  builder.Restore();
322  }
323 
324  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
325 }
326 } // namespace
327 
328 TEST_P(AiksTest, CanRenderLinearGradientWithIncompleteStops) {
329  CanRenderGradientWithIncompleteStops(this,
330  DlColorSourceType::kLinearGradient);
331 }
332 TEST_P(AiksTest, CanRenderRadialGradientWithIncompleteStops) {
333  CanRenderGradientWithIncompleteStops(this,
334  DlColorSourceType::kRadialGradient);
335 }
336 TEST_P(AiksTest, CanRenderConicalGradientWithIncompleteStops) {
337  CanRenderGradientWithIncompleteStops(this,
338  DlColorSourceType::kConicalGradient);
339 }
340 TEST_P(AiksTest, CanRenderSweepGradientWithIncompleteStops) {
341  CanRenderGradientWithIncompleteStops(this, DlColorSourceType::kSweepGradient);
342 }
343 
344 namespace {
345 void CanRenderLinearGradientManyColors(AiksTest* aiks_test,
346  DlTileMode tile_mode) {
347  DisplayListBuilder builder;
348  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
349  DlPaint paint;
350  builder.Translate(100, 100);
351 
352  std::vector<DlColor> colors = {
353  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
354  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
355  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
356  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
357  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
358  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
359  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
360  std::vector<Scalar> stops = {
361  0.0,
362  (1.0 / 6.0) * 1,
363  (1.0 / 6.0) * 2,
364  (1.0 / 6.0) * 3,
365  (1.0 / 6.0) * 4,
366  (1.0 / 6.0) * 5,
367  1.0,
368  };
369 
370  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200},
371  stops.size(), colors.data(),
372  stops.data(), tile_mode));
373 
374  paint.setColor(DlColor::kWhite());
375  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
376  builder.Restore();
377  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
378 }
379 } // namespace
380 
381 TEST_P(AiksTest, CanRenderLinearGradientManyColorsClamp) {
382  CanRenderLinearGradientManyColors(this, DlTileMode::kClamp);
383 }
384 TEST_P(AiksTest, CanRenderLinearGradientManyColorsRepeat) {
385  CanRenderLinearGradientManyColors(this, DlTileMode::kRepeat);
386 }
387 TEST_P(AiksTest, CanRenderLinearGradientManyColorsMirror) {
388  CanRenderLinearGradientManyColors(this, DlTileMode::kMirror);
389 }
390 TEST_P(AiksTest, CanRenderLinearGradientManyColorsDecal) {
391  CanRenderLinearGradientManyColors(this, DlTileMode::kDecal);
392 }
393 
394 namespace {
395 void CanRenderLinearGradientWayManyColors(AiksTest* aiks_test,
396  DlTileMode tile_mode) {
397  DisplayListBuilder builder;
398  DlPaint paint;
399  builder.Translate(100.0, 100.0);
400  auto color = DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB());
401  std::vector<DlColor> colors;
402  std::vector<Scalar> stops;
403  auto current_stop = 0.0;
404  for (int i = 0; i < 2000; i++) {
405  colors.push_back(color);
406  stops.push_back(current_stop);
407  current_stop += 1 / 2000.0;
408  }
409  stops[2000 - 1] = 1.0;
410 
411  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200},
412  stops.size(), colors.data(),
413  stops.data(), tile_mode));
414 
415  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
416  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
417 }
418 } // namespace
419 
420 // Only test clamp on purpose since they all look the same.
421 TEST_P(AiksTest, CanRenderLinearGradientWayManyColorsClamp) {
422  CanRenderLinearGradientWayManyColors(this, DlTileMode::kClamp);
423 }
424 
425 TEST_P(AiksTest, CanRenderLinearGradientManyColorsUnevenStops) {
426  auto callback = [&]() -> sk_sp<DisplayList> {
427  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
428  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
429  DlTileMode::kMirror, DlTileMode::kDecal};
430 
431  static int selected_tile_mode = 0;
432  static Matrix matrix;
433  if (AiksTest::ImGuiBegin("Controls", nullptr,
434  ImGuiWindowFlags_AlwaysAutoResize)) {
435  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
436  sizeof(tile_mode_names) / sizeof(char*));
437  std::string label = "##1";
438  for (int i = 0; i < 4; i++) {
439  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
440  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
441  label[2]++;
442  }
443  ImGui::End();
444  }
445 
446  DisplayListBuilder builder;
447  DlPaint paint;
448  builder.Translate(100.0, 100.0);
449  auto tile_mode = tile_modes[selected_tile_mode];
450 
451  std::vector<DlColor> colors = {
452  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
453  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
454  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
455  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
456  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
457  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
458  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
459  std::vector<Scalar> stops = {
460  0.0, 2.0 / 62.0, 4.0 / 62.0, 8.0 / 62.0, 16.0 / 62.0, 32.0 / 62.0, 1.0,
461  };
462 
463  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200},
464  stops.size(), colors.data(),
465  stops.data(), tile_mode));
466 
467  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
468  return builder.Build();
469  };
470  ASSERT_TRUE(OpenPlaygroundHere(callback));
471 }
472 
473 TEST_P(AiksTest, CanRenderLinearGradientMaskBlur) {
474  DisplayListBuilder builder;
475 
476  std::vector<DlColor> colors = {
477  DlColor::kRed(), DlColor::kWhite(), DlColor::kRed(), DlColor::kWhite(),
478  DlColor::kRed(), DlColor::kWhite(), DlColor::kRed(), DlColor::kWhite(),
479  DlColor::kRed(), DlColor::kWhite(), DlColor::kRed()};
480  std::vector<Scalar> stops = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5,
481  0.6, 0.7, 0.8, 0.9, 1.0};
482 
483  DlPaint paint;
484  paint.setColor(DlColor::kWhite());
485  paint.setColorSource(DlColorSource::MakeLinear(
486  {200, 200}, {400, 400}, stops.size(), colors.data(), stops.data(),
487  DlTileMode::kClamp));
488  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
489 
490  builder.DrawCircle(DlPoint(300, 300), 200, paint);
491  builder.DrawRect(DlRect::MakeLTRB(100, 300, 500, 600), paint);
492 
493  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
494 }
495 
496 TEST_P(AiksTest, CanRenderRadialGradient) {
497  auto callback = [&]() -> sk_sp<DisplayList> {
498  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
499  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
500  DlTileMode::kMirror, DlTileMode::kDecal};
501 
502  static int selected_tile_mode = 0;
503  static Matrix matrix;
504  if (AiksTest::ImGuiBegin("Controls", nullptr,
505  ImGuiWindowFlags_AlwaysAutoResize)) {
506  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
507  sizeof(tile_mode_names) / sizeof(char*));
508  std::string label = "##1";
509  for (int i = 0; i < 4; i++) {
510  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
511  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
512  label[2]++;
513  }
514  ImGui::End();
515  }
516 
517  DisplayListBuilder builder;
518  DlPaint paint;
519  builder.Translate(100.0, 100.0);
520  auto tile_mode = tile_modes[selected_tile_mode];
521 
522  std::vector<DlColor> colors = {
523  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
524  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
525  std::vector<Scalar> stops = {0.0, 1.0};
526 
527  paint.setColorSource(DlColorSource::MakeRadial(
528  {100, 100}, 100, 2, colors.data(), stops.data(), tile_mode));
529 
530  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
531  return builder.Build();
532  };
533  ASSERT_TRUE(OpenPlaygroundHere(callback));
534 }
535 
536 TEST_P(AiksTest, CanRenderRadialGradientManyColors) {
537  auto callback = [&]() -> sk_sp<DisplayList> {
538  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
539  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
540  DlTileMode::kMirror, DlTileMode::kDecal};
541 
542  static int selected_tile_mode = 0;
543  static Matrix matrix = {
544  1, 0, 0, 0, //
545  0, 1, 0, 0, //
546  0, 0, 1, 0, //
547  0, 0, 0, 1 //
548  };
549  if (AiksTest::ImGuiBegin("Controls", nullptr,
550  ImGuiWindowFlags_AlwaysAutoResize)) {
551  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
552  sizeof(tile_mode_names) / sizeof(char*));
553  std::string label = "##1";
554  for (int i = 0; i < 4; i++) {
555  ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
556  &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0);
557  label[2]++;
558  }
559  ImGui::End();
560  }
561 
562  DisplayListBuilder builder;
563  DlPaint paint;
564  builder.Translate(100.0, 100.0);
565  auto tile_mode = tile_modes[selected_tile_mode];
566 
567  std::vector<DlColor> colors = {
568  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
569  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
570  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
571  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
572  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
573  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
574  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
575  std::vector<Scalar> stops = {
576  0.0,
577  (1.0 / 6.0) * 1,
578  (1.0 / 6.0) * 2,
579  (1.0 / 6.0) * 3,
580  (1.0 / 6.0) * 4,
581  (1.0 / 6.0) * 5,
582  1.0,
583  };
584 
585  paint.setColorSource(DlColorSource::MakeRadial(
586  {100, 100}, 100, stops.size(), colors.data(), stops.data(), tile_mode));
587 
588  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
589  return builder.Build();
590  };
591  ASSERT_TRUE(OpenPlaygroundHere(callback));
592 }
593 
594 namespace {
595 void CanRenderSweepGradient(AiksTest* aiks_test, DlTileMode tile_mode) {
596  DisplayListBuilder builder;
597  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
598  DlPaint paint;
599  builder.Translate(100, 100);
600 
601  std::vector<DlColor> colors = {
602  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
603  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
604  std::vector<Scalar> stops = {0.0, 1.0};
605 
606  paint.setColorSource(DlColorSource::MakeSweep(
607  {100, 100}, /*start=*/45, /*end=*/135, /*stop_count=*/2, colors.data(),
608  stops.data(), tile_mode));
609 
610  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
611  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
612 }
613 } // namespace
614 
615 TEST_P(AiksTest, CanRenderSweepGradientClamp) {
616  CanRenderSweepGradient(this, DlTileMode::kClamp);
617 }
618 TEST_P(AiksTest, CanRenderSweepGradientRepeat) {
619  CanRenderSweepGradient(this, DlTileMode::kRepeat);
620 }
621 TEST_P(AiksTest, CanRenderSweepGradientMirror) {
622  CanRenderSweepGradient(this, DlTileMode::kMirror);
623 }
624 TEST_P(AiksTest, CanRenderSweepGradientDecal) {
625  CanRenderSweepGradient(this, DlTileMode::kDecal);
626 }
627 
628 namespace {
629 void CanRenderSweepGradientManyColors(AiksTest* aiks_test,
630  DlTileMode tile_mode) {
631  DisplayListBuilder builder;
632  DlPaint paint;
633  builder.Translate(100.0, 100.0);
634 
635  std::vector<DlColor> colors = {
636  DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()),
637  DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()),
638  DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
639  DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()),
640  DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()),
641  DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()),
642  DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())};
643  std::vector<Scalar> stops = {
644  0.0,
645  (1.0 / 6.0) * 1,
646  (1.0 / 6.0) * 2,
647  (1.0 / 6.0) * 3,
648  (1.0 / 6.0) * 4,
649  (1.0 / 6.0) * 5,
650  1.0,
651  };
652 
653  paint.setColorSource(DlColorSource::MakeSweep({100, 100}, 45, 135,
654  stops.size(), colors.data(),
655  stops.data(), tile_mode));
656 
657  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
658  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
659 }
660 } // namespace
661 
662 TEST_P(AiksTest, CanRenderSweepGradientManyColorsClamp) {
663  CanRenderSweepGradientManyColors(this, DlTileMode::kClamp);
664 }
665 TEST_P(AiksTest, CanRenderSweepGradientManyColorsRepeat) {
666  CanRenderSweepGradientManyColors(this, DlTileMode::kRepeat);
667 }
668 TEST_P(AiksTest, CanRenderSweepGradientManyColorsMirror) {
669  CanRenderSweepGradientManyColors(this, DlTileMode::kMirror);
670 }
671 TEST_P(AiksTest, CanRenderSweepGradientManyColorsDecal) {
672  CanRenderSweepGradientManyColors(this, DlTileMode::kDecal);
673 }
674 
675 TEST_P(AiksTest, CanRenderConicalGradient) {
676  Scalar size = 256;
677  DisplayListBuilder builder;
678  DlPaint paint;
679  paint.setColor(DlColor::kWhite());
680  builder.DrawRect(DlRect::MakeXYWH(0, 0, size * 3, size * 3), paint);
681  std::vector<DlColor> colors = {
682  DlColor(Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF).ToARGB()),
683  DlColor(Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF).ToARGB()),
684  DlColor(Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF).ToARGB()),
685  DlColor(Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF).ToARGB())};
686  std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
687  std::array<std::tuple<DlPoint, float, DlPoint, float>, 8> array{
688  std::make_tuple(DlPoint(size / 2.f, size / 2.f), 0.f,
689  DlPoint(size / 2.f, size / 2.f), size / 2.f),
690  std::make_tuple(DlPoint(size / 2.f, size / 2.f), size / 4.f,
691  DlPoint(size / 2.f, size / 2.f), size / 2.f),
692  std::make_tuple(DlPoint(size / 4.f, size / 4.f), 0.f,
693  DlPoint(size / 2.f, size / 2.f), size / 2.f),
694  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 2.f,
695  DlPoint(size / 2.f, size / 2.f), 0),
696  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 4.f,
697  DlPoint(size / 2.f, size / 2.f), size / 2.f),
698  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 16.f,
699  DlPoint(size / 2.f, size / 2.f), size / 8.f),
700  std::make_tuple(DlPoint(size / 4.f, size / 4.f), size / 8.f,
701  DlPoint(size / 2.f, size / 2.f), size / 16.f),
702  std::make_tuple(DlPoint(size / 8.f, size / 8.f), size / 8.f,
703  DlPoint(size / 2.f, size / 2.f), size / 8.f),
704  };
705  for (int i = 0; i < 8; i++) {
706  builder.Save();
707  builder.Translate((i % 3) * size, i / 3 * size);
708  paint.setColorSource(DlColorSource::MakeConical(
709  /*start_center=*/std::get<2>(array[i]),
710  /*start_radius=*/std::get<3>(array[i]),
711  /*end_center=*/std::get<0>(array[i]),
712  /*end_radius=*/std::get<1>(array[i]),
713  /*stop_count=*/stops.size(),
714  /*colors=*/colors.data(),
715  /*stops=*/stops.data(),
716  /*tile_mode=*/DlTileMode::kClamp));
717  builder.DrawRect(DlRect::MakeXYWH(0, 0, size, size), paint);
718  builder.Restore();
719  }
720  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
721 }
722 
723 TEST_P(AiksTest, CanRenderGradientDecalWithBackground) {
724  std::vector<DlColor> colors = {
725  DlColor(Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF).ToARGB()),
726  DlColor(Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF).ToARGB()),
727  DlColor(Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF).ToARGB()),
728  DlColor(Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF).ToARGB())};
729  std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
730 
731  std::array<std::shared_ptr<DlColorSource>, 3> color_sources = {
732  DlColorSource::MakeLinear({0, 0}, {100, 100}, stops.size(), colors.data(),
733  stops.data(), DlTileMode::kDecal),
734  DlColorSource::MakeRadial({100, 100}, 100, stops.size(), colors.data(),
735  stops.data(), DlTileMode::kDecal),
736  DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(),
737  stops.data(), DlTileMode::kDecal),
738  };
739 
740  DisplayListBuilder builder;
741  DlPaint paint;
742  paint.setColor(DlColor::kWhite());
743  builder.DrawRect(DlRect::MakeLTRB(0, 0, 605, 205), paint);
744  for (int i = 0; i < 3; i++) {
745  builder.Save();
746  builder.Translate(i * 200.0f, 0);
747  paint.setColorSource(color_sources[i]);
748  builder.DrawRect(DlRect::MakeLTRB(0, 0, 200, 200), paint);
749  builder.Restore();
750  }
751  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
752 }
753 
754 TEST_P(AiksTest, GradientStrokesRenderCorrectly) {
755  // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
756  auto callback = [&]() -> sk_sp<DisplayList> {
757  static float scale = 3;
758  static bool add_circle_clip = true;
759  const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
760  const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat,
761  DlTileMode::kMirror, DlTileMode::kDecal};
762  static int selected_tile_mode = 0;
763  static float alpha = 1;
764 
765  if (AiksTest::ImGuiBegin("Controls", nullptr,
766  ImGuiWindowFlags_AlwaysAutoResize)) {
767  ImGui::SliderFloat("Scale", &scale, 0, 6);
768  ImGui::Checkbox("Circle clip", &add_circle_clip);
769  ImGui::SliderFloat("Alpha", &alpha, 0, 1);
770  ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
771  sizeof(tile_mode_names) / sizeof(char*));
772  ImGui::End();
773  }
774 
775  DisplayListBuilder builder;
776  builder.Scale(GetContentScale().x, GetContentScale().y);
777  DlPaint paint;
778  paint.setColor(DlColor::kWhite());
779  builder.DrawPaint(paint);
780 
781  paint.setDrawStyle(DlDrawStyle::kStroke);
782  paint.setColor(DlColor::kWhite().withAlpha(alpha * 255));
783  paint.setStrokeWidth(10);
784  auto tile_mode = tile_modes[selected_tile_mode];
785 
786  std::vector<DlColor> colors = {
787  DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()),
788  DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())};
789  std::vector<Scalar> stops = {0.0, 1.0};
790 
791  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {50, 50},
792  stops.size(), colors.data(),
793  stops.data(), tile_mode));
794 
795  DlPathBuilder path_builder;
796  path_builder.MoveTo(DlPoint(20, 20));
797  path_builder.QuadraticCurveTo(DlPoint(60, 20), DlPoint(60, 60));
798  path_builder.Close();
799  path_builder.MoveTo(DlPoint(60, 20));
800  path_builder.QuadraticCurveTo(DlPoint(60, 60), DlPoint(20, 60));
801  DlPath path = path_builder.TakePath();
802 
803  builder.Scale(scale, scale);
804 
805  if (add_circle_clip) {
806  static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
807  Color::Red());
808  static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
809  Color::Red());
810  auto [handle_a, handle_b] =
811  DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
812 
813  Matrix ip_matrix = builder.GetMatrix();
814  if (!ip_matrix.IsInvertible()) {
815  return nullptr;
816  }
817  ip_matrix = ip_matrix.Invert();
818  Point point_a = ip_matrix * handle_a * GetContentScale();
819  Point point_b = ip_matrix * handle_b * GetContentScale();
820 
821  Point middle = (point_a + point_b) / 2;
822  auto radius = point_a.GetDistance(middle);
823  builder.ClipPath(DlPath::MakeCircle(middle, radius));
824  }
825 
826  for (auto join :
827  {DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) {
828  paint.setStrokeJoin(join);
829  for (auto cap :
830  {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
831  paint.setStrokeCap(cap);
832  builder.DrawPath(path, paint);
833  builder.Translate(80, 0);
834  }
835  builder.Translate(-240, 60);
836  }
837 
838  return builder.Build();
839  };
840 
841  ASSERT_TRUE(OpenPlaygroundHere(callback));
842 }
843 
844 // Draws two gradients that should look identical (except that one is an RRECT).
845 TEST_P(AiksTest, FastGradientTestHorizontal) {
846  DisplayListBuilder builder;
847  DlPaint paint;
848  builder.Translate(100.0f, 0);
849 
850  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
851  DlColor::kGreen()};
852  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
853 
854  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {300, 0}, stops.size(),
855  colors.data(), stops.data(),
856  DlTileMode::kClamp));
857 
858  paint.setColor(DlColor::kWhite());
859  builder.DrawRect(DlRect::MakeXYWH(0, 0, 300, 300), paint);
860  builder.Translate(400, 0);
861  builder.DrawRoundRect(
862  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(0, 0, 300, 300), 4, 4), paint);
863 
864  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
865 }
866 
867 // Draws two gradients that should look identical (except that one is an RRECT).
868 TEST_P(AiksTest, FastGradientTestVertical) {
869  DisplayListBuilder builder;
870  DlPaint paint;
871  builder.Translate(100.0f, 0);
872 
873  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
874  DlColor::kGreen()};
875  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
876 
877  paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {0, 300}, stops.size(),
878  colors.data(), stops.data(),
879  DlTileMode::kClamp));
880 
881  paint.setColor(DlColor::kWhite());
882  builder.DrawRect(DlRect::MakeXYWH(0, 0, 300, 300), paint);
883  builder.Translate(400, 0);
884  builder.DrawRoundRect(
885  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(0, 0, 300, 300), 4, 4), paint);
886 
887  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
888 }
889 
890 // Draws two gradients that should look identical (except that one is an RRECT).
891 TEST_P(AiksTest, FastGradientTestHorizontalReversed) {
892  DisplayListBuilder builder;
893  DlPaint paint;
894  builder.Translate(100.0f, 0);
895 
896  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
897  DlColor::kGreen()};
898  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
899 
900  paint.setColorSource(DlColorSource::MakeLinear({300, 0}, {0, 0}, stops.size(),
901  colors.data(), stops.data(),
902  DlTileMode::kClamp));
903 
904  paint.setColor(DlColor::kWhite());
905  builder.DrawRect(DlRect::MakeXYWH(0, 0, 300, 300), paint);
906  builder.Translate(400, 0);
907  builder.DrawRoundRect(
908  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(0, 0, 300, 300), 4, 4), paint);
909 
910  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
911 }
912 
913 // Draws two gradients that should look identical (except that one is an RRECT).
914 TEST_P(AiksTest, FastGradientTestVerticalReversed) {
915  DisplayListBuilder builder;
916  DlPaint paint;
917  builder.Translate(100.0f, 0);
918 
919  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
920  DlColor::kGreen()};
921  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
922 
923  paint.setColorSource(DlColorSource::MakeLinear({0, 300}, {0, 0}, stops.size(),
924  colors.data(), stops.data(),
925  DlTileMode::kClamp));
926 
927  paint.setColor(DlColor::kWhite());
928  builder.DrawRect(DlRect::MakeXYWH(0, 0, 300, 300), paint);
929  builder.Translate(400, 0);
930  builder.DrawRoundRect(
931  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(0, 0, 300, 300), 4, 4), paint);
932 
933  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
934 }
935 
936 TEST_P(AiksTest, VerifyNonOptimizedGradient) {
937  DisplayListBuilder builder;
938  DlPaint paint;
939  builder.Translate(100.0f, 0);
940 
941  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue(),
942  DlColor::kGreen()};
943  std::vector<Scalar> stops = {0.0, 0.1, 1.0};
944 
945  // Inset the start and end point to verify that we do not apply
946  // the fast gradient condition.
947  paint.setColorSource(
948  DlColorSource::MakeLinear({0, 150}, {0, 100}, stops.size(), colors.data(),
949  stops.data(), DlTileMode::kRepeat));
950 
951  paint.setColor(DlColor::kWhite());
952  builder.DrawRect(DlRect::MakeXYWH(0, 0, 300, 300), paint);
953  builder.Translate(400, 0);
954  builder.DrawRoundRect(
955  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(0, 0, 300, 300), 4, 4), paint);
956 
957  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
958 }
959 
960 } // namespace testing
961 } // namespace impeller
bool OpenPlaygroundHere(const AiksDlPlaygroundCallback &callback)
Point GetContentScale() const
Definition: playground.cc:193
int32_t x
AiksPlayground AiksTest
static void CanRenderLinearGradientWithDithering(AiksTest *aiks_test)
static void CanRenderRadialGradientWithDithering(AiksTest *aiks_test)
static void CanRenderConicalGradientWithDithering(AiksTest *aiks_test)
static void CanRenderSweepGradientWithDithering(AiksTest *aiks_test)
TEST_P(AiksTest, VerifyNonOptimizedGradient)
float Scalar
Definition: scalar.h:19
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:51
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
uint32_t ToARGB() const
Convert to ARGB 32 bit color.
Definition: color.h:259
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
Vector4 vec[4]
Definition: matrix.h:41
constexpr Type GetDistance(const TPoint &p) const
Definition: point.h:201