1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 #include "Test.h" 9 #include "SkMath.h" 10 #include "SkMatrix.h" 11 #include "SkRandom.h" 12 13 static bool nearly_equal_scalar(SkScalar a, SkScalar b) { 14 // Note that we get more compounded error for multiple operations when 15 // SK_SCALAR_IS_FIXED. 16 #ifdef SK_SCALAR_IS_FLOAT 17 const SkScalar tolerance = SK_Scalar1 / 200000; 18 #else 19 const SkScalar tolerance = SK_Scalar1 / 1024; 20 #endif 21 22 return SkScalarAbs(a - b) <= tolerance; 23 } 24 25 static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) { 26 for (int i = 0; i < 9; i++) { 27 if (!nearly_equal_scalar(a[i], b[i])) { 28 printf("not equal %g %g\n", (float)a[i], (float)b[i]); 29 return false; 30 } 31 } 32 return true; 33 } 34 35 static bool is_identity(const SkMatrix& m) { 36 SkMatrix identity; 37 identity.reset(); 38 return nearly_equal(m, identity); 39 } 40 41 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) { 42 // add 100 in case we have a bug, I don't want to kill my stack in the test 43 char buffer[SkMatrix::kMaxFlattenSize + 100]; 44 uint32_t size1 = m.flatten(NULL); 45 uint32_t size2 = m.flatten(buffer); 46 REPORTER_ASSERT(reporter, size1 == size2); 47 REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize); 48 49 SkMatrix m2; 50 uint32_t size3 = m2.unflatten(buffer); 51 REPORTER_ASSERT(reporter, size1 == size2); 52 REPORTER_ASSERT(reporter, m == m2); 53 54 char buffer2[SkMatrix::kMaxFlattenSize + 100]; 55 size3 = m2.flatten(buffer2); 56 REPORTER_ASSERT(reporter, size1 == size2); 57 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0); 58 } 59 60 void test_matrix_max_stretch(skiatest::Reporter* reporter) { 61 SkMatrix identity; 62 identity.reset(); 63 REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch()); 64 65 SkMatrix scale; 66 scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4); 67 REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch()); 68 69 SkMatrix rot90Scale; 70 rot90Scale.setRotate(90 * SK_Scalar1); 71 rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2); 72 REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch()); 73 74 SkMatrix rotate; 75 rotate.setRotate(128 * SK_Scalar1); 76 REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero); 77 78 SkMatrix translate; 79 translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1); 80 REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch()); 81 82 SkMatrix perspX; 83 perspX.reset(); 84 perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000)); 85 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch()); 86 87 SkMatrix perspY; 88 perspY.reset(); 89 perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500)); 90 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch()); 91 92 SkMatrix baseMats[] = {scale, rot90Scale, rotate, 93 translate, perspX, perspY}; 94 SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)]; 95 for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) { 96 mats[i] = baseMats[i]; 97 bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]); 98 REPORTER_ASSERT(reporter, invertable); 99 } 100 SkRandom rand; 101 for (int m = 0; m < 1000; ++m) { 102 SkMatrix mat; 103 mat.reset(); 104 for (int i = 0; i < 4; ++i) { 105 int x = rand.nextU() % SK_ARRAY_COUNT(mats); 106 mat.postConcat(mats[x]); 107 } 108 SkScalar stretch = mat.getMaxStretch(); 109 110 if ((stretch < 0) != mat.hasPerspective()) { 111 stretch = mat.getMaxStretch(); 112 } 113 114 REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective()); 115 116 if (mat.hasPerspective()) { 117 m -= 1; // try another non-persp matrix 118 continue; 119 } 120 121 // test a bunch of vectors. None should be scaled by more than stretch 122 // (modulo some error) and we should find a vector that is scaled by 123 // almost stretch. 124 static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100; 125 static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100; 126 SkScalar max = 0; 127 SkVector vectors[1000]; 128 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { 129 vectors[i].fX = rand.nextSScalar1(); 130 vectors[i].fY = rand.nextSScalar1(); 131 if (!vectors[i].normalize()) { 132 i -= 1; 133 continue; 134 } 135 } 136 mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors)); 137 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { 138 SkScalar d = vectors[i].length(); 139 REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol); 140 if (max < d) { 141 max = d; 142 } 143 } 144 REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol); 145 } 146 } 147 148 void TestMatrix(skiatest::Reporter* reporter) { 149 SkMatrix mat, inverse, iden1, iden2; 150 151 mat.reset(); 152 mat.setTranslate(SK_Scalar1, SK_Scalar1); 153 mat.invert(&inverse); 154 iden1.setConcat(mat, inverse); 155 REPORTER_ASSERT(reporter, is_identity(iden1)); 156 157 mat.setScale(SkIntToScalar(2), SkIntToScalar(2)); 158 mat.invert(&inverse); 159 iden1.setConcat(mat, inverse); 160 REPORTER_ASSERT(reporter, is_identity(iden1)); 161 test_flatten(reporter, mat); 162 163 mat.setScale(SK_Scalar1/2, SK_Scalar1/2); 164 mat.invert(&inverse); 165 iden1.setConcat(mat, inverse); 166 REPORTER_ASSERT(reporter, is_identity(iden1)); 167 test_flatten(reporter, mat); 168 169 mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0); 170 mat.postRotate(SkIntToScalar(25)); 171 REPORTER_ASSERT(reporter, mat.invert(NULL)); 172 mat.invert(&inverse); 173 iden1.setConcat(mat, inverse); 174 REPORTER_ASSERT(reporter, is_identity(iden1)); 175 iden2.setConcat(inverse, mat); 176 REPORTER_ASSERT(reporter, is_identity(iden2)); 177 test_flatten(reporter, mat); 178 test_flatten(reporter, iden2); 179 180 // rectStaysRect test 181 { 182 static const struct { 183 SkScalar m00, m01, m10, m11; 184 bool mStaysRect; 185 } 186 gRectStaysRectSamples[] = { 187 { 0, 0, 0, 0, false }, 188 { 0, 0, 0, SK_Scalar1, false }, 189 { 0, 0, SK_Scalar1, 0, false }, 190 { 0, 0, SK_Scalar1, SK_Scalar1, false }, 191 { 0, SK_Scalar1, 0, 0, false }, 192 { 0, SK_Scalar1, 0, SK_Scalar1, false }, 193 { 0, SK_Scalar1, SK_Scalar1, 0, true }, 194 { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }, 195 { SK_Scalar1, 0, 0, 0, false }, 196 { SK_Scalar1, 0, 0, SK_Scalar1, true }, 197 { SK_Scalar1, 0, SK_Scalar1, 0, false }, 198 { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false }, 199 { SK_Scalar1, SK_Scalar1, 0, 0, false }, 200 { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false }, 201 { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false }, 202 { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false } 203 }; 204 205 for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) { 206 SkMatrix m; 207 208 m.reset(); 209 m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00); 210 m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01); 211 m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10); 212 m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11); 213 REPORTER_ASSERT(reporter, 214 m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect); 215 } 216 } 217 218 mat.reset(); 219 mat.set(SkMatrix::kMScaleX, SkIntToScalar(1)); 220 mat.set(SkMatrix::kMSkewX, SkIntToScalar(2)); 221 mat.set(SkMatrix::kMTransX, SkIntToScalar(3)); 222 mat.set(SkMatrix::kMSkewY, SkIntToScalar(4)); 223 mat.set(SkMatrix::kMScaleY, SkIntToScalar(5)); 224 mat.set(SkMatrix::kMTransY, SkIntToScalar(6)); 225 SkScalar affine[6]; 226 REPORTER_ASSERT(reporter, mat.asAffine(affine)); 227 228 #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e) 229 REPORTER_ASSERT(reporter, affineEqual(ScaleX)); 230 REPORTER_ASSERT(reporter, affineEqual(SkewY)); 231 REPORTER_ASSERT(reporter, affineEqual(SkewX)); 232 REPORTER_ASSERT(reporter, affineEqual(ScaleY)); 233 REPORTER_ASSERT(reporter, affineEqual(TransX)); 234 REPORTER_ASSERT(reporter, affineEqual(TransY)); 235 #undef affineEqual 236 237 mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2)); 238 REPORTER_ASSERT(reporter, !mat.asAffine(affine)); 239 240 test_matrix_max_stretch(reporter); 241 } 242 243 #include "TestClassDef.h" 244 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix) 245