2 * Copyright 2011 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
10 #include "SkMatrixUtils.h"
14 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
15 const SkScalar tolerance = SK_Scalar1 / 200000;
16 return SkScalarAbs(a - b) <= tolerance;
19 static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
20 for (int i = 0; i < 9; i++) {
21 if (!nearly_equal_scalar(a[i], b[i])) {
22 SkDebugf("not equal %g %g\n", (float)a[i], (float)b[i]);
29 static bool are_equal(skiatest::Reporter* reporter,
33 bool cheapEqual = a.cheapEqualTo(b);
34 if (equal != cheapEqual) {
36 bool foundZeroSignDiff = false;
37 for (int i = 0; i < 9; ++i) {
38 float aVal = a.get(i);
39 float bVal = b.get(i);
40 int aValI = *SkTCast<int*>(&aVal);
41 int bValI = *SkTCast<int*>(&bVal);
42 if (0 == aVal && 0 == bVal && aValI != bValI) {
43 foundZeroSignDiff = true;
45 REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
48 REPORTER_ASSERT(reporter, foundZeroSignDiff);
50 bool foundNaN = false;
51 for (int i = 0; i < 9; ++i) {
52 float aVal = a.get(i);
53 float bVal = b.get(i);
54 int aValI = *SkTCast<int*>(&aVal);
55 int bValI = *SkTCast<int*>(&bVal);
56 if (sk_float_isnan(aVal) && aValI == bValI) {
59 REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
62 REPORTER_ASSERT(reporter, foundNaN);
68 static bool is_identity(const SkMatrix& m) {
71 return nearly_equal(m, identity);
74 static void test_matrix_recttorect(skiatest::Reporter* reporter) {
78 src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
80 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
81 REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
82 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
84 dst.offset(SK_Scalar1, SK_Scalar1);
85 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
86 REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
87 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
89 dst.fRight += SK_Scalar1;
90 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
91 REPORTER_ASSERT(reporter,
92 (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
93 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
96 dst.fRight = src.fRight * 2;
97 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
98 REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
99 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
102 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
103 // add 100 in case we have a bug, I don't want to kill my stack in the test
104 static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
105 char buffer[kBufferSize];
106 size_t size1 = m.writeToMemory(NULL);
107 size_t size2 = m.writeToMemory(buffer);
108 REPORTER_ASSERT(reporter, size1 == size2);
109 REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
112 size_t size3 = m2.readFromMemory(buffer, kBufferSize);
113 REPORTER_ASSERT(reporter, size1 == size3);
114 REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
116 char buffer2[kBufferSize];
117 size3 = m2.writeToMemory(buffer2);
118 REPORTER_ASSERT(reporter, size1 == size3);
119 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
122 static void test_matrix_min_max_scale(skiatest::Reporter* reporter) {
128 REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale());
129 REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxScale());
130 success = identity.getMinMaxScales(scales);
131 REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
134 scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
135 REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale());
136 REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxScale());
137 success = scale.getMinMaxScales(scales);
138 REPORTER_ASSERT(reporter, success && SK_Scalar1 * 2 == scales[0] && SK_Scalar1 * 4 == scales[1]);
141 rot90Scale.setRotate(90 * SK_Scalar1);
142 rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
143 REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
144 REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale());
145 success = rot90Scale.getMinMaxScales(scales);
146 REPORTER_ASSERT(reporter, success && SK_Scalar1 / 4 == scales[0] && SK_Scalar1 / 2 == scales[1]);
149 rotate.setRotate(128 * SK_Scalar1);
150 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinScale(), SK_ScalarNearlyZero));
151 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxScale(), SK_ScalarNearlyZero));
152 success = rotate.getMinMaxScales(scales);
153 REPORTER_ASSERT(reporter, success);
154 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[0], SK_ScalarNearlyZero));
155 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[1], SK_ScalarNearlyZero));
158 translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
159 REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale());
160 REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxScale());
161 success = translate.getMinMaxScales(scales);
162 REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
166 perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
167 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
168 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale());
169 // Verify that getMinMaxScales() doesn't update the scales array on failure.
172 success = perspX.getMinMaxScales(scales);
173 REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]);
177 perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
178 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
179 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale());
182 success = perspY.getMinMaxScales(scales);
183 REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]);
185 SkMatrix baseMats[] = {scale, rot90Scale, rotate,
186 translate, perspX, perspY};
187 SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
188 for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
189 mats[i] = baseMats[i];
190 bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
191 REPORTER_ASSERT(reporter, invertable);
194 for (int m = 0; m < 1000; ++m) {
197 for (int i = 0; i < 4; ++i) {
198 int x = rand.nextU() % SK_ARRAY_COUNT(mats);
199 mat.postConcat(mats[x]);
202 SkScalar minScale = mat.getMinScale();
203 SkScalar maxScale = mat.getMaxScale();
204 REPORTER_ASSERT(reporter, (minScale < 0) == (maxScale < 0));
205 REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective());
208 bool success = mat.getMinMaxScales(scales);
209 REPORTER_ASSERT(reporter, success == !mat.hasPerspective());
210 REPORTER_ASSERT(reporter, !success || (scales[0] == minScale && scales[1] == maxScale));
212 if (mat.hasPerspective()) {
213 m -= 1; // try another non-persp matrix
217 // test a bunch of vectors. All should be scaled by between minScale and maxScale
218 // (modulo some error) and we should find a vector that is scaled by almost each.
219 static const SkScalar gVectorScaleTol = (105 * SK_Scalar1) / 100;
220 static const SkScalar gCloseScaleTol = (97 * SK_Scalar1) / 100;
221 SkScalar max = 0, min = SK_ScalarMax;
222 SkVector vectors[1000];
223 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
224 vectors[i].fX = rand.nextSScalar1();
225 vectors[i].fY = rand.nextSScalar1();
226 if (!vectors[i].normalize()) {
231 mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
232 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
233 SkScalar d = vectors[i].length();
234 REPORTER_ASSERT(reporter, SkScalarDiv(d, maxScale) < gVectorScaleTol);
235 REPORTER_ASSERT(reporter, SkScalarDiv(minScale, d) < gVectorScaleTol);
243 REPORTER_ASSERT(reporter, SkScalarDiv(max, maxScale) >= gCloseScaleTol);
244 REPORTER_ASSERT(reporter, SkScalarDiv(minScale, min) >= gCloseScaleTol);
248 static void test_matrix_preserve_shape(skiatest::Reporter* reporter) {
253 REPORTER_ASSERT(reporter, mat.isSimilarity());
254 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
258 mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
259 REPORTER_ASSERT(reporter, mat.isSimilarity());
260 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
262 // scale with same size
264 mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
265 REPORTER_ASSERT(reporter, mat.isSimilarity());
266 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
268 // scale with one negative
270 mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
271 REPORTER_ASSERT(reporter, mat.isSimilarity());
272 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
274 // scale with different size
276 mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
277 REPORTER_ASSERT(reporter, !mat.isSimilarity());
278 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
280 // scale with same size at a pivot point
282 mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
283 SkIntToScalar(2), SkIntToScalar(2));
284 REPORTER_ASSERT(reporter, mat.isSimilarity());
285 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
287 // scale with different size at a pivot point
289 mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
290 SkIntToScalar(2), SkIntToScalar(2));
291 REPORTER_ASSERT(reporter, !mat.isSimilarity());
292 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
294 // skew with same size
296 mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
297 REPORTER_ASSERT(reporter, !mat.isSimilarity());
298 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
300 // skew with different size
302 mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
303 REPORTER_ASSERT(reporter, !mat.isSimilarity());
304 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
306 // skew with same size at a pivot point
308 mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
309 SkIntToScalar(2), SkIntToScalar(2));
310 REPORTER_ASSERT(reporter, !mat.isSimilarity());
311 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
313 // skew with different size at a pivot point
315 mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
316 SkIntToScalar(2), SkIntToScalar(2));
317 REPORTER_ASSERT(reporter, !mat.isSimilarity());
318 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
322 mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
323 REPORTER_ASSERT(reporter, !mat.isSimilarity());
324 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
328 mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
329 REPORTER_ASSERT(reporter, !mat.isSimilarity());
330 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
333 for (int angle = 0; angle < 360; ++angle) {
335 mat.setRotate(SkIntToScalar(angle));
336 REPORTER_ASSERT(reporter, mat.isSimilarity());
337 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
340 // see if there are any accumulated precision issues
342 for (int i = 1; i < 360; i++) {
343 mat.postRotate(SkIntToScalar(1));
345 REPORTER_ASSERT(reporter, mat.isSimilarity());
346 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
348 // rotate + translate
350 mat.setRotate(SkIntToScalar(30));
351 mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
352 REPORTER_ASSERT(reporter, mat.isSimilarity());
353 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
355 // rotate + uniform scale
357 mat.setRotate(SkIntToScalar(30));
358 mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
359 REPORTER_ASSERT(reporter, mat.isSimilarity());
360 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
362 // rotate + non-uniform scale
364 mat.setRotate(SkIntToScalar(30));
365 mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
366 REPORTER_ASSERT(reporter, !mat.isSimilarity());
367 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
369 // non-uniform scale + rotate
371 mat.setScale(SkIntToScalar(3), SkIntToScalar(2));
372 mat.postRotate(SkIntToScalar(30));
373 REPORTER_ASSERT(reporter, !mat.isSimilarity());
374 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
377 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
378 REPORTER_ASSERT(reporter, !mat.isSimilarity());
379 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
381 // all zero except perspective
383 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
384 REPORTER_ASSERT(reporter, !mat.isSimilarity());
385 REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
387 // scales zero, only skews (rotation)
388 mat.setAll(0, SK_Scalar1, 0,
390 0, 0, SkMatrix::I()[8]);
391 REPORTER_ASSERT(reporter, mat.isSimilarity());
392 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
394 // scales zero, only skews (reflection)
395 mat.setAll(0, SK_Scalar1, 0,
397 0, 0, SkMatrix::I()[8]);
398 REPORTER_ASSERT(reporter, mat.isSimilarity());
399 REPORTER_ASSERT(reporter, mat.preservesRightAngles());
402 // For test_matrix_decomposition, below.
403 static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
404 SkScalar tolerance = SK_ScalarNearlyZero) {
407 SkScalar diff = SkScalarAbs(a - b);
408 if (diff < tolerance) {
415 SkScalar largest = (b > a) ? b : a;
417 if (diff <= largest*tolerance) {
424 static bool check_matrix_recomposition(const SkMatrix& mat,
425 const SkPoint& rotation1,
426 const SkPoint& scale,
427 const SkPoint& rotation2) {
428 SkScalar c1 = rotation1.fX;
429 SkScalar s1 = rotation1.fY;
430 SkScalar scaleX = scale.fX;
431 SkScalar scaleY = scale.fY;
432 SkScalar c2 = rotation2.fX;
433 SkScalar s2 = rotation2.fY;
435 // We do a relative check here because large scale factors cause problems with an absolute check
436 bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
437 scaleX*c1*c2 - scaleY*s1*s2) &&
438 scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
439 -scaleX*s1*c2 - scaleY*c1*s2) &&
440 scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
441 scaleX*c1*s2 + scaleY*s1*c2) &&
442 scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
443 -scaleX*s1*s2 + scaleY*c1*c2);
447 static void test_matrix_decomposition(skiatest::Reporter* reporter) {
449 SkPoint rotation1, scale, rotation2;
451 const float kRotation0 = 15.5f;
452 const float kRotation1 = -50.f;
453 const float kScale0 = 5000.f;
454 const float kScale1 = 0.001f;
458 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
459 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
460 // make sure it doesn't crash if we pass in NULLs
461 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));
464 mat.setRotate(kRotation0);
465 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
466 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
468 // uniform scale only
469 mat.setScale(kScale0, kScale0);
470 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
471 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
473 // anisotropic scale only
474 mat.setScale(kScale1, kScale0);
475 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
476 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
478 // rotation then uniform scale
479 mat.setRotate(kRotation1);
480 mat.postScale(kScale0, kScale0);
481 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
482 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
484 // uniform scale then rotation
485 mat.setScale(kScale0, kScale0);
486 mat.postRotate(kRotation1);
487 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
488 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
490 // rotation then uniform scale+reflection
491 mat.setRotate(kRotation0);
492 mat.postScale(kScale1, -kScale1);
493 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
494 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
496 // uniform scale+reflection, then rotate
497 mat.setScale(kScale0, -kScale0);
498 mat.postRotate(kRotation1);
499 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
500 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
502 // rotation then anisotropic scale
503 mat.setRotate(kRotation1);
504 mat.postScale(kScale1, kScale0);
505 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
506 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
508 // rotation then anisotropic scale
510 mat.postScale(kScale1, kScale0);
511 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
512 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
514 // anisotropic scale then rotation
515 mat.setScale(kScale1, kScale0);
516 mat.postRotate(kRotation0);
517 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
518 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
520 // anisotropic scale then rotation
521 mat.setScale(kScale1, kScale0);
523 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
524 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
526 // rotation, uniform scale, then different rotation
527 mat.setRotate(kRotation1);
528 mat.postScale(kScale0, kScale0);
529 mat.postRotate(kRotation0);
530 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
531 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
533 // rotation, anisotropic scale, then different rotation
534 mat.setRotate(kRotation0);
535 mat.postScale(kScale1, kScale0);
536 mat.postRotate(kRotation1);
537 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
538 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
540 // rotation, anisotropic scale + reflection, then different rotation
541 mat.setRotate(kRotation0);
542 mat.postScale(-kScale1, kScale0);
543 mat.postRotate(kRotation1);
544 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
545 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
547 // try some random matrices
549 for (int m = 0; m < 1000; ++m) {
550 SkScalar rot0 = rand.nextRangeF(-180, 180);
551 SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
552 SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
553 SkScalar rot1 = rand.nextRangeF(-180, 180);
555 mat.postScale(sx, sy);
556 mat.postRotate(rot1);
558 if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
559 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
561 // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
562 SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
563 mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
564 REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
568 // translation shouldn't affect this
569 mat.postTranslate(-1000.f, 1000.f);
570 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
571 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
573 // perspective shouldn't affect this
574 mat[SkMatrix::kMPersp0] = 12.f;
575 mat[SkMatrix::kMPersp1] = 4.f;
576 mat[SkMatrix::kMPersp2] = 1872.f;
577 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
578 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
580 // degenerate matrices
581 // mostly zero entries
583 mat[SkMatrix::kMScaleX] = 0.f;
584 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
586 mat[SkMatrix::kMScaleY] = 0.f;
587 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
589 // linearly dependent entries
590 mat[SkMatrix::kMScaleX] = 1.f;
591 mat[SkMatrix::kMSkewX] = 2.f;
592 mat[SkMatrix::kMSkewY] = 4.f;
593 mat[SkMatrix::kMScaleY] = 8.f;
594 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
597 // For test_matrix_homogeneous, below.
598 static bool scalar_array_nearly_equal_relative(const SkScalar a[], const SkScalar b[], int count) {
599 for (int i = 0; i < count; ++i) {
600 if (!scalar_nearly_equal_relative(a[i], b[i])) {
607 // For test_matrix_homogeneous, below.
608 // Maps a single triple in src using m and compares results to those in dst
609 static bool naive_homogeneous_mapping(const SkMatrix& m, const SkScalar src[3],
610 const SkScalar dst[3]) {
612 SkScalar ms[9] = {m[0], m[1], m[2],
615 res[0] = src[0] * ms[0] + src[1] * ms[1] + src[2] * ms[2];
616 res[1] = src[0] * ms[3] + src[1] * ms[4] + src[2] * ms[5];
617 res[2] = src[0] * ms[6] + src[1] * ms[7] + src[2] * ms[8];
618 return scalar_array_nearly_equal_relative(res, dst, 3);
621 static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
624 const float kRotation0 = 15.5f;
625 const float kRotation1 = -50.f;
626 const float kScale0 = 5000.f;
628 const int kTripleCount = 1000;
629 const int kMatrixCount = 1000;
632 SkScalar randTriples[3*kTripleCount];
633 for (int i = 0; i < 3*kTripleCount; ++i) {
634 randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
637 SkMatrix mats[kMatrixCount];
638 for (int i = 0; i < kMatrixCount; ++i) {
639 for (int j = 0; j < 9; ++j) {
640 mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
647 SkScalar dst[3*kTripleCount];
648 mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
649 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
654 mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
655 SkScalar dst[3*kTripleCount];
656 mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
657 SkScalar zeros[3] = {0.f, 0.f, 0.f};
658 for (int i = 0; i < kTripleCount; ++i) {
659 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
665 SkScalar zeros[3] = {0.f, 0.f, 0.f};
666 for (int i = 0; i < kMatrixCount; ++i) {
668 mats[i].mapHomogeneousPoints(dst, zeros, 1);
669 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
673 // doesn't crash with null dst, src, count == 0
675 mats[0].mapHomogeneousPoints(NULL, NULL, 0);
678 // uniform scale of point
680 mat.setScale(kScale0, kScale0);
682 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
684 pnt.set(src[0], src[1]);
685 mat.mapHomogeneousPoints(dst, src, 1);
686 mat.mapPoints(&pnt, &pnt, 1);
687 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
688 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
689 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
694 mat.setRotate(kRotation0);
696 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
698 pnt.set(src[0], src[1]);
699 mat.mapHomogeneousPoints(dst, src, 1);
700 mat.mapPoints(&pnt, &pnt, 1);
701 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
702 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
703 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
706 // rotation, scale, rotation of point
708 mat.setRotate(kRotation1);
709 mat.postScale(kScale0, kScale0);
710 mat.postRotate(kRotation0);
712 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
714 pnt.set(src[0], src[1]);
715 mat.mapHomogeneousPoints(dst, src, 1);
716 mat.mapPoints(&pnt, &pnt, 1);
717 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
718 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
719 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
722 // compare with naive approach
724 for (int i = 0; i < kMatrixCount; ++i) {
725 for (int j = 0; j < kTripleCount; ++j) {
727 mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
728 REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
735 DEF_TEST(Matrix, reporter) {
736 SkMatrix mat, inverse, iden1, iden2;
739 mat.setTranslate(SK_Scalar1, SK_Scalar1);
740 REPORTER_ASSERT(reporter, mat.invert(&inverse));
741 iden1.setConcat(mat, inverse);
742 REPORTER_ASSERT(reporter, is_identity(iden1));
744 mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
745 REPORTER_ASSERT(reporter, mat.invert(&inverse));
746 iden1.setConcat(mat, inverse);
747 REPORTER_ASSERT(reporter, is_identity(iden1));
748 test_flatten(reporter, mat);
750 mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
751 REPORTER_ASSERT(reporter, mat.invert(&inverse));
752 iden1.setConcat(mat, inverse);
753 REPORTER_ASSERT(reporter, is_identity(iden1));
754 test_flatten(reporter, mat);
756 mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
757 mat.postRotate(SkIntToScalar(25));
758 REPORTER_ASSERT(reporter, mat.invert(NULL));
759 REPORTER_ASSERT(reporter, mat.invert(&inverse));
760 iden1.setConcat(mat, inverse);
761 REPORTER_ASSERT(reporter, is_identity(iden1));
762 iden2.setConcat(inverse, mat);
763 REPORTER_ASSERT(reporter, is_identity(iden2));
764 test_flatten(reporter, mat);
765 test_flatten(reporter, iden2);
767 mat.setScale(0, SK_Scalar1);
768 REPORTER_ASSERT(reporter, !mat.invert(NULL));
769 REPORTER_ASSERT(reporter, !mat.invert(&inverse));
770 mat.setScale(SK_Scalar1, 0);
771 REPORTER_ASSERT(reporter, !mat.invert(NULL));
772 REPORTER_ASSERT(reporter, !mat.invert(&inverse));
774 // rectStaysRect test
776 static const struct {
777 SkScalar m00, m01, m10, m11;
780 gRectStaysRectSamples[] = {
781 { 0, 0, 0, 0, false },
782 { 0, 0, 0, SK_Scalar1, false },
783 { 0, 0, SK_Scalar1, 0, false },
784 { 0, 0, SK_Scalar1, SK_Scalar1, false },
785 { 0, SK_Scalar1, 0, 0, false },
786 { 0, SK_Scalar1, 0, SK_Scalar1, false },
787 { 0, SK_Scalar1, SK_Scalar1, 0, true },
788 { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false },
789 { SK_Scalar1, 0, 0, 0, false },
790 { SK_Scalar1, 0, 0, SK_Scalar1, true },
791 { SK_Scalar1, 0, SK_Scalar1, 0, false },
792 { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false },
793 { SK_Scalar1, SK_Scalar1, 0, 0, false },
794 { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false },
795 { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false },
796 { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }
799 for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
803 m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
804 m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01);
805 m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10);
806 m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
807 REPORTER_ASSERT(reporter,
808 m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
813 mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
814 mat.set(SkMatrix::kMSkewX, SkIntToScalar(2));
815 mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
816 mat.set(SkMatrix::kMSkewY, SkIntToScalar(4));
817 mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
818 mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
820 REPORTER_ASSERT(reporter, mat.asAffine(affine));
822 #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
823 REPORTER_ASSERT(reporter, affineEqual(ScaleX));
824 REPORTER_ASSERT(reporter, affineEqual(SkewY));
825 REPORTER_ASSERT(reporter, affineEqual(SkewX));
826 REPORTER_ASSERT(reporter, affineEqual(ScaleY));
827 REPORTER_ASSERT(reporter, affineEqual(TransX));
828 REPORTER_ASSERT(reporter, affineEqual(TransY));
831 mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
832 REPORTER_ASSERT(reporter, !mat.asAffine(affine));
838 mat.set(SkMatrix::kMSkewX, -zero);
839 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
843 mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
844 mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
845 REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
847 test_matrix_min_max_scale(reporter);
848 test_matrix_preserve_shape(reporter);
849 test_matrix_recttorect(reporter);
850 test_matrix_decomposition(reporter);
851 test_matrix_homogeneous(reporter);
854 DEF_TEST(Matrix_Concat, r) {
856 a.setTranslate(10, 20);
862 expected.setConcat(a,b);
864 REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b));