Flutter Impeller
geometry_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 "gtest/gtest.h"
7 
8 #include <limits>
9 #include <map>
10 #include <sstream>
11 #include <type_traits>
12 
13 #include "flutter/fml/build_config.h"
14 #include "flutter/testing/testing.h"
18 #include "impeller/geometry/half.h"
20 #include "impeller/geometry/rect.h"
23 #include "impeller/geometry/size.h"
24 
25 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
26 // NOLINTBEGIN(bugprone-unchecked-optional-access)
27 
28 namespace impeller {
29 namespace testing {
30 
31 TEST(GeometryTest, ScalarNearlyEqual) {
32  ASSERT_FALSE(ScalarNearlyEqual(0.0021f, 0.001f));
33  ASSERT_TRUE(ScalarNearlyEqual(0.0019f, 0.001f));
34  ASSERT_TRUE(ScalarNearlyEqual(0.002f, 0.001f, 0.0011f));
35  ASSERT_FALSE(ScalarNearlyEqual(0.002f, 0.001f, 0.0009f));
36  ASSERT_TRUE(ScalarNearlyEqual(
37  1.0f, 1.0f + std::numeric_limits<float>::epsilon() * 4));
38 }
39 
40 TEST(GeometryTest, MakeColumn) {
41  auto matrix = Matrix::MakeColumn(1, 2, 3, 4, //
42  5, 6, 7, 8, //
43  9, 10, 11, 12, //
44  13, 14, 15, 16);
45 
46  auto expect = Matrix{1, 2, 3, 4, //
47  5, 6, 7, 8, //
48  9, 10, 11, 12, //
49  13, 14, 15, 16};
50 
51  ASSERT_TRUE(matrix == expect);
52 }
53 
54 TEST(GeometryTest, MakeRow) {
55  auto matrix = Matrix::MakeRow(1, 2, 3, 4, //
56  5, 6, 7, 8, //
57  9, 10, 11, 12, //
58  13, 14, 15, 16);
59 
60  auto expect = Matrix{1, 5, 9, 13, //
61  2, 6, 10, 14, //
62  3, 7, 11, 15, //
63  4, 8, 12, 16};
64 
65  ASSERT_TRUE(matrix == expect);
66 }
67 
68 TEST(GeometryTest, RotationMatrix) {
69  auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
70  // clang-format off
71  auto expect = Matrix{k1OverSqrt2, k1OverSqrt2, 0, 0,
72  -k1OverSqrt2, k1OverSqrt2, 0, 0,
73  0, 0, 1, 0,
74  0, 0, 0, 1};
75  // clang-format on
76  ASSERT_MATRIX_NEAR(rotation, expect);
77 }
78 
79 TEST(GeometryTest, InvertMultMatrix) {
80  {
81  auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
82  auto invert = rotation.Invert();
83  // clang-format off
84  auto expect = Matrix{k1OverSqrt2, -k1OverSqrt2, 0, 0,
85  k1OverSqrt2, k1OverSqrt2, 0, 0,
86  0, 0, 1, 0,
87  0, 0, 0, 1};
88  // clang-format on
89  ASSERT_MATRIX_NEAR(invert, expect);
90  }
91  {
92  auto scale = Matrix::MakeScale(Vector2{2, 4});
93  auto invert = scale.Invert();
94  auto expect = Matrix{0.5, 0, 0, 0, //
95  0, 0.25, 0, 0, //
96  0, 0, 1, 0, //
97  0, 0, 0, 1};
98  ASSERT_MATRIX_NEAR(invert, expect);
99  }
100 }
101 
102 TEST(GeometryTest, MatrixBasis) {
103  auto matrix = Matrix{1, 2, 3, 4, //
104  5, 6, 7, 8, //
105  9, 10, 11, 12, //
106  13, 14, 15, 16};
107  auto basis = matrix.Basis();
108  auto expect = Matrix{1, 2, 3, 0, //
109  5, 6, 7, 0, //
110  9, 10, 11, 0, //
111  0, 0, 0, 1};
112  ASSERT_MATRIX_NEAR(basis, expect);
113 }
114 
115 TEST(GeometryTest, MutliplicationMatrix) {
116  auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
117  auto invert = rotation.Invert();
118  ASSERT_MATRIX_NEAR(rotation * invert, Matrix{});
119 }
120 
121 TEST(GeometryTest, DeterminantTest) {
122  auto matrix = Matrix{3, 4, 14, 155, 2, 1, 3, 4, 2, 3, 2, 1, 1, 2, 4, 2};
123  ASSERT_EQ(matrix.GetDeterminant(), -1889);
124 }
125 
126 TEST(GeometryTest, InvertMatrix) {
127  auto inverted = Matrix{10, -9, -12, 8, //
128  7, -12, 11, 22, //
129  -10, 10, 3, 6, //
130  -2, 22, 2, 1}
131  .Invert();
132 
133  auto result = Matrix{
134  438.0 / 85123.0, 1751.0 / 85123.0, -7783.0 / 85123.0, 4672.0 / 85123.0,
135  393.0 / 85123.0, -178.0 / 85123.0, -570.0 / 85123.0, 4192 / 85123.0,
136  -5230.0 / 85123.0, 2802.0 / 85123.0, -3461.0 / 85123.0, 962.0 / 85123.0,
137  2690.0 / 85123.0, 1814.0 / 85123.0, 3896.0 / 85123.0, 319.0 / 85123.0};
138 
139  ASSERT_MATRIX_NEAR(inverted, result);
140 }
141 
142 TEST(GeometryTest, TestDecomposition) {
143  auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
144 
145  auto result = rotated.Decompose();
146 
147  ASSERT_TRUE(result.has_value());
148 
149  MatrixDecomposition res = result.value();
150 
151  auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
152  ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
153 }
154 
155 TEST(GeometryTest, TestDecomposition2) {
156  auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
157  auto scaled = Matrix::MakeScale({2.0, 3.0, 1.0});
158  auto translated = Matrix::MakeTranslation({-200, 750, 20});
159 
160  auto result = (translated * rotated * scaled).Decompose();
161 
162  ASSERT_TRUE(result.has_value());
163 
164  MatrixDecomposition res = result.value();
165 
166  auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
167 
168  ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
169 
170  ASSERT_FLOAT_EQ(res.translation.x, -200);
171  ASSERT_FLOAT_EQ(res.translation.y, 750);
172  ASSERT_FLOAT_EQ(res.translation.z, 20);
173 
174  ASSERT_FLOAT_EQ(res.scale.x, 2);
175  ASSERT_FLOAT_EQ(res.scale.y, 3);
176  ASSERT_FLOAT_EQ(res.scale.z, 1);
177 }
178 
179 TEST(GeometryTest, TestRecomposition) {
180  /*
181  * Decomposition.
182  */
183  auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
184 
185  auto result = rotated.Decompose();
186 
187  ASSERT_TRUE(result.has_value());
188 
189  MatrixDecomposition res = result.value();
190 
191  auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
192 
193  ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
194 
195  /*
196  * Recomposition.
197  */
198  ASSERT_MATRIX_NEAR(rotated, Matrix{res});
199 }
200 
201 TEST(GeometryTest, TestRecomposition2) {
202  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
204  Matrix::MakeScale({2.0, 2.0, 2.0});
205 
206  auto result = matrix.Decompose();
207 
208  ASSERT_TRUE(result.has_value());
209 
210  ASSERT_MATRIX_NEAR(matrix, Matrix{result.value()});
211 }
212 
213 TEST(GeometryTest, MatrixVectorMultiplication) {
214  {
215  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
217  Matrix::MakeScale({2.0, 2.0, 2.0});
218  auto vector = Vector4(10, 20, 30, 2);
219 
220  Vector4 result = matrix * vector;
221  auto expected = Vector4(160, 220, 260, 2);
222  ASSERT_VECTOR4_NEAR(result, expected);
223  }
224 
225  {
226  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
228  Matrix::MakeScale({2.0, 2.0, 2.0});
229  auto vector = Vector3(10, 20, 30);
230 
231  Vector3 result = matrix * vector;
232  auto expected = Vector3(60, 120, 160);
233  ASSERT_VECTOR3_NEAR(result, expected);
234  }
235 
236  {
237  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
239  Matrix::MakeScale({2.0, 2.0, 2.0});
240  auto vector = Point(10, 20);
241 
242  Point result = matrix * vector;
243  auto expected = Point(60, 120);
244  ASSERT_POINT_NEAR(result, expected);
245  }
246 
247  // Matrix Vector ops should respect perspective transforms.
248  {
249  auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100);
250  auto vector = Vector3(3, 3, -3);
251 
252  Vector3 result = matrix * vector;
253  auto expected = Vector3(-1, -1, 1.3468);
254  ASSERT_VECTOR3_NEAR(result, expected);
255  }
256 
257  {
258  auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100) *
259  Matrix::MakeTranslation(Vector3(0, 0, -3));
260  auto point = Point(3, 3);
261 
262  Point result = matrix * point;
263  auto expected = Point(-1, -1);
264  ASSERT_POINT_NEAR(result, expected);
265  }
266 
267  // Resolves to 0 on perspective singularity.
268  {
269  auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100);
270  auto point = Point(3, 3);
271 
272  Point result = matrix * point;
273  auto expected = Point(0, 0);
274  ASSERT_POINT_NEAR(result, expected);
275  }
276 }
277 
278 TEST(GeometryTest, MatrixMakeRotationFromQuaternion) {
279  {
280  auto matrix = Matrix::MakeRotation(Quaternion({1, 0, 0}, kPiOver2));
281  auto expected = Matrix::MakeRotationX(Radians(kPiOver2));
282  ASSERT_MATRIX_NEAR(matrix, expected);
283  }
284 
285  {
286  auto matrix = Matrix::MakeRotation(Quaternion({0, 1, 0}, kPiOver2));
287  auto expected = Matrix::MakeRotationY(Radians(kPiOver2));
288  ASSERT_MATRIX_NEAR(matrix, expected);
289  }
290 
291  {
292  auto matrix = Matrix::MakeRotation(Quaternion({0, 0, 1}, kPiOver2));
293  auto expected = Matrix::MakeRotationZ(Radians(kPiOver2));
294  ASSERT_MATRIX_NEAR(matrix, expected);
295  }
296 }
297 
298 TEST(GeometryTest, MatrixTransformDirection) {
299  {
300  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
302  Matrix::MakeScale({2.0, 2.0, 2.0});
303  auto vector = Vector4(10, 20, 30, 2);
304 
305  Vector4 result = matrix.TransformDirection(vector);
306  auto expected = Vector4(-40, 20, 60, 2);
307  ASSERT_VECTOR4_NEAR(result, expected);
308  }
309 
310  {
311  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
313  Matrix::MakeScale({2.0, 2.0, 2.0});
314  auto vector = Vector3(10, 20, 30);
315 
316  Vector3 result = matrix.TransformDirection(vector);
317  auto expected = Vector3(-40, 20, 60);
318  ASSERT_VECTOR3_NEAR(result, expected);
319  }
320 
321  {
322  auto matrix = Matrix::MakeTranslation({0, -0.4, 100}) *
324  Matrix::MakeScale({2.0, 2.0, 2.0});
325  auto vector = Point(10, 20);
326 
327  Point result = matrix.TransformDirection(vector);
328  auto expected = Point(-40, 20);
329  ASSERT_POINT_NEAR(result, expected);
330  }
331 }
332 
333 TEST(GeometryTest, MatrixGetMaxBasisLength) {
334  {
335  auto m = Matrix::MakeScale({3, 1, 1});
336  ASSERT_EQ(m.GetMaxBasisLength(), 3);
337 
338  m = m * Matrix::MakeSkew(0, 4);
339  ASSERT_EQ(m.GetMaxBasisLength(), 5);
340  }
341 
342  {
343  auto m = Matrix::MakeScale({-3, 4, 2});
344  ASSERT_EQ(m.GetMaxBasisLength(), 4);
345  }
346 }
347 
348 TEST(GeometryTest, MatrixGetMaxBasisLengthXY) {
349  {
350  auto m = Matrix::MakeScale({3, 1, 1});
351  ASSERT_EQ(m.GetMaxBasisLengthXY(), 3);
352 
353  m = m * Matrix::MakeSkew(0, 4);
354  ASSERT_EQ(m.GetMaxBasisLengthXY(), 5);
355  }
356 
357  {
358  auto m = Matrix::MakeScale({-3, 4, 7});
359  ASSERT_EQ(m.GetMaxBasisLengthXY(), 4);
360  }
361 
362  {
363  // clang-format off
364  auto m = Matrix::MakeColumn(
365  1.0f, 0.0f, 0.0f, 0.0f,
366  0.0f, 1.0f, 0.0f, 0.0f,
367  4.0f, 0.0f, 1.0f, 0.0f,
368  0.0f, 0.0f, 0.0f, 1.0f
369  );
370  // clang-format on
371  ASSERT_EQ(m.GetMaxBasisLengthXY(), 1.0f);
372  }
373 }
374 
375 TEST(GeometryTest, MatrixMakeOrthographic) {
376  {
377  auto m = Matrix::MakeOrthographic(Size(100, 200));
378  auto expect = Matrix{
379  0.02, 0, 0, 0, //
380  0, -0.01, 0, 0, //
381  0, 0, 0, 0, //
382  -1, 1, 0.5, 1, //
383  };
384  ASSERT_MATRIX_NEAR(m, expect);
385  }
386 
387  {
388  auto m = Matrix::MakeOrthographic(Size(400, 100));
389  auto expect = Matrix{
390  0.005, 0, 0, 0, //
391  0, -0.02, 0, 0, //
392  0, 0, 0, 0, //
393  -1, 1, 0.5, 1, //
394  };
395  ASSERT_MATRIX_NEAR(m, expect);
396  }
397 }
398 
399 TEST(GeometryTest, MatrixMakePerspective) {
400  {
401  auto m = Matrix::MakePerspective(Degrees(60), Size(100, 200), 1, 10);
402  auto expect = Matrix{
403  3.4641, 0, 0, 0, //
404  0, 1.73205, 0, 0, //
405  0, 0, 1.11111, 1, //
406  0, 0, -1.11111, 0, //
407  };
408  ASSERT_MATRIX_NEAR(m, expect);
409  }
410 
411  {
412  auto m = Matrix::MakePerspective(Radians(1), 2, 10, 20);
413  auto expect = Matrix{
414  0.915244, 0, 0, 0, //
415  0, 1.83049, 0, 0, //
416  0, 0, 2, 1, //
417  0, 0, -20, 0, //
418  };
419  ASSERT_MATRIX_NEAR(m, expect);
420  }
421 }
422 
423 TEST(GeometryTest, MatrixGetBasisVectors) {
424  {
425  auto m = Matrix();
426  Vector3 x = m.GetBasisX();
427  Vector3 y = m.GetBasisY();
428  Vector3 z = m.GetBasisZ();
429  ASSERT_VECTOR3_NEAR(x, Vector3(1, 0, 0));
430  ASSERT_VECTOR3_NEAR(y, Vector3(0, 1, 0));
431  ASSERT_VECTOR3_NEAR(z, Vector3(0, 0, 1));
432  }
433 
434  {
437  Matrix::MakeScale(Vector3(2, 3, 4));
438  Vector3 x = m.GetBasisX();
439  Vector3 y = m.GetBasisY();
440  Vector3 z = m.GetBasisZ();
441  ASSERT_VECTOR3_NEAR(x, Vector3(0, 2, 0));
442  ASSERT_VECTOR3_NEAR(y, Vector3(0, 0, 3));
443  ASSERT_VECTOR3_NEAR(z, Vector3(4, 0, 0));
444  }
445 }
446 
447 TEST(GeometryTest, MatrixGetDirectionScale) {
448  {
449  auto m = Matrix();
450  Scalar result = m.GetDirectionScale(Vector3{1, 0, 0});
451  ASSERT_FLOAT_EQ(result, 1);
452  }
453 
454  {
455  auto m = Matrix::MakeRotationX(Degrees{10}) *
458  Scalar result = m.GetDirectionScale(Vector3{0, 1, 0});
459  ASSERT_FLOAT_EQ(result, 1);
460  }
461 
462  {
464  Matrix::MakeScale(Vector3(3, 4, 5));
465  Scalar result = m.GetDirectionScale(Vector3{2, 0, 0});
466  ASSERT_FLOAT_EQ(result, 8);
467  }
468 }
469 
470 TEST(GeometryTest, MatrixTranslationScaleOnly) {
471  {
472  auto m = Matrix();
473  bool result = m.IsTranslationScaleOnly();
474  ASSERT_TRUE(result);
475  }
476 
477  {
478  auto m = Matrix::MakeScale(Vector3(2, 3, 4));
479  bool result = m.IsTranslationScaleOnly();
480  ASSERT_TRUE(result);
481  }
482 
483  {
484  auto m = Matrix::MakeTranslation(Vector3(2, 3, 4));
485  bool result = m.IsTranslationScaleOnly();
486  ASSERT_TRUE(result);
487  }
488 
489  {
490  auto m = Matrix::MakeRotationZ(Degrees(10));
491  bool result = m.IsTranslationScaleOnly();
492  ASSERT_FALSE(result);
493  }
494 }
495 
496 TEST(GeometryTest, MatrixLookAt) {
497  {
498  auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
499  Vector3(0, 1, 0));
500  auto expected = Matrix{
501  1, 0, 0, 0, //
502  0, 1, 0, 0, //
503  0, 0, 1, 0, //
504  0, 0, 1, 1, //
505  };
506  ASSERT_MATRIX_NEAR(m, expected);
507  }
508 
509  // Sideways tilt.
510  {
511  auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
512  Vector3(1, 1, 0).Normalize());
513 
514  // clang-format off
515  auto expected = Matrix{
516  k1OverSqrt2, k1OverSqrt2, 0, 0,
517  -k1OverSqrt2, k1OverSqrt2, 0, 0,
518  0, 0, 1, 0,
519  0, 0, 1, 1,
520  };
521  // clang-format on
522  ASSERT_MATRIX_NEAR(m, expected);
523  }
524 
525  // Half way between +x and -y, yaw 90
526  {
527  auto m =
528  Matrix::MakeLookAt(Vector3(), Vector3(10, -10, 0), Vector3(0, 0, -1));
529 
530  // clang-format off
531  auto expected = Matrix{
532  -k1OverSqrt2, 0, k1OverSqrt2, 0,
533  -k1OverSqrt2, 0, -k1OverSqrt2, 0,
534  0, -1, 0, 0,
535  0, 0, 0, 1,
536  };
537  // clang-format on
538  ASSERT_MATRIX_NEAR(m, expected);
539  }
540 }
541 
542 TEST(GeometryTest, QuaternionLerp) {
543  auto q1 = Quaternion{{0.0, 0.0, 1.0}, 0.0};
544  auto q2 = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
545 
546  auto q3 = q1.Slerp(q2, 0.5);
547 
548  auto expected = Quaternion{{0.0, 0.0, 1.0}, kPiOver4 / 2.0};
549 
550  ASSERT_QUATERNION_NEAR(q3, expected);
551 }
552 
553 TEST(GeometryTest, QuaternionVectorMultiply) {
554  {
555  Quaternion q({0, 0, 1}, 0);
556  Vector3 v(0, 1, 0);
557 
558  Vector3 result = q * v;
559  Vector3 expected(0, 1, 0);
560 
561  ASSERT_VECTOR3_NEAR(result, expected);
562  }
563 
564  {
565  Quaternion q({0, 0, 1}, k2Pi);
566  Vector3 v(1, 0, 0);
567 
568  Vector3 result = q * v;
569  Vector3 expected(1, 0, 0);
570 
571  ASSERT_VECTOR3_NEAR(result, expected);
572  }
573 
574  {
575  Quaternion q({0, 0, 1}, kPiOver4);
576  Vector3 v(0, 1, 0);
577 
578  Vector3 result = q * v;
579  Vector3 expected(-k1OverSqrt2, k1OverSqrt2, 0);
580 
581  ASSERT_VECTOR3_NEAR(result, expected);
582  }
583 
584  {
585  Quaternion q(Vector3(1, 0, 1).Normalize(), kPi);
586  Vector3 v(0, 0, -1);
587 
588  Vector3 result = q * v;
589  Vector3 expected(-1, 0, 0);
590 
591  ASSERT_VECTOR3_NEAR(result, expected);
592  }
593 }
594 
595 TEST(GeometryTest, CanGenerateMipCounts) {
596  ASSERT_EQ((Size{128, 128}.MipCount()), 7u);
597  ASSERT_EQ((Size{128, 256}.MipCount()), 8u);
598  ASSERT_EQ((Size{128, 130}.MipCount()), 8u);
599  ASSERT_EQ((Size{128, 257}.MipCount()), 9u);
600  ASSERT_EQ((Size{257, 128}.MipCount()), 9u);
601  ASSERT_EQ((Size{128, 0}.MipCount()), 1u);
602  ASSERT_EQ((Size{128, -25}.MipCount()), 1u);
603  ASSERT_EQ((Size{-128, 25}.MipCount()), 1u);
604  ASSERT_EQ((Size{1, 1}.MipCount()), 1u);
605  ASSERT_EQ((Size{0, 0}.MipCount()), 1u);
606 }
607 
608 TEST(GeometryTest, CanConvertTTypesExplicitly) {
609  {
610  Point p1(1.0, 2.0);
611  IPoint p2 = static_cast<IPoint>(p1);
612  ASSERT_EQ(p2.x, 1u);
613  ASSERT_EQ(p2.y, 2u);
614  }
615 
616  {
617  Size s1(1.0, 2.0);
618  ISize s2 = static_cast<ISize>(s1);
619  ASSERT_EQ(s2.width, 1u);
620  ASSERT_EQ(s2.height, 2u);
621  }
622 
623  {
624  Size s1(1.0, 2.0);
625  Point p1 = static_cast<Point>(s1);
626  ASSERT_EQ(p1.x, 1u);
627  ASSERT_EQ(p1.y, 2u);
628  }
629 }
630 
631 TEST(GeometryTest, CanPerformAlgebraicPointOps) {
632  {
633  IPoint p1(1, 2);
634  IPoint p2 = p1 + IPoint(1, 2);
635  ASSERT_EQ(p2.x, 2u);
636  ASSERT_EQ(p2.y, 4u);
637  }
638 
639  {
640  IPoint p1(3, 6);
641  IPoint p2 = p1 - IPoint(1, 2);
642  ASSERT_EQ(p2.x, 2u);
643  ASSERT_EQ(p2.y, 4u);
644  }
645 
646  {
647  IPoint p1(1, 2);
648  IPoint p2 = p1 * IPoint(2, 3);
649  ASSERT_EQ(p2.x, 2u);
650  ASSERT_EQ(p2.y, 6u);
651  }
652 
653  {
654  IPoint p1(2, 6);
655  IPoint p2 = p1 / IPoint(2, 3);
656  ASSERT_EQ(p2.x, 1u);
657  ASSERT_EQ(p2.y, 2u);
658  }
659 }
660 
661 TEST(GeometryTest, CanPerformAlgebraicPointOpsWithArithmeticTypes) {
662  // LHS
663  {
664  IPoint p1(1, 2);
665  IPoint p2 = p1 * 2.0f;
666  ASSERT_EQ(p2.x, 2u);
667  ASSERT_EQ(p2.y, 4u);
668  }
669 
670  {
671  IPoint p1(2, 6);
672  IPoint p2 = p1 / 2.0f;
673  ASSERT_EQ(p2.x, 1u);
674  ASSERT_EQ(p2.y, 3u);
675  }
676 
677  // RHS
678  {
679  IPoint p1(1, 2);
680  IPoint p2 = 2.0f * p1;
681  ASSERT_EQ(p2.x, 2u);
682  ASSERT_EQ(p2.y, 4u);
683  }
684 
685  {
686  IPoint p1(2, 6);
687  IPoint p2 = 12.0f / p1;
688  ASSERT_EQ(p2.x, 6u);
689  ASSERT_EQ(p2.y, 2u);
690  }
691 }
692 
693 TEST(GeometryTest, PointIntegerCoercesToFloat) {
694  // Integer on LHS, float on RHS
695  {
696  IPoint p1(1, 2);
697  Point p2 = p1 + Point(1, 2);
698  ASSERT_FLOAT_EQ(p2.x, 2u);
699  ASSERT_FLOAT_EQ(p2.y, 4u);
700  }
701 
702  {
703  IPoint p1(3, 6);
704  Point p2 = p1 - Point(1, 2);
705  ASSERT_FLOAT_EQ(p2.x, 2u);
706  ASSERT_FLOAT_EQ(p2.y, 4u);
707  }
708 
709  {
710  IPoint p1(1, 2);
711  Point p2 = p1 * Point(2, 3);
712  ASSERT_FLOAT_EQ(p2.x, 2u);
713  ASSERT_FLOAT_EQ(p2.y, 6u);
714  }
715 
716  {
717  IPoint p1(2, 6);
718  Point p2 = p1 / Point(2, 3);
719  ASSERT_FLOAT_EQ(p2.x, 1u);
720  ASSERT_FLOAT_EQ(p2.y, 2u);
721  }
722 
723  // Float on LHS, integer on RHS
724  {
725  Point p1(1, 2);
726  Point p2 = p1 + IPoint(1, 2);
727  ASSERT_FLOAT_EQ(p2.x, 2u);
728  ASSERT_FLOAT_EQ(p2.y, 4u);
729  }
730 
731  {
732  Point p1(3, 6);
733  Point p2 = p1 - IPoint(1, 2);
734  ASSERT_FLOAT_EQ(p2.x, 2u);
735  ASSERT_FLOAT_EQ(p2.y, 4u);
736  }
737 
738  {
739  Point p1(1, 2);
740  Point p2 = p1 * IPoint(2, 3);
741  ASSERT_FLOAT_EQ(p2.x, 2u);
742  ASSERT_FLOAT_EQ(p2.y, 6u);
743  }
744 
745  {
746  Point p1(2, 6);
747  Point p2 = p1 / IPoint(2, 3);
748  ASSERT_FLOAT_EQ(p2.x, 1u);
749  ASSERT_FLOAT_EQ(p2.y, 2u);
750  }
751 }
752 
753 TEST(GeometryTest, SizeCoercesToPoint) {
754  // Point on LHS, Size on RHS
755  {
756  IPoint p1(1, 2);
757  IPoint p2 = p1 + ISize(1, 2);
758  ASSERT_EQ(p2.x, 2u);
759  ASSERT_EQ(p2.y, 4u);
760  }
761 
762  {
763  IPoint p1(3, 6);
764  IPoint p2 = p1 - ISize(1, 2);
765  ASSERT_EQ(p2.x, 2u);
766  ASSERT_EQ(p2.y, 4u);
767  }
768 
769  {
770  IPoint p1(1, 2);
771  IPoint p2 = p1 * ISize(2, 3);
772  ASSERT_EQ(p2.x, 2u);
773  ASSERT_EQ(p2.y, 6u);
774  }
775 
776  {
777  IPoint p1(2, 6);
778  IPoint p2 = p1 / ISize(2, 3);
779  ASSERT_EQ(p2.x, 1u);
780  ASSERT_EQ(p2.y, 2u);
781  }
782 
783  // Size on LHS, Point on RHS
784  {
785  ISize p1(1, 2);
786  IPoint p2 = p1 + IPoint(1, 2);
787  ASSERT_EQ(p2.x, 2u);
788  ASSERT_EQ(p2.y, 4u);
789  }
790 
791  {
792  ISize p1(3, 6);
793  IPoint p2 = p1 - IPoint(1, 2);
794  ASSERT_EQ(p2.x, 2u);
795  ASSERT_EQ(p2.y, 4u);
796  }
797 
798  {
799  ISize p1(1, 2);
800  IPoint p2 = p1 * IPoint(2, 3);
801  ASSERT_EQ(p2.x, 2u);
802  ASSERT_EQ(p2.y, 6u);
803  }
804 
805  {
806  ISize p1(2, 6);
807  IPoint p2 = p1 / IPoint(2, 3);
808  ASSERT_EQ(p2.x, 1u);
809  ASSERT_EQ(p2.y, 2u);
810  }
811 }
812 
813 TEST(GeometryTest, CanUsePointAssignmentOperators) {
814  // Point on RHS
815  {
816  IPoint p(1, 2);
817  p += IPoint(1, 2);
818  ASSERT_EQ(p.x, 2u);
819  ASSERT_EQ(p.y, 4u);
820  }
821 
822  {
823  IPoint p(3, 6);
824  p -= IPoint(1, 2);
825  ASSERT_EQ(p.x, 2u);
826  ASSERT_EQ(p.y, 4u);
827  }
828 
829  {
830  IPoint p(1, 2);
831  p *= IPoint(2, 3);
832  ASSERT_EQ(p.x, 2u);
833  ASSERT_EQ(p.y, 6u);
834  }
835 
836  {
837  IPoint p(2, 6);
838  p /= IPoint(2, 3);
839  ASSERT_EQ(p.x, 1u);
840  ASSERT_EQ(p.y, 2u);
841  }
842 
843  // Size on RHS
844  {
845  IPoint p(1, 2);
846  p += ISize(1, 2);
847  ASSERT_EQ(p.x, 2u);
848  ASSERT_EQ(p.y, 4u);
849  }
850 
851  {
852  IPoint p(3, 6);
853  p -= ISize(1, 2);
854  ASSERT_EQ(p.x, 2u);
855  ASSERT_EQ(p.y, 4u);
856  }
857 
858  {
859  IPoint p(1, 2);
860  p *= ISize(2, 3);
861  ASSERT_EQ(p.x, 2u);
862  ASSERT_EQ(p.y, 6u);
863  }
864 
865  {
866  IPoint p(2, 6);
867  p /= ISize(2, 3);
868  ASSERT_EQ(p.x, 1u);
869  ASSERT_EQ(p.y, 2u);
870  }
871 
872  // Arithmetic type on RHS
873  {
874  IPoint p(1, 2);
875  p *= 3;
876  ASSERT_EQ(p.x, 3u);
877  ASSERT_EQ(p.y, 6u);
878  }
879 
880  {
881  IPoint p(3, 6);
882  p /= 3;
883  ASSERT_EQ(p.x, 1u);
884  ASSERT_EQ(p.y, 2u);
885  }
886 }
887 
888 TEST(GeometryTest, PointDotProduct) {
889  {
890  Point p(1, 0);
891  Scalar s = p.Dot(Point(-1, 0));
892  ASSERT_FLOAT_EQ(s, -1);
893  }
894 
895  {
896  Point p(0, -1);
897  Scalar s = p.Dot(Point(-1, 0));
898  ASSERT_FLOAT_EQ(s, 0);
899  }
900 
901  {
902  Point p(1, 2);
903  Scalar s = p.Dot(Point(3, -4));
904  ASSERT_FLOAT_EQ(s, -5);
905  }
906 }
907 
908 TEST(GeometryTest, PointCrossProduct) {
909  {
910  Point p(1, 0);
911  Scalar s = p.Cross(Point(-1, 0));
912  ASSERT_FLOAT_EQ(s, 0);
913  }
914 
915  {
916  Point p(0, -1);
917  Scalar s = p.Cross(Point(-1, 0));
918  ASSERT_FLOAT_EQ(s, -1);
919  }
920 
921  {
922  Point p(1, 2);
923  Scalar s = p.Cross(Point(3, -4));
924  ASSERT_FLOAT_EQ(s, -10);
925  }
926 }
927 
928 TEST(GeometryTest, PointReflect) {
929  {
930  Point axis = Point(0, 1);
931  Point a(2, 3);
932  auto reflected = a.Reflect(axis);
933  auto expected = Point(2, -3);
934  ASSERT_POINT_NEAR(reflected, expected);
935  }
936 
937  {
938  Point axis = Point(1, 1).Normalize();
939  Point a(1, 0);
940  auto reflected = a.Reflect(axis);
941  auto expected = Point(0, -1);
942  ASSERT_POINT_NEAR(reflected, expected);
943  }
944 
945  {
946  Point axis = Point(1, 1).Normalize();
947  Point a(-1, -1);
948  auto reflected = a.Reflect(axis);
949  ASSERT_POINT_NEAR(reflected, -a);
950  }
951 }
952 
953 TEST(GeometryTest, PointAbs) {
954  Point a(-1, -2);
955  auto a_abs = a.Abs();
956  auto expected = Point(1, 2);
957  ASSERT_POINT_NEAR(a_abs, expected);
958 }
959 
960 TEST(GeometryTest, PointRotate) {
961  {
962  Point a(1, 0);
963  auto rotated = a.Rotate(Radians{kPiOver2});
964  auto expected = Point(0, 1);
965  ASSERT_POINT_NEAR(rotated, expected);
966  }
967 
968  {
969  Point a(1, 0);
970  auto rotated = a.Rotate(Radians{-kPiOver2});
971  auto expected = Point(0, -1);
972  ASSERT_POINT_NEAR(rotated, expected);
973  }
974 
975  {
976  Point a(1, 0);
977  auto rotated = a.Rotate(Radians{kPi});
978  auto expected = Point(-1, 0);
979  ASSERT_POINT_NEAR(rotated, expected);
980  }
981 
982  {
983  Point a(1, 0);
984  auto rotated = a.Rotate(Radians{kPi * 1.5});
985  auto expected = Point(0, -1);
986  ASSERT_POINT_NEAR(rotated, expected);
987  }
988 }
989 
990 TEST(GeometryTest, PointAngleTo) {
991  // Negative result in the CCW (with up = -Y) direction.
992  {
993  Point a(1, 1);
994  Point b(1, -1);
995  Radians actual = a.AngleTo(b);
996  Radians expected = Radians{-kPi / 2};
997  ASSERT_FLOAT_EQ(actual.radians, expected.radians);
998  }
999 
1000  // Check the other direction to ensure the result is signed correctly.
1001  {
1002  Point a(1, -1);
1003  Point b(1, 1);
1004  Radians actual = a.AngleTo(b);
1005  Radians expected = Radians{kPi / 2};
1006  ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1007  }
1008 
1009  // Differences in magnitude should have no impact on the result.
1010  {
1011  Point a(100, -100);
1012  Point b(0.01, 0.01);
1013  Radians actual = a.AngleTo(b);
1014  Radians expected = Radians{kPi / 2};
1015  ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1016  }
1017 }
1018 
1019 TEST(GeometryTest, PointMin) {
1020  Point p(1, 2);
1021  Point result = p.Min({0, 10});
1022  Point expected(0, 2);
1023  ASSERT_POINT_NEAR(result, expected);
1024 }
1025 
1026 TEST(GeometryTest, Vector3Min) {
1027  Vector3 p(1, 2, 3);
1028  Vector3 result = p.Min({0, 10, 2});
1029  Vector3 expected(0, 2, 2);
1030  ASSERT_VECTOR3_NEAR(result, expected);
1031 }
1032 
1033 TEST(GeometryTest, Vector4Min) {
1034  Vector4 p(1, 2, 3, 4);
1035  Vector4 result = p.Min({0, 10, 2, 1});
1036  Vector4 expected(0, 2, 2, 1);
1037  ASSERT_VECTOR4_NEAR(result, expected);
1038 }
1039 
1040 TEST(GeometryTest, PointMax) {
1041  Point p(1, 2);
1042  Point result = p.Max({0, 10});
1043  Point expected(1, 10);
1044  ASSERT_POINT_NEAR(result, expected);
1045 }
1046 
1047 TEST(GeometryTest, Vector3Max) {
1048  Vector3 p(1, 2, 3);
1049  Vector3 result = p.Max({0, 10, 2});
1050  Vector3 expected(1, 10, 3);
1051  ASSERT_VECTOR3_NEAR(result, expected);
1052 }
1053 
1054 TEST(GeometryTest, Vector4Max) {
1055  Vector4 p(1, 2, 3, 4);
1056  Vector4 result = p.Max({0, 10, 2, 1});
1057  Vector4 expected(1, 10, 3, 4);
1058  ASSERT_VECTOR4_NEAR(result, expected);
1059 }
1060 
1061 TEST(GeometryTest, PointFloor) {
1062  Point p(1.5, 2.3);
1063  Point result = p.Floor();
1064  Point expected(1, 2);
1065  ASSERT_POINT_NEAR(result, expected);
1066 }
1067 
1068 TEST(GeometryTest, Vector3Floor) {
1069  Vector3 p(1.5, 2.3, 3.9);
1070  Vector3 result = p.Floor();
1071  Vector3 expected(1, 2, 3);
1072  ASSERT_VECTOR3_NEAR(result, expected);
1073 }
1074 
1075 TEST(GeometryTest, Vector4Floor) {
1076  Vector4 p(1.5, 2.3, 3.9, 4.0);
1077  Vector4 result = p.Floor();
1078  Vector4 expected(1, 2, 3, 4);
1079  ASSERT_VECTOR4_NEAR(result, expected);
1080 }
1081 
1082 TEST(GeometryTest, PointCeil) {
1083  Point p(1.5, 2.3);
1084  Point result = p.Ceil();
1085  Point expected(2, 3);
1086  ASSERT_POINT_NEAR(result, expected);
1087 }
1088 
1089 TEST(GeometryTest, Vector3Ceil) {
1090  Vector3 p(1.5, 2.3, 3.9);
1091  Vector3 result = p.Ceil();
1092  Vector3 expected(2, 3, 4);
1093  ASSERT_VECTOR3_NEAR(result, expected);
1094 }
1095 
1096 TEST(GeometryTest, Vector4Ceil) {
1097  Vector4 p(1.5, 2.3, 3.9, 4.0);
1098  Vector4 result = p.Ceil();
1099  Vector4 expected(2, 3, 4, 4);
1100  ASSERT_VECTOR4_NEAR(result, expected);
1101 }
1102 
1103 TEST(GeometryTest, PointRound) {
1104  Point p(1.5, 2.3);
1105  Point result = p.Round();
1106  Point expected(2, 2);
1107  ASSERT_POINT_NEAR(result, expected);
1108 }
1109 
1110 TEST(GeometryTest, Vector3Round) {
1111  Vector3 p(1.5, 2.3, 3.9);
1112  Vector3 result = p.Round();
1113  Vector3 expected(2, 2, 4);
1114  ASSERT_VECTOR3_NEAR(result, expected);
1115 }
1116 
1117 TEST(GeometryTest, Vector4Round) {
1118  Vector4 p(1.5, 2.3, 3.9, 4.0);
1119  Vector4 result = p.Round();
1120  Vector4 expected(2, 2, 4, 4);
1121  ASSERT_VECTOR4_NEAR(result, expected);
1122 }
1123 
1124 TEST(GeometryTest, PointLerp) {
1125  Point p(1, 2);
1126  Point result = p.Lerp({5, 10}, 0.75);
1127  Point expected(4, 8);
1128  ASSERT_POINT_NEAR(result, expected);
1129 }
1130 
1131 TEST(GeometryTest, Vector3Lerp) {
1132  Vector3 p(1, 2, 3);
1133  Vector3 result = p.Lerp({5, 10, 15}, 0.75);
1134  Vector3 expected(4, 8, 12);
1135  ASSERT_VECTOR3_NEAR(result, expected);
1136 }
1137 
1138 TEST(GeometryTest, Vector4Lerp) {
1139  Vector4 p(1, 2, 3, 4);
1140  Vector4 result = p.Lerp({5, 10, 15, 20}, 0.75);
1141  Vector4 expected(4, 8, 12, 16);
1142  ASSERT_VECTOR4_NEAR(result, expected);
1143 }
1144 
1145 TEST(GeometryTest, SeparatedVector2NormalizesWithConstructor) {
1146  SeparatedVector2 v(Vector2(10, 0));
1148  ASSERT_NEAR(v.magnitude, 10, kEhCloseEnough);
1149 }
1150 
1151 TEST(GeometryTest, SeparatedVector2GetVector) {
1152  SeparatedVector2 v(Vector2(10, 0));
1153  ASSERT_POINT_NEAR(v.GetVector(), Vector2(10, 0));
1154 }
1155 
1156 TEST(GeometryTest, SeparatedVector2GetAlignment) {
1157  // Parallel
1158  {
1159  SeparatedVector2 v(Vector2(10, 0));
1160  Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(5, 0)));
1161  ASSERT_NEAR(actual, 1, kEhCloseEnough);
1162  }
1163 
1164  // Perpendicular
1165  {
1166  SeparatedVector2 v(Vector2(10, 0));
1167  Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, 5)));
1168  ASSERT_NEAR(actual, 0, kEhCloseEnough);
1169  }
1170 
1171  // Opposite parallel
1172  {
1173  SeparatedVector2 v(Vector2(0, 10));
1174  Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, -5)));
1175  ASSERT_NEAR(actual, -1, kEhCloseEnough);
1176  }
1177 }
1178 
1179 TEST(GeometryTest, SeparatedVector2AngleTo) {
1180  {
1181  SeparatedVector2 v(Vector2(10, 0));
1182  Radians actual = v.AngleTo(SeparatedVector2(Vector2(5, 0)));
1183  Radians expected = Radians{0};
1184  ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1185  }
1186 
1187  {
1188  SeparatedVector2 v(Vector2(10, 0));
1189  Radians actual = v.AngleTo(SeparatedVector2(Vector2(0, -5)));
1190  Radians expected = Radians{-kPi / 2};
1191  ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1192  }
1193 }
1194 
1195 TEST(GeometryTest, CanUseVector3AssignmentOperators) {
1196  {
1197  Vector3 p(1, 2, 4);
1198  p += Vector3(1, 2, 4);
1199  ASSERT_EQ(p.x, 2u);
1200  ASSERT_EQ(p.y, 4u);
1201  ASSERT_EQ(p.z, 8u);
1202  }
1203 
1204  {
1205  Vector3 p(3, 6, 8);
1206  p -= Vector3(1, 2, 3);
1207  ASSERT_EQ(p.x, 2u);
1208  ASSERT_EQ(p.y, 4u);
1209  ASSERT_EQ(p.z, 5u);
1210  }
1211 
1212  {
1213  Vector3 p(1, 2, 3);
1214  p *= Vector3(2, 3, 4);
1215  ASSERT_EQ(p.x, 2u);
1216  ASSERT_EQ(p.y, 6u);
1217  ASSERT_EQ(p.z, 12u);
1218  }
1219 
1220  {
1221  Vector3 p(1, 2, 3);
1222  p *= 2;
1223  ASSERT_EQ(p.x, 2u);
1224  ASSERT_EQ(p.y, 4u);
1225  ASSERT_EQ(p.z, 6u);
1226  }
1227 
1228  {
1229  Vector3 p(2, 6, 12);
1230  p /= Vector3(2, 3, 4);
1231  ASSERT_EQ(p.x, 1u);
1232  ASSERT_EQ(p.y, 2u);
1233  ASSERT_EQ(p.z, 3u);
1234  }
1235 
1236  {
1237  Vector3 p(2, 6, 12);
1238  p /= 2;
1239  ASSERT_EQ(p.x, 1u);
1240  ASSERT_EQ(p.y, 3u);
1241  ASSERT_EQ(p.z, 6u);
1242  }
1243 }
1244 
1245 TEST(GeometryTest, CanPerformAlgebraicVector3Ops) {
1246  {
1247  Vector3 p1(1, 2, 3);
1248  Vector3 p2 = p1 + Vector3(1, 2, 3);
1249  ASSERT_EQ(p2.x, 2u);
1250  ASSERT_EQ(p2.y, 4u);
1251  ASSERT_EQ(p2.z, 6u);
1252  }
1253 
1254  {
1255  Vector3 p1(3, 6, 9);
1256  Vector3 p2 = p1 - Vector3(1, 2, 3);
1257  ASSERT_EQ(p2.x, 2u);
1258  ASSERT_EQ(p2.y, 4u);
1259  ASSERT_EQ(p2.z, 6u);
1260  }
1261 
1262  {
1263  Vector3 p1(1, 2, 3);
1264  Vector3 p2 = p1 * Vector3(2, 3, 4);
1265  ASSERT_EQ(p2.x, 2u);
1266  ASSERT_EQ(p2.y, 6u);
1267  ASSERT_EQ(p2.z, 12u);
1268  }
1269 
1270  {
1271  Vector3 p1(2, 6, 12);
1272  Vector3 p2 = p1 / Vector3(2, 3, 4);
1273  ASSERT_EQ(p2.x, 1u);
1274  ASSERT_EQ(p2.y, 2u);
1275  ASSERT_EQ(p2.z, 3u);
1276  }
1277 }
1278 
1279 TEST(GeometryTest, CanPerformAlgebraicVector3OpsWithArithmeticTypes) {
1280  // LHS
1281  {
1282  Vector3 p1(1, 2, 3);
1283  Vector3 p2 = p1 + 2.0f;
1284  ASSERT_EQ(p2.x, 3);
1285  ASSERT_EQ(p2.y, 4);
1286  ASSERT_EQ(p2.z, 5);
1287  }
1288 
1289  {
1290  Vector3 p1(1, 2, 3);
1291  Vector3 p2 = p1 - 2.0f;
1292  ASSERT_EQ(p2.x, -1);
1293  ASSERT_EQ(p2.y, 0);
1294  ASSERT_EQ(p2.z, 1);
1295  }
1296 
1297  {
1298  Vector3 p1(1, 2, 3);
1299  Vector3 p2 = p1 * 2.0f;
1300  ASSERT_EQ(p2.x, 2);
1301  ASSERT_EQ(p2.y, 4);
1302  ASSERT_EQ(p2.z, 6);
1303  }
1304 
1305  {
1306  Vector3 p1(2, 6, 12);
1307  Vector3 p2 = p1 / 2.0f;
1308  ASSERT_EQ(p2.x, 1);
1309  ASSERT_EQ(p2.y, 3);
1310  ASSERT_EQ(p2.z, 6);
1311  }
1312 
1313  // RHS
1314  {
1315  Vector3 p1(1, 2, 3);
1316  Vector3 p2 = 2.0f + p1;
1317  ASSERT_EQ(p2.x, 3);
1318  ASSERT_EQ(p2.y, 4);
1319  ASSERT_EQ(p2.z, 5);
1320  }
1321 
1322  {
1323  Vector3 p1(1, 2, 3);
1324  Vector3 p2 = 2.0f - p1;
1325  ASSERT_EQ(p2.x, 1);
1326  ASSERT_EQ(p2.y, 0);
1327  ASSERT_EQ(p2.z, -1);
1328  }
1329 
1330  {
1331  Vector3 p1(1, 2, 3);
1332  Vector3 p2 = 2.0f * p1;
1333  ASSERT_EQ(p2.x, 2);
1334  ASSERT_EQ(p2.y, 4);
1335  ASSERT_EQ(p2.z, 6);
1336  }
1337 
1338  {
1339  Vector3 p1(2, 6, 12);
1340  Vector3 p2 = 12.0f / p1;
1341  ASSERT_EQ(p2.x, 6);
1342  ASSERT_EQ(p2.y, 2);
1343  ASSERT_EQ(p2.z, 1);
1344  }
1345 }
1346 
1347 TEST(GeometryTest, ColorPremultiply) {
1348  {
1349  Color a(1.0, 0.5, 0.2, 0.5);
1350  Color premultiplied = a.Premultiply();
1351  Color expected = Color(0.5, 0.25, 0.1, 0.5);
1352  ASSERT_COLOR_NEAR(premultiplied, expected);
1353  }
1354 
1355  {
1356  Color a(0.5, 0.25, 0.1, 0.5);
1357  Color unpremultiplied = a.Unpremultiply();
1358  Color expected = Color(1.0, 0.5, 0.2, 0.5);
1359  ASSERT_COLOR_NEAR(unpremultiplied, expected);
1360  }
1361 
1362  {
1363  Color a(0.5, 0.25, 0.1, 0.0);
1364  Color unpremultiplied = a.Unpremultiply();
1365  Color expected = Color(0.0, 0.0, 0.0, 0.0);
1366  ASSERT_COLOR_NEAR(unpremultiplied, expected);
1367  }
1368 }
1369 
1370 TEST(GeometryTest, ColorR8G8B8A8) {
1371  {
1372  Color a(1.0, 0.5, 0.2, 0.5);
1373  std::array<uint8_t, 4> expected = {255, 128, 51, 128};
1374  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1375  }
1376 
1377  {
1378  Color a(0.0, 0.0, 0.0, 0.0);
1379  std::array<uint8_t, 4> expected = {0, 0, 0, 0};
1380  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1381  }
1382 
1383  {
1384  Color a(1.0, 1.0, 1.0, 1.0);
1385  std::array<uint8_t, 4> expected = {255, 255, 255, 255};
1386  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1387  }
1388 }
1389 
1390 TEST(GeometryTest, ColorLerp) {
1391  {
1392  Color a(0.0, 0.0, 0.0, 0.0);
1393  Color b(1.0, 1.0, 1.0, 1.0);
1394 
1395  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.5, 0.5, 0.5, 0.5));
1396  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1397  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1398  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.2, 0.2, 0.2, 0.2));
1399  }
1400 
1401  {
1402  Color a(0.2, 0.4, 1.0, 0.5);
1403  Color b(0.4, 1.0, 0.2, 0.3);
1404 
1405  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.3, 0.7, 0.6, 0.4));
1406  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1407  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1408  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.24, 0.52, 0.84, 0.46));
1409  }
1410 }
1411 
1412 TEST(GeometryTest, ColorClamp01) {
1413  {
1414  Color result = Color(0.5, 0.5, 0.5, 0.5).Clamp01();
1415  Color expected = Color(0.5, 0.5, 0.5, 0.5);
1416  ASSERT_COLOR_NEAR(result, expected);
1417  }
1418 
1419  {
1420  Color result = Color(-1, -1, -1, -1).Clamp01();
1421  Color expected = Color(0, 0, 0, 0);
1422  ASSERT_COLOR_NEAR(result, expected);
1423  }
1424 
1425  {
1426  Color result = Color(2, 2, 2, 2).Clamp01();
1427  Color expected = Color(1, 1, 1, 1);
1428  ASSERT_COLOR_NEAR(result, expected);
1429  }
1430 }
1431 
1432 TEST(GeometryTest, ColorMakeRGBA8) {
1433  {
1434  Color a = Color::MakeRGBA8(0, 0, 0, 0);
1436  ASSERT_COLOR_NEAR(a, b);
1437  }
1438 
1439  {
1440  Color a = Color::MakeRGBA8(255, 255, 255, 255);
1441  Color b = Color::White();
1442  ASSERT_COLOR_NEAR(a, b);
1443  }
1444 
1445  {
1446  Color a = Color::MakeRGBA8(63, 127, 191, 127);
1447  Color b(0.247059, 0.498039, 0.74902, 0.498039);
1448  ASSERT_COLOR_NEAR(a, b);
1449  }
1450 }
1451 
1452 TEST(GeometryTest, ColorApplyColorMatrix) {
1453  {
1454  ColorMatrix color_matrix = {
1455  1, 1, 1, 1, 1, //
1456  1, 1, 1, 1, 1, //
1457  1, 1, 1, 1, 1, //
1458  1, 1, 1, 1, 1, //
1459  };
1460  auto result = Color::White().ApplyColorMatrix(color_matrix);
1461  auto expected = Color(1, 1, 1, 1);
1462  ASSERT_COLOR_NEAR(result, expected);
1463  }
1464 
1465  {
1466  ColorMatrix color_matrix = {
1467  0.1, 0, 0, 0, 0.01, //
1468  0, 0.2, 0, 0, 0.02, //
1469  0, 0, 0.3, 0, 0.03, //
1470  0, 0, 0, 0.4, 0.04, //
1471  };
1472  auto result = Color::White().ApplyColorMatrix(color_matrix);
1473  auto expected = Color(0.11, 0.22, 0.33, 0.44);
1474  ASSERT_COLOR_NEAR(result, expected);
1475  }
1476 }
1477 
1478 TEST(GeometryTest, ColorLinearToSRGB) {
1479  {
1480  auto result = Color::White().LinearToSRGB();
1481  auto expected = Color(1, 1, 1, 1);
1482  ASSERT_COLOR_NEAR(result, expected);
1483  }
1484 
1485  {
1486  auto result = Color::BlackTransparent().LinearToSRGB();
1487  auto expected = Color(0, 0, 0, 0);
1488  ASSERT_COLOR_NEAR(result, expected);
1489  }
1490 
1491  {
1492  auto result = Color(0.2, 0.4, 0.6, 0.8).LinearToSRGB();
1493  auto expected = Color(0.484529, 0.665185, 0.797738, 0.8);
1494  ASSERT_COLOR_NEAR(result, expected);
1495  }
1496 }
1497 
1498 TEST(GeometryTest, ColorSRGBToLinear) {
1499  {
1500  auto result = Color::White().SRGBToLinear();
1501  auto expected = Color(1, 1, 1, 1);
1502  ASSERT_COLOR_NEAR(result, expected);
1503  }
1504 
1505  {
1506  auto result = Color::BlackTransparent().SRGBToLinear();
1507  auto expected = Color(0, 0, 0, 0);
1508  ASSERT_COLOR_NEAR(result, expected);
1509  }
1510 
1511  {
1512  auto result = Color(0.2, 0.4, 0.6, 0.8).SRGBToLinear();
1513  auto expected = Color(0.0331048, 0.132868, 0.318547, 0.8);
1514  ASSERT_COLOR_NEAR(result, expected);
1515  }
1516 }
1517 
1519  static constexpr Color kDestinationColor =
1521  static constexpr Color kSourceColors[] = {Color::White().WithAlpha(0.75),
1522  Color::LimeGreen().WithAlpha(0.75),
1523  Color::Black().WithAlpha(0.75)};
1524 
1525  static const std::map<BlendMode, Color>
1527 };
1528 
1529 // THIS RESULT TABLE IS GENERATED!
1530 //
1531 // Uncomment the `GenerateColorBlendResults` test below to print a new table
1532 // after making changes to `Color::Blend`.
1533 const std::map<BlendMode, Color> ColorBlendTestData::kExpectedResults[sizeof(
1535  {
1536  {BlendMode::kClear, {0, 0, 0, 0}},
1537  {BlendMode::kSource, {1, 1, 1, 0.75}},
1538  {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1539  {BlendMode::kSourceOver, {0.878431, 0.916863, 0.985882, 0.9375}},
1540  {BlendMode::kDestinationOver, {0.513726, 0.667451, 0.943529, 0.9375}},
1541  {BlendMode::kSourceIn, {1, 1, 1, 0.5625}},
1542  {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1543  {BlendMode::kSourceOut, {1, 1, 1, 0.1875}},
1544  {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1545  {BlendMode::kSourceATop, {0.848039, 0.896078, 0.982353, 0.75}},
1546  {BlendMode::kDestinationATop, {0.544118, 0.688235, 0.947059, 0.75}},
1547  {BlendMode::kXor, {0.696078, 0.792157, 0.964706, 0.375}},
1548  {BlendMode::kPlus, {1, 1, 1, 1}},
1549  {BlendMode::kModulate, {0.392157, 0.584314, 0.929412, 0.5625}},
1550  {BlendMode::kScreen, {0.878431, 0.916863, 0.985882, 0.9375}},
1551  {BlendMode::kOverlay, {0.74902, 0.916863, 0.985882, 0.9375}},
1552  {BlendMode::kDarken, {0.513726, 0.667451, 0.943529, 0.9375}},
1553  {BlendMode::kLighten, {0.878431, 0.916863, 0.985882, 0.9375}},
1554  {BlendMode::kColorDodge, {0.878431, 0.916863, 0.985882, 0.9375}},
1555  {BlendMode::kColorBurn, {0.513725, 0.667451, 0.943529, 0.9375}},
1556  {BlendMode::kHardLight, {0.878431, 0.916863, 0.985882, 0.9375}},
1557  {BlendMode::kSoftLight, {0.654166, 0.775505, 0.964318, 0.9375}},
1558  {BlendMode::kDifference, {0.643137, 0.566275, 0.428235, 0.9375}},
1559  {BlendMode::kExclusion, {0.643137, 0.566275, 0.428235, 0.9375}},
1560  {BlendMode::kMultiply, {0.513726, 0.667451, 0.943529, 0.9375}},
1561  {BlendMode::kHue, {0.617208, 0.655639, 0.724659, 0.9375}},
1562  {BlendMode::kSaturation, {0.617208, 0.655639, 0.724659, 0.9375}},
1563  {BlendMode::kColor, {0.617208, 0.655639, 0.724659, 0.9375}},
1564  {BlendMode::kLuminosity, {0.878431, 0.916863, 0.985882, 0.9375}},
1565  },
1566  {
1567  {BlendMode::kClear, {0, 0, 0, 0}},
1568  {BlendMode::kSource, {0.196078, 0.803922, 0.196078, 0.75}},
1569  {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1570  {BlendMode::kSourceOver, {0.235294, 0.76, 0.342745, 0.9375}},
1571  {BlendMode::kDestinationOver, {0.352941, 0.628235, 0.782745, 0.9375}},
1572  {BlendMode::kSourceIn, {0.196078, 0.803922, 0.196078, 0.5625}},
1573  {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1574  {BlendMode::kSourceOut, {0.196078, 0.803922, 0.196078, 0.1875}},
1575  {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1576  {BlendMode::kSourceATop, {0.245098, 0.74902, 0.379412, 0.75}},
1577  {BlendMode::kDestinationATop, {0.343137, 0.639216, 0.746078, 0.75}},
1578  {BlendMode::kXor, {0.294118, 0.694118, 0.562745, 0.375}},
1579  {BlendMode::kPlus, {0.441176, 1, 0.844118, 1}},
1580  {BlendMode::kModulate, {0.0768935, 0.469742, 0.182238, 0.5625}},
1581  {BlendMode::kScreen, {0.424452, 0.828743, 0.79105, 0.9375}},
1582  {BlendMode::kOverlay, {0.209919, 0.779839, 0.757001, 0.9375}},
1583  {BlendMode::kDarken, {0.235294, 0.628235, 0.342745, 0.9375}},
1584  {BlendMode::kLighten, {0.352941, 0.76, 0.782745, 0.9375}},
1585  {BlendMode::kColorDodge, {0.41033, 0.877647, 0.825098, 0.9375}},
1586  {BlendMode::kColorBurn, {0.117647, 0.567403, 0.609098, 0.9375}},
1587  {BlendMode::kHardLight, {0.209919, 0.779839, 0.443783, 0.9375}},
1588  {BlendMode::kSoftLight, {0.266006, 0.693915, 0.758818, 0.9375}},
1589  {BlendMode::kDifference, {0.235294, 0.409412, 0.665098, 0.9375}},
1590  {BlendMode::kExclusion, {0.378316, 0.546897, 0.681707, 0.9375}},
1591  {BlendMode::kMultiply, {0.163783, 0.559493, 0.334441, 0.9375}},
1592  {BlendMode::kHue, {0.266235, 0.748588, 0.373686, 0.9375}},
1593  {BlendMode::kSaturation, {0.339345, 0.629787, 0.811502, 0.9375}},
1594  {BlendMode::kColor, {0.241247, 0.765953, 0.348698, 0.9375}},
1595  {BlendMode::kLuminosity, {0.346988, 0.622282, 0.776792, 0.9375}},
1596  },
1597  {
1598  {BlendMode::kClear, {0, 0, 0, 0}},
1599  {BlendMode::kSource, {0, 0, 0, 0.75}},
1600  {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1601  {BlendMode::kSourceOver, {0.0784314, 0.116863, 0.185882, 0.9375}},
1602  {BlendMode::kDestinationOver, {0.313726, 0.467451, 0.743529, 0.9375}},
1603  {BlendMode::kSourceIn, {0, 0, 0, 0.5625}},
1604  {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1605  {BlendMode::kSourceOut, {0, 0, 0, 0.1875}},
1606  {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1607  {BlendMode::kSourceATop, {0.0980392, 0.146078, 0.232353, 0.75}},
1608  {BlendMode::kDestinationATop, {0.294118, 0.438235, 0.697059, 0.75}},
1609  {BlendMode::kXor, {0.196078, 0.292157, 0.464706, 0.375}},
1610  {BlendMode::kPlus, {0.294118, 0.438235, 0.697059, 1}},
1611  {BlendMode::kModulate, {0, 0, 0, 0.5625}},
1612  {BlendMode::kScreen, {0.313726, 0.467451, 0.743529, 0.9375}},
1613  {BlendMode::kOverlay, {0.0784314, 0.218039, 0.701176, 0.9375}},
1614  {BlendMode::kDarken, {0.0784314, 0.116863, 0.185882, 0.9375}},
1615  {BlendMode::kLighten, {0.313726, 0.467451, 0.743529, 0.9375}},
1616  {BlendMode::kColorDodge, {0.313726, 0.467451, 0.743529, 0.9375}},
1617  {BlendMode::kColorBurn, {0.0784314, 0.116863, 0.185882, 0.9375}},
1618  {BlendMode::kHardLight, {0.0784314, 0.116863, 0.185882, 0.9375}},
1619  {BlendMode::kSoftLight, {0.170704, 0.321716, 0.704166, 0.9375}},
1620  {BlendMode::kDifference, {0.313726, 0.467451, 0.743529, 0.9375}},
1621  {BlendMode::kExclusion, {0.313726, 0.467451, 0.743529, 0.9375}},
1622  {BlendMode::kMultiply, {0.0784314, 0.116863, 0.185882, 0.9375}},
1623  {BlendMode::kHue, {0.417208, 0.455639, 0.524659, 0.9375}},
1624  {BlendMode::kSaturation, {0.417208, 0.455639, 0.524659, 0.9375}},
1625  {BlendMode::kColor, {0.417208, 0.455639, 0.524659, 0.9375}},
1626  {BlendMode::kLuminosity, {0.0784314, 0.116863, 0.185882, 0.9375}},
1627  },
1628 };
1629 
1630 /// To print a new ColorBlendTestData::kExpectedResults table, uncomment this
1631 /// test and run with:
1632 /// --gtest_filter="GeometryTest.GenerateColorBlendResults"
1633 /*
1634 TEST(GeometryTest, GenerateColorBlendResults) {
1635  auto& o = std::cout;
1636  using BlendT = std::underlying_type_t<BlendMode>;
1637  o << "{";
1638  for (const auto& source : ColorBlendTestData::kSourceColors) {
1639  o << "{";
1640  for (BlendT blend_i = 0;
1641  blend_i < static_cast<BlendT>(BlendMode::kLast) + 1; blend_i++) {
1642  auto blend = static_cast<BlendMode>(blend_i);
1643  Color c = ColorBlendTestData::kDestinationColor.Blend(source, blend);
1644  o << "{ BlendMode::k" << BlendModeToString(blend) << ", ";
1645  o << "{" << c.red << "," << c.green << "," << c.blue << "," << c.alpha
1646  << "}";
1647  o << "}," << std::endl;
1648  }
1649  o << "},";
1650  }
1651  o << "};" << std::endl;
1652 }
1653 */
1654 
1655 #define _BLEND_MODE_RESULT_CHECK(blend_mode) \
1656  expected = ColorBlendTestData::kExpectedResults[source_i] \
1657  .find(BlendMode::k##blend_mode) \
1658  ->second; \
1659  EXPECT_COLOR_NEAR(dst.Blend(src, BlendMode::k##blend_mode), expected);
1660 
1661 TEST(GeometryTest, ColorBlendReturnsExpectedResults) {
1663  for (size_t source_i = 0;
1664  source_i < sizeof(ColorBlendTestData::kSourceColors) / sizeof(Color);
1665  source_i++) {
1666  Color src = ColorBlendTestData::kSourceColors[source_i];
1667 
1668  Color expected;
1670  }
1671 }
1672 
1673 #define _BLEND_MODE_NAME_CHECK(blend_mode) \
1674  case BlendMode::k##blend_mode: \
1675  ASSERT_STREQ(result, #blend_mode); \
1676  break;
1677 
1678 TEST(GeometryTest, BlendModeToString) {
1679  using BlendT = std::underlying_type_t<BlendMode>;
1680  for (BlendT i = 0; i <= static_cast<BlendT>(BlendMode::kLast); i++) {
1681  auto mode = static_cast<BlendMode>(i);
1682  auto result = BlendModeToString(mode);
1684  }
1685 }
1686 
1687 TEST(GeometryTest, CanConvertBetweenDegressAndRadians) {
1688  {
1689  auto deg = Degrees{90.0};
1690  Radians rad = deg;
1691  ASSERT_FLOAT_EQ(rad.radians, kPiOver2);
1692  }
1693 }
1694 
1695 TEST(GeometryTest, MatrixPrinting) {
1696  {
1697  std::stringstream stream;
1698  Matrix m;
1699  stream << m;
1700  ASSERT_EQ(stream.str(), R"((
1701  1.000000, 0.000000, 0.000000, 0.000000,
1702  0.000000, 1.000000, 0.000000, 0.000000,
1703  0.000000, 0.000000, 1.000000, 0.000000,
1704  0.000000, 0.000000, 0.000000, 1.000000,
1705 ))");
1706  }
1707 
1708  {
1709  std::stringstream stream;
1710  Matrix m = Matrix::MakeTranslation(Vector3(10, 20, 30));
1711  stream << m;
1712 
1713  ASSERT_EQ(stream.str(), R"((
1714  1.000000, 0.000000, 0.000000, 10.000000,
1715  0.000000, 1.000000, 0.000000, 20.000000,
1716  0.000000, 0.000000, 1.000000, 30.000000,
1717  0.000000, 0.000000, 0.000000, 1.000000,
1718 ))");
1719  }
1720 }
1721 
1722 TEST(GeometryTest, PointPrinting) {
1723  {
1724  std::stringstream stream;
1725  Point m;
1726  stream << m;
1727  ASSERT_EQ(stream.str(), "(0, 0)");
1728  }
1729 
1730  {
1731  std::stringstream stream;
1732  Point m(13, 37);
1733  stream << m;
1734  ASSERT_EQ(stream.str(), "(13, 37)");
1735  }
1736 }
1737 
1738 TEST(GeometryTest, Vector3Printing) {
1739  {
1740  std::stringstream stream;
1741  Vector3 m;
1742  stream << m;
1743  ASSERT_EQ(stream.str(), "(0, 0, 0)");
1744  }
1745 
1746  {
1747  std::stringstream stream;
1748  Vector3 m(1, 2, 3);
1749  stream << m;
1750  ASSERT_EQ(stream.str(), "(1, 2, 3)");
1751  }
1752 }
1753 
1754 TEST(GeometryTest, Vector4Printing) {
1755  {
1756  std::stringstream stream;
1757  Vector4 m;
1758  stream << m;
1759  ASSERT_EQ(stream.str(), "(0, 0, 0, 1)");
1760  }
1761 
1762  {
1763  std::stringstream stream;
1764  Vector4 m(1, 2, 3, 4);
1765  stream << m;
1766  ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1767  }
1768 }
1769 
1770 TEST(GeometryTest, ColorPrinting) {
1771  {
1772  std::stringstream stream;
1773  Color m;
1774  stream << m;
1775  ASSERT_EQ(stream.str(), "(0, 0, 0, 0)");
1776  }
1777 
1778  {
1779  std::stringstream stream;
1780  Color m(1, 2, 3, 4);
1781  stream << m;
1782  ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1783  }
1784 }
1785 
1786 TEST(GeometryTest, ToIColor) {
1787  ASSERT_EQ(Color::ToIColor(Color(0, 0, 0, 0)), 0u);
1788  ASSERT_EQ(Color::ToIColor(Color(1.0, 1.0, 1.0, 1.0)), 0xFFFFFFFF);
1789  ASSERT_EQ(Color::ToIColor(Color(0.5, 0.5, 1.0, 1.0)), 0xFF8080FF);
1790 }
1791 
1792 TEST(GeometryTest, Gradient) {
1793  {
1794  // Simple 2 color gradient produces color buffer containing exactly those
1795  // values.
1796  std::vector<Color> colors = {Color::Red(), Color::Blue()};
1797  std::vector<Scalar> stops = {0.0, 1.0};
1798 
1799  auto gradient = CreateGradientBuffer(colors, stops);
1800 
1801  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1802  ASSERT_EQ(gradient.texture_size, 2u);
1803  }
1804 
1805  {
1806  // Gradient with duplicate stops does not create an empty texture.
1807  std::vector<Color> colors = {Color::Red(), Color::Yellow(), Color::Black(),
1808  Color::Blue()};
1809  std::vector<Scalar> stops = {0.0, 0.25, 0.25, 1.0};
1810 
1811  auto gradient = CreateGradientBuffer(colors, stops);
1812  ASSERT_EQ(gradient.texture_size, 5u);
1813  }
1814 
1815  {
1816  // Simple N color gradient produces color buffer containing exactly those
1817  // values.
1818  std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green(),
1819  Color::White()};
1820  std::vector<Scalar> stops = {0.0, 0.33, 0.66, 1.0};
1821 
1822  auto gradient = CreateGradientBuffer(colors, stops);
1823 
1824  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1825  ASSERT_EQ(gradient.texture_size, 4u);
1826  }
1827 
1828  {
1829  // Gradient with color stops will lerp and scale buffer.
1830  std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
1831  std::vector<Scalar> stops = {0.0, 0.25, 1.0};
1832 
1833  auto gradient = CreateGradientBuffer(colors, stops);
1834 
1835  std::vector<Color> lerped_colors = {
1836  Color::Red(),
1837  Color::Blue(),
1838  Color::Lerp(Color::Blue(), Color::Green(), 0.3333),
1839  Color::Lerp(Color::Blue(), Color::Green(), 0.6666),
1840  Color::Green(),
1841  };
1842  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, lerped_colors);
1843  ASSERT_EQ(gradient.texture_size, 5u);
1844  }
1845 
1846  {
1847  // Gradient size is capped at 1024.
1848  std::vector<Color> colors = {};
1849  std::vector<Scalar> stops = {};
1850  for (auto i = 0u; i < 1025; i++) {
1851  colors.push_back(Color::Blue());
1852  stops.push_back(i / 1025.0);
1853  }
1854 
1855  auto gradient = CreateGradientBuffer(colors, stops);
1856 
1857  ASSERT_EQ(gradient.texture_size, 1024u);
1858  ASSERT_EQ(gradient.color_bytes.size(), 1024u * 4);
1859  }
1860 }
1861 
1862 TEST(GeometryTest, HalfConversions) {
1863 #if defined(FML_OS_MACOSX) || defined(FML_OS_IOS) || \
1864  defined(FML_OS_IOS_SIMULATOR)
1865  ASSERT_EQ(ScalarToHalf(0.0), 0.0f16);
1866  ASSERT_EQ(ScalarToHalf(0.05), 0.05f16);
1867  ASSERT_EQ(ScalarToHalf(2.43), 2.43f16);
1868  ASSERT_EQ(ScalarToHalf(-1.45), -1.45f16);
1869 
1870  // 65504 is the largest possible half.
1871  ASSERT_EQ(ScalarToHalf(65504.0f), 65504.0f16);
1872  ASSERT_EQ(ScalarToHalf(65504.0f + 1), 65504.0f16);
1873 
1874  // Colors
1875  ASSERT_EQ(HalfVector4(Color::Red()),
1876  HalfVector4(1.0f16, 0.0f16, 0.0f16, 1.0f16));
1877  ASSERT_EQ(HalfVector4(Color::Green()),
1878  HalfVector4(0.0f16, 1.0f16, 0.0f16, 1.0f16));
1879  ASSERT_EQ(HalfVector4(Color::Blue()),
1880  HalfVector4(0.0f16, 0.0f16, 1.0f16, 1.0f16));
1881  ASSERT_EQ(HalfVector4(Color::Black().WithAlpha(0)),
1882  HalfVector4(0.0f16, 0.0f16, 0.0f16, 0.0f16));
1883 
1884  ASSERT_EQ(HalfVector3(Vector3(4.0, 6.0, -1.0)),
1885  HalfVector3(4.0f16, 6.0f16, -1.0f16));
1886  ASSERT_EQ(HalfVector2(Vector2(4.0, 6.0)), HalfVector2(4.0f16, 6.0f16));
1887 
1888  ASSERT_EQ(Half(0.5f), Half(0.5f16));
1889  ASSERT_EQ(Half(0.5), Half(0.5f16));
1890  ASSERT_EQ(Half(5), Half(5.0f16));
1891 #else
1892  GTEST_SKIP() << "Half-precision floats (IEEE 754) are not portable and "
1893  "only used on Apple platforms.";
1894 #endif // FML_OS_MACOSX || FML_OS_IOS || FML_OS_IOS_SIMULATOR
1895 }
1896 
1897 } // namespace testing
1898 } // namespace impeller
1899 
1900 // NOLINTEND(bugprone-unchecked-optional-access)
impeller::Matrix::MakeSkew
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:117
impeller::ISize
ISize64 ISize
Definition: size.h:140
_BLEND_MODE_RESULT_CHECK
#define _BLEND_MODE_RESULT_CHECK(blend_mode)
Definition: geometry_unittests.cc:1655
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:278
ASSERT_COLOR_NEAR
#define ASSERT_COLOR_NEAR(a, b)
Definition: geometry_asserts.h:192
impeller::Matrix::Decompose
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:209
impeller::k1OverSqrt2
constexpr float k1OverSqrt2
Definition: constants.h:50
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:183
separated_vector.h
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::BlendMode::kDestinationATop
@ kDestinationATop
point.h
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::HalfVector2
A storage only class for half precision floating point vector 2.
Definition: half.h:129
impeller::TPoint::Ceil
constexpr TPoint Ceil() const
Definition: point.h:196
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:274
geometry_asserts.h
impeller::Vector3::Round
constexpr Vector3 Round() const
Definition: vector.h:86
impeller::Vector4::Max
constexpr Vector4 Max(const Vector4 &p) const
Definition: vector.h:292
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:154
impeller::Color
Definition: color.h:124
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:218
impeller::TPoint::Lerp
constexpr TPoint Lerp(const TPoint &p, Scalar t) const
Definition: point.h:236
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::SeparatedVector2
A Vector2, broken down as a separate magnitude and direction. Assumes that the direction given is nor...
Definition: separated_vector.h:21
impeller::Vector4
Definition: vector.h:232
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::BlendMode::kSource
@ kSource
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:126
impeller::BlendMode::kDestination
@ kDestination
impeller::TPoint::Min
constexpr TPoint Min(const TPoint &p) const
Definition: point.h:186
impeller::SeparatedVector2::magnitude
Scalar magnitude
The magnitude of the vector.
Definition: separated_vector.h:26
impeller::BlendMode::kDarken
@ kDarken
impeller::SeparatedVector2::direction
Vector2 direction
The normalized direction of the vector.
Definition: separated_vector.h:23
impeller::testing::TEST
TEST(AiksCanvasTest, EmptyCullRect)
Definition: canvas_unittests.cc:18
impeller::TPoint::Round
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:49
impeller::Vector2
Point Vector2
Definition: point.h:326
impeller::BlendMode::kColor
@ kColor
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:198
impeller::kPi
constexpr float kPi
Definition: constants.h:26
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kPlus
@ kPlus
impeller::Color::CornflowerBlue
static constexpr Color CornflowerBlue()
Definition: color.h:344
impeller::BlendMode::kOverlay
@ kOverlay
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:844
impeller::Radians::radians
Scalar radians
Definition: scalar.h:39
impeller::Matrix::MakeRow
static constexpr Matrix MakeRow(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:83
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
ASSERT_POINT_NEAR
#define ASSERT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:193
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Color::Clamp01
constexpr Color Clamp01() const
Definition: color.h:238
ASSERT_VECTOR4_NEAR
#define ASSERT_VECTOR4_NEAR(a, b)
Definition: geometry_asserts.h:195
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::TPoint::Floor
constexpr TPoint Floor() const
Definition: point.h:194
impeller::BlendMode::kModulate
@ kModulate
impeller::TPoint::Dot
constexpr Type Dot(const TPoint &p) const
Definition: point.h:220
impeller::MatrixDecomposition
Definition: matrix_decomposition.h:15
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::Matrix::MakeLookAt
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:532
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:506
impeller::BlendMode::kSaturation
@ kSaturation
impeller::BlendMode::kDifference
@ kDifference
gradient.h
impeller::Vector4::Round
constexpr Vector4 Round() const
Definition: vector.h:305
impeller::TPoint::Reflect
constexpr TPoint Reflect(const TPoint &axis) const
Definition: point.h:222
impeller::BlendMode::kLighten
@ kLighten
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:229
impeller::TSize< Scalar >
impeller::Quaternion
Definition: quaternion.h:14
impeller::BlendMode::kSoftLight
@ kSoftLight
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:29
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::Half
A storage only class for half precision floating point.
Definition: half.h:41
impeller::TPoint::AngleTo
constexpr Radians AngleTo(const TPoint &p) const
Definition: point.h:232
impeller::BlendMode::kHardLight
@ kHardLight
impeller::Color::ToIColor
static constexpr uint32_t ToIColor(Color color)
Convert this color to a 32-bit representation.
Definition: color.h:161
impeller::SeparatedVector2::GetVector
Vector2 GetVector() const
Returns the vector representation of the vector.
Definition: separated_vector.cc:17
impeller::Color::SRGBToLinear
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition: color.cc:399
impeller::BlendMode::kClear
@ kClear
impeller::Vector4::Lerp
constexpr Vector4 Lerp(const Vector4 &v, Scalar t) const
Definition: vector.h:309
impeller::testing::ColorBlendTestData::kExpectedResults
static const std::map< BlendMode, Color > kExpectedResults[sizeof(kSourceColors)]
Definition: geometry_unittests.cc:1526
impeller::ScalarToHalf
constexpr InternalHalf ScalarToHalf(Scalar f)
Convert a scalar to a half precision float.
Definition: half.h:32
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:280
impeller::TPoint::Max
constexpr TPoint Max(const TPoint &p) const
Definition: point.h:190
impeller::Vector4::Min
constexpr Vector4 Min(const Vector4 &p) const
Definition: vector.h:287
impeller::Color::ToR8G8B8A8
constexpr std::array< uint8_t, 4 > ToR8G8B8A8() const
Convert to R8G8B8A8 representation.
Definition: color.h:248
impeller::SeparatedVector2::GetAlignment
Scalar GetAlignment(const SeparatedVector2 &other) const
Definition: separated_vector.cc:21
impeller::Color::White
static constexpr Color White()
Definition: color.h:266
impeller::Matrix::MakeColumn
static constexpr Matrix MakeColumn(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:69
impeller::Vector3::Floor
constexpr Vector3 Floor() const
Definition: vector.h:78
impeller::Vector3::Ceil
constexpr Vector3 Ceil() const
Definition: vector.h:82
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Radians
Definition: scalar.h:38
impeller::MatrixDecomposition::translation
Vector3 translation
Definition: matrix_decomposition.h:16
impeller::HalfVector3
A storage only class for half precision floating point vector 3.
Definition: half.h:101
_BLEND_MODE_NAME_CHECK
#define _BLEND_MODE_NAME_CHECK(blend_mode)
Definition: geometry_unittests.cc:1673
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:276
ASSERT_MATRIX_NEAR
#define ASSERT_MATRIX_NEAR(a, b)
Definition: geometry_asserts.h:189
impeller::TPoint::Cross
constexpr Type Cross(const TPoint &p) const
Definition: point.h:218
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::CreateGradientBuffer
GradientData CreateGradientBuffer(const std::vector< Color > &colors, const std::vector< Scalar > &stops)
Populate a vector with the interpolated color bytes for the linear gradient described by colors and s...
Definition: gradient.cc:20
impeller::MatrixDecomposition::rotation
Quaternion rotation
Definition: matrix_decomposition.h:20
impeller::TSize::width
Type width
Definition: size.h:22
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::TPoint::x
Type x
Definition: point.h:30
ASSERT_QUATERNION_NEAR
#define ASSERT_QUATERNION_NEAR(a, b)
Definition: geometry_asserts.h:190
impeller::Matrix::GetDirectionScale
constexpr Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:316
scalar.h
impeller::MatrixDecomposition::scale
Vector3 scale
Definition: matrix_decomposition.h:17
impeller::BlendMode::kDestinationIn
@ kDestinationIn
impeller::TPoint::Abs
constexpr TPoint Abs() const
Definition: point.h:216
impeller::BlendMode::kExclusion
@ kExclusion
impeller::BlendMode::kDestinationOut
@ kDestinationOut
half.h
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
constants.h
impeller::IPoint
TPoint< int64_t > IPoint
Definition: point.h:323
ASSERT_VECTOR3_NEAR
#define ASSERT_VECTOR3_NEAR(a, b)
Definition: geometry_asserts.h:194
rect.h
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:272
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:268
impeller::testing::ColorBlendTestData::kSourceColors
static constexpr Color kSourceColors[]
Definition: geometry_unittests.cc:1521
impeller::Vector4::Floor
constexpr Vector4 Floor() const
Definition: vector.h:297
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::HalfVector4
A storage only class for half precision floating point vector 4.
Definition: half.h:60
scale
const Scalar scale
Definition: stroke_path_geometry.cc:308
impeller::BlendMode::kScreen
@ kScreen
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:497
impeller::testing::ColorBlendTestData
Definition: geometry_unittests.cc:1518
impeller::testing::ColorBlendTestData::kDestinationColor
static constexpr Color kDestinationColor
Definition: geometry_unittests.cc:1519
impeller::Vector3::Min
constexpr Vector3 Min(const Vector3 &p) const
Definition: vector.h:70
impeller::Degrees
Definition: scalar.h:46
color.h
impeller::Color::LimeGreen
static constexpr Color LimeGreen()
Definition: color.h:604
impeller::BlendMode::kLast
@ kLast
impeller::TPoint::Rotate
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226
impeller::TSize::height
Type height
Definition: size.h:23
impeller::BlendMode::kHue
@ kHue
impeller::SeparatedVector2::AngleTo
Radians AngleTo(const SeparatedVector2 &other) const
Returns the scalar angle between the two rays.
Definition: separated_vector.cc:25
impeller::BlendMode::kXor
@ kXor
impeller::TSize::MipCount
constexpr size_t MipCount() const
Definition: size.h:115
impeller::Color::Lerp
constexpr static Color Lerp(Color a, Color b, Scalar t)
Return a color that is linearly interpolated between colors a and b, according to the value of t.
Definition: color.h:234
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:208
impeller::ColorMatrix
Definition: color.h:117
impeller::Color::ApplyColorMatrix
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:378
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:388
ASSERT_COLOR_BUFFER_NEAR
#define ASSERT_COLOR_BUFFER_NEAR(a, b)
Definition: geometry_asserts.h:198
impeller
Definition: aiks_blend_unittests.cc:18
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::BlendMode::kMultiply
@ kMultiply
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:35
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:214
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
size.h
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Vector3::Lerp
constexpr Vector3 Lerp(const Vector3 &v, Scalar t) const
Definition: vector.h:178
ASSERT_ARRAY_4_NEAR
#define ASSERT_ARRAY_4_NEAR(a, b)
Definition: geometry_asserts.h:197
impeller::Vector3::Max
constexpr Vector3 Max(const Vector3 &p) const
Definition: vector.h:74
impeller::Vector4::Ceil
constexpr Vector4 Ceil() const
Definition: vector.h:301