blob: d9c2677a57782e072eb6f00ca4c57efb514dc4d6 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkMatrix.h"
9#include "Sk64.h"
10#include "SkFloatBits.h"
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +000011#include "SkOnce.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkString.h"
13
14#ifdef SK_SCALAR_IS_FLOAT
15 #define kMatrix22Elem SK_Scalar1
reed@android.comab7ac022009-09-18 13:38:43 +000016
17 static inline float SkDoubleToFloat(double x) {
18 return static_cast<float>(x);
19 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#else
21 #define kMatrix22Elem SK_Fract1
22#endif
23
24/* [scale-x skew-x trans-x] [X] [X']
25 [skew-y scale-y trans-y] * [Y] = [Y']
26 [persp-0 persp-1 persp-2] [1] [1 ]
27*/
28
29void SkMatrix::reset() {
30 fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000031 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 fMat[kMTransX] = fMat[kMTransY] =
33 fMat[kMPersp0] = fMat[kMPersp1] = 0;
34 fMat[kMPersp2] = kMatrix22Elem;
35
36 this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
37}
38
reed@android.com8a1c16f2008-12-17 15:59:43 +000039// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
40enum {
41 kTranslate_Shift,
42 kScale_Shift,
43 kAffine_Shift,
44 kPerspective_Shift,
45 kRectStaysRect_Shift
46};
47
48#ifdef SK_SCALAR_IS_FLOAT
49 static const int32_t kScalar1Int = 0x3f800000;
reed@android.com8a1c16f2008-12-17 15:59:43 +000050#else
51 #define scalarAsInt(x) (x)
52 static const int32_t kScalar1Int = (1 << 16);
djsollen@google.com9e59cb62013-11-21 18:29:53 +000053 static const int32_t kPersp1Int = (1 << 30);
reed@android.com8a1c16f2008-12-17 15:59:43 +000054#endif
55
mtklein@google.com6e223af2013-11-22 20:43:54 +000056#ifdef SK_SCALAR_SLOW_COMPARES
57 static const int32_t kPersp1Int = 0x3f800000;
58#endif
59
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000060uint8_t SkMatrix::computePerspectiveTypeMask() const {
junov@chromium.org6fc56992012-07-12 14:01:32 +000061#ifdef SK_SCALAR_SLOW_COMPARES
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000062 if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
63 SkScalarAs2sCompliment(fMat[kMPersp1]) |
djsollen@google.com9e59cb62013-11-21 18:29:53 +000064 (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000065 return SkToU8(kORableMasks);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000066 }
junov@chromium.org6fc56992012-07-12 14:01:32 +000067#else
68 // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
69 // is a win, but replacing those below is not. We don't yet understand
70 // that result.
71 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 ||
72 fMat[kMPersp2] != kMatrix22Elem) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +000073 // If this is a perspective transform, we return true for all other
bsalomon@google.com19263b12012-07-13 13:36:38 +000074 // transform flags - this does not disable any optimizations, respects
rmistry@google.comfbfcd562012-08-23 18:09:54 +000075 // the rule that the type mask must be conservative, and speeds up
junov@chromium.org6fc56992012-07-12 14:01:32 +000076 // type mask computation.
77 return SkToU8(kORableMasks);
78 }
79#endif
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000080
junov@chromium.org6fc56992012-07-12 14:01:32 +000081 return SkToU8(kOnlyPerspectiveValid_Mask | kUnknown_Mask);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000082}
83
reed@android.com8a1c16f2008-12-17 15:59:43 +000084uint8_t SkMatrix::computeTypeMask() const {
85 unsigned mask = 0;
86
tomhudson@google.comac385252011-06-06 15:18:28 +000087#ifdef SK_SCALAR_SLOW_COMPARES
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
89 SkScalarAs2sCompliment(fMat[kMPersp1]) |
djsollen@google.com9e59cb62013-11-21 18:29:53 +000090 (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000091 return SkToU8(kORableMasks);
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 }
tomhudson@google.comac385252011-06-06 15:18:28 +000093
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 if (SkScalarAs2sCompliment(fMat[kMTransX]) |
95 SkScalarAs2sCompliment(fMat[kMTransY])) {
96 mask |= kTranslate_Mask;
97 }
tomhudson@google.comac385252011-06-06 15:18:28 +000098#else
tomhudson@google.comac385252011-06-06 15:18:28 +000099 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 ||
tomhudson@google.com521ed7c2011-06-06 17:21:44 +0000100 fMat[kMPersp2] != kMatrix22Elem) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000101 // Once it is determined that that this is a perspective transform,
102 // all other flags are moot as far as optimizations are concerned.
103 return SkToU8(kORableMasks);
tomhudson@google.comac385252011-06-06 15:18:28 +0000104 }
105
106 if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
107 mask |= kTranslate_Mask;
108 }
109#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110
111 int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
112 int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
113 int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
114 int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
tomhudson@google.comac385252011-06-06 15:18:28 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 if (m01 | m10) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000117 // The skew components may be scale-inducing, unless we are dealing
118 // with a pure rotation. Testing for a pure rotation is expensive,
119 // so we opt for being conservative by always setting the scale bit.
120 // along with affine.
121 // By doing this, we are also ensuring that matrices have the same
122 // type masks as their inverses.
123 mask |= kAffine_Mask | kScale_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124
junov@chromium.org6fc56992012-07-12 14:01:32 +0000125 // For rectStaysRect, in the affine case, we only need check that
126 // the primary diagonal is all zeros and that the secondary diagonal
127 // is all non-zero.
tomhudson@google.comac385252011-06-06 15:18:28 +0000128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 // map non-zero to 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130 m01 = m01 != 0;
131 m10 = m10 != 0;
tomhudson@google.comac385252011-06-06 15:18:28 +0000132
junov@chromium.org6fc56992012-07-12 14:01:32 +0000133 int dp0 = 0 == (m00 | m11) ; // true if both are 0
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 int ds1 = m01 & m10; // true if both are 1
tomhudson@google.comac385252011-06-06 15:18:28 +0000135
junov@chromium.org6fc56992012-07-12 14:01:32 +0000136 mask |= (dp0 & ds1) << kRectStaysRect_Shift;
137 } else {
138 // Only test for scale explicitly if not affine, since affine sets the
139 // scale bit.
140 if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
141 mask |= kScale_Mask;
142 }
143
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000144 // Not affine, therefore we already know secondary diagonal is
junov@chromium.org6fc56992012-07-12 14:01:32 +0000145 // all zeros, so we just need to check that primary diagonal is
146 // all non-zero.
147
148 // map non-zero to 1
149 m00 = m00 != 0;
150 m11 = m11 != 0;
151
152 // record if the (p)rimary diagonal is all non-zero
153 mask |= (m00 & m11) << kRectStaysRect_Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 }
155
156 return SkToU8(mask);
157}
158
159///////////////////////////////////////////////////////////////////////////////
160
reed@google.com3fb51872011-06-01 15:11:22 +0000161#ifdef SK_SCALAR_IS_FLOAT
162
163bool operator==(const SkMatrix& a, const SkMatrix& b) {
164 const SkScalar* SK_RESTRICT ma = a.fMat;
165 const SkScalar* SK_RESTRICT mb = b.fMat;
166
167 return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
168 ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
169 ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
170}
171
172#endif
173
174///////////////////////////////////////////////////////////////////////////////
175
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000176// helper function to determine if upper-left 2x2 of matrix is degenerate
skia.committer@gmail.com16d53aa2013-07-30 07:01:00 +0000177static inline bool is_degenerate_2x2(SkScalar scaleX, SkScalar skewX,
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000178 SkScalar skewY, SkScalar scaleY) {
179 SkScalar perp_dot = scaleX*scaleY - skewX*skewY;
180 return SkScalarNearlyZero(perp_dot, SK_ScalarNearlyZero*SK_ScalarNearlyZero);
181}
182
183///////////////////////////////////////////////////////////////////////////////
184
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000185bool SkMatrix::isSimilarity(SkScalar tol) const {
186 // if identity or translate matrix
187 TypeMask mask = this->getType();
188 if (mask <= kTranslate_Mask) {
189 return true;
190 }
191 if (mask & kPerspective_Mask) {
192 return false;
193 }
194
195 SkScalar mx = fMat[kMScaleX];
196 SkScalar my = fMat[kMScaleY];
197 // if no skew, can just compare scale factors
198 if (!(mask & kAffine_Mask)) {
199 return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
200 }
201 SkScalar sx = fMat[kMSkewX];
202 SkScalar sy = fMat[kMSkewY];
203
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000204 if (is_degenerate_2x2(mx, sx, sy, my)) {
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000205 return false;
206 }
207
208 // it has scales and skews, but it could also be rotation, check it out.
209 SkVector vec[2];
210 vec[0].set(mx, sx);
211 vec[1].set(sy, my);
212
213 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
214 SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000215 SkScalarSquare(tol));
216}
217
218bool SkMatrix::preservesRightAngles(SkScalar tol) const {
219 TypeMask mask = this->getType();
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000220
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000221 if (mask <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
222 // identity, translate and/or scale
223 return true;
224 }
225 if (mask & kPerspective_Mask) {
226 return false;
227 }
228
229 SkASSERT(mask & kAffine_Mask);
230
231 SkScalar mx = fMat[kMScaleX];
232 SkScalar my = fMat[kMScaleY];
233 SkScalar sx = fMat[kMSkewX];
234 SkScalar sy = fMat[kMSkewY];
235
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000236 if (is_degenerate_2x2(mx, sx, sy, my)) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000237 return false;
238 }
239
240 // it has scales and skews, but it could also be rotation, check it out.
241 SkVector vec[2];
242 vec[0].set(mx, sx);
243 vec[1].set(sy, my);
244
245 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
246 SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
247 SkScalarSquare(tol));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000248}
249
250///////////////////////////////////////////////////////////////////////////////
251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
reed@google.comc0784db2013-12-13 21:16:12 +0000253 if (dx || dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 fMat[kMTransX] = dx;
255 fMat[kMTransY] = dy;
256
257 fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000258 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 fMat[kMPersp0] = fMat[kMPersp1] = 0;
260 fMat[kMPersp2] = kMatrix22Elem;
261
262 this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
263 } else {
264 this->reset();
265 }
266}
267
268bool SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000269 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 SkMatrix m;
271 m.setTranslate(dx, dy);
272 return this->preConcat(m);
273 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000274
reed@google.comc0784db2013-12-13 21:16:12 +0000275 if (dx || dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) +
277 SkScalarMul(fMat[kMSkewX], dy);
278 fMat[kMTransY] += SkScalarMul(fMat[kMSkewY], dx) +
279 SkScalarMul(fMat[kMScaleY], dy);
280
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000281 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 }
283 return true;
284}
285
286bool SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000287 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 SkMatrix m;
289 m.setTranslate(dx, dy);
290 return this->postConcat(m);
291 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000292
reed@google.comc0784db2013-12-13 21:16:12 +0000293 if (dx || dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 fMat[kMTransX] += dx;
295 fMat[kMTransY] += dy;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000296 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 }
298 return true;
299}
300
301///////////////////////////////////////////////////////////////////////////////
302
303void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@google.comf244f902011-09-06 21:02:36 +0000304 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
305 this->reset();
306 } else {
307 fMat[kMScaleX] = sx;
308 fMat[kMScaleY] = sy;
309 fMat[kMTransX] = px - SkScalarMul(sx, px);
310 fMat[kMTransY] = py - SkScalarMul(sy, py);
311 fMat[kMPersp2] = kMatrix22Elem;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000313 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000314 fMat[kMPersp0] = fMat[kMPersp1] = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000315
reed@google.comf244f902011-09-06 21:02:36 +0000316 this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
317 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318}
319
320void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000321 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
322 this->reset();
323 } else {
324 fMat[kMScaleX] = sx;
325 fMat[kMScaleY] = sy;
326 fMat[kMPersp2] = kMatrix22Elem;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327
reed@google.comf244f902011-09-06 21:02:36 +0000328 fMat[kMTransX] = fMat[kMTransY] =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000329 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000330 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331
reed@google.comf244f902011-09-06 21:02:36 +0000332 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
333 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334}
335
bsalomon@google.com5c638652011-07-18 19:31:59 +0000336bool SkMatrix::setIDiv(int divx, int divy) {
337 if (!divx || !divy) {
338 return false;
339 }
340 this->setScale(SK_Scalar1 / divx, SK_Scalar1 / divy);
341 return true;
342}
343
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
345 SkMatrix m;
346 m.setScale(sx, sy, px, py);
347 return this->preConcat(m);
348}
349
350bool SkMatrix::preScale(SkScalar sx, SkScalar sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000351 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
352 return true;
353 }
354
reed@google.com3fb51872011-06-01 15:11:22 +0000355#ifdef SK_SCALAR_IS_FIXED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 SkMatrix m;
357 m.setScale(sx, sy);
358 return this->preConcat(m);
reed@google.com3fb51872011-06-01 15:11:22 +0000359#else
360 // the assumption is that these multiplies are very cheap, and that
361 // a full concat and/or just computing the matrix type is more expensive.
362 // Also, the fixed-point case checks for overflow, but the float doesn't,
363 // so we can get away with these blind multiplies.
364
365 fMat[kMScaleX] = SkScalarMul(fMat[kMScaleX], sx);
366 fMat[kMSkewY] = SkScalarMul(fMat[kMSkewY], sx);
367 fMat[kMPersp0] = SkScalarMul(fMat[kMPersp0], sx);
368
369 fMat[kMSkewX] = SkScalarMul(fMat[kMSkewX], sy);
370 fMat[kMScaleY] = SkScalarMul(fMat[kMScaleY], sy);
371 fMat[kMPersp1] = SkScalarMul(fMat[kMPersp1], sy);
372
373 this->orTypeMask(kScale_Mask);
374 return true;
375#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376}
377
378bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@google.comf244f902011-09-06 21:02:36 +0000379 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
380 return true;
381 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 SkMatrix m;
383 m.setScale(sx, sy, px, py);
384 return this->postConcat(m);
385}
386
387bool SkMatrix::postScale(SkScalar sx, SkScalar sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000388 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
389 return true;
390 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 SkMatrix m;
392 m.setScale(sx, sy);
393 return this->postConcat(m);
394}
395
396#ifdef SK_SCALAR_IS_FIXED
397 static inline SkFixed roundidiv(SkFixed numer, int denom) {
398 int ns = numer >> 31;
399 int ds = denom >> 31;
400 numer = (numer ^ ns) - ns;
401 denom = (denom ^ ds) - ds;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000402
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 SkFixed answer = (numer + (denom >> 1)) / denom;
404 int as = ns ^ ds;
405 return (answer ^ as) - as;
406 }
407#endif
408
409// this guy perhaps can go away, if we have a fract/high-precision way to
410// scale matrices
411bool SkMatrix::postIDiv(int divx, int divy) {
412 if (divx == 0 || divy == 0) {
413 return false;
414 }
415
416#ifdef SK_SCALAR_IS_FIXED
417 fMat[kMScaleX] = roundidiv(fMat[kMScaleX], divx);
418 fMat[kMSkewX] = roundidiv(fMat[kMSkewX], divx);
419 fMat[kMTransX] = roundidiv(fMat[kMTransX], divx);
420
421 fMat[kMScaleY] = roundidiv(fMat[kMScaleY], divy);
422 fMat[kMSkewY] = roundidiv(fMat[kMSkewY], divy);
423 fMat[kMTransY] = roundidiv(fMat[kMTransY], divy);
424#else
425 const float invX = 1.f / divx;
426 const float invY = 1.f / divy;
427
428 fMat[kMScaleX] *= invX;
429 fMat[kMSkewX] *= invX;
430 fMat[kMTransX] *= invX;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000431
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 fMat[kMScaleY] *= invY;
433 fMat[kMSkewY] *= invY;
434 fMat[kMTransY] *= invY;
435#endif
436
437 this->setTypeMask(kUnknown_Mask);
438 return true;
439}
440
441////////////////////////////////////////////////////////////////////////////////////
442
443void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV,
444 SkScalar px, SkScalar py) {
445 const SkScalar oneMinusCosV = SK_Scalar1 - cosV;
446
447 fMat[kMScaleX] = cosV;
448 fMat[kMSkewX] = -sinV;
449 fMat[kMTransX] = SkScalarMul(sinV, py) + SkScalarMul(oneMinusCosV, px);
450
451 fMat[kMSkewY] = sinV;
452 fMat[kMScaleY] = cosV;
453 fMat[kMTransY] = SkScalarMul(-sinV, px) + SkScalarMul(oneMinusCosV, py);
454
455 fMat[kMPersp0] = fMat[kMPersp1] = 0;
456 fMat[kMPersp2] = kMatrix22Elem;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000457
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000458 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459}
460
461void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
462 fMat[kMScaleX] = cosV;
463 fMat[kMSkewX] = -sinV;
464 fMat[kMTransX] = 0;
465
466 fMat[kMSkewY] = sinV;
467 fMat[kMScaleY] = cosV;
468 fMat[kMTransY] = 0;
469
470 fMat[kMPersp0] = fMat[kMPersp1] = 0;
471 fMat[kMPersp2] = kMatrix22Elem;
472
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000473 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474}
475
476void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
477 SkScalar sinV, cosV;
478 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
479 this->setSinCos(sinV, cosV, px, py);
480}
481
482void SkMatrix::setRotate(SkScalar degrees) {
483 SkScalar sinV, cosV;
484 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
485 this->setSinCos(sinV, cosV);
486}
487
488bool SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
489 SkMatrix m;
490 m.setRotate(degrees, px, py);
491 return this->preConcat(m);
492}
493
494bool SkMatrix::preRotate(SkScalar degrees) {
495 SkMatrix m;
496 m.setRotate(degrees);
497 return this->preConcat(m);
498}
499
500bool SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
501 SkMatrix m;
502 m.setRotate(degrees, px, py);
503 return this->postConcat(m);
504}
505
506bool SkMatrix::postRotate(SkScalar degrees) {
507 SkMatrix m;
508 m.setRotate(degrees);
509 return this->postConcat(m);
510}
511
512////////////////////////////////////////////////////////////////////////////////////
513
514void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
515 fMat[kMScaleX] = SK_Scalar1;
516 fMat[kMSkewX] = sx;
517 fMat[kMTransX] = SkScalarMul(-sx, py);
518
519 fMat[kMSkewY] = sy;
520 fMat[kMScaleY] = SK_Scalar1;
521 fMat[kMTransY] = SkScalarMul(-sy, px);
522
523 fMat[kMPersp0] = fMat[kMPersp1] = 0;
524 fMat[kMPersp2] = kMatrix22Elem;
525
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000526 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527}
528
529void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
530 fMat[kMScaleX] = SK_Scalar1;
531 fMat[kMSkewX] = sx;
532 fMat[kMTransX] = 0;
533
534 fMat[kMSkewY] = sy;
535 fMat[kMScaleY] = SK_Scalar1;
536 fMat[kMTransY] = 0;
537
538 fMat[kMPersp0] = fMat[kMPersp1] = 0;
539 fMat[kMPersp2] = kMatrix22Elem;
540
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000541 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542}
543
544bool SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
545 SkMatrix m;
546 m.setSkew(sx, sy, px, py);
547 return this->preConcat(m);
548}
549
550bool SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
551 SkMatrix m;
552 m.setSkew(sx, sy);
553 return this->preConcat(m);
554}
555
556bool SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
557 SkMatrix m;
558 m.setSkew(sx, sy, px, py);
559 return this->postConcat(m);
560}
561
562bool SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
563 SkMatrix m;
564 m.setSkew(sx, sy);
565 return this->postConcat(m);
566}
567
568///////////////////////////////////////////////////////////////////////////////
569
570bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst,
571 ScaleToFit align)
572{
573 if (src.isEmpty()) {
574 this->reset();
575 return false;
576 }
577
578 if (dst.isEmpty()) {
reed@android.com4516f472009-06-29 16:25:36 +0000579 sk_bzero(fMat, 8 * sizeof(SkScalar));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
581 } else {
582 SkScalar tx, sx = SkScalarDiv(dst.width(), src.width());
583 SkScalar ty, sy = SkScalarDiv(dst.height(), src.height());
584 bool xLarger = false;
585
586 if (align != kFill_ScaleToFit) {
587 if (sx > sy) {
588 xLarger = true;
589 sx = sy;
590 } else {
591 sy = sx;
592 }
593 }
594
595 tx = dst.fLeft - SkScalarMul(src.fLeft, sx);
596 ty = dst.fTop - SkScalarMul(src.fTop, sy);
597 if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
598 SkScalar diff;
599
600 if (xLarger) {
601 diff = dst.width() - SkScalarMul(src.width(), sy);
602 } else {
603 diff = dst.height() - SkScalarMul(src.height(), sy);
604 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 if (align == kCenter_ScaleToFit) {
607 diff = SkScalarHalf(diff);
608 }
609
610 if (xLarger) {
611 tx += diff;
612 } else {
613 ty += diff;
614 }
615 }
616
617 fMat[kMScaleX] = sx;
618 fMat[kMScaleY] = sy;
619 fMat[kMTransX] = tx;
620 fMat[kMTransY] = ty;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000621 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 fMat[kMPersp0] = fMat[kMPersp1] = 0;
623
reed@google.com97cd69c2012-10-12 14:35:48 +0000624 unsigned mask = kRectStaysRect_Mask;
625 if (sx != SK_Scalar1 || sy != SK_Scalar1) {
626 mask |= kScale_Mask;
627 }
628 if (tx || ty) {
629 mask |= kTranslate_Mask;
630 }
631 this->setTypeMask(mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 }
633 // shared cleanup
634 fMat[kMPersp2] = kMatrix22Elem;
635 return true;
636}
637
638///////////////////////////////////////////////////////////////////////////////
639
640#ifdef SK_SCALAR_IS_FLOAT
641 static inline int fixmuladdmul(float a, float b, float c, float d,
642 float* result) {
reed@android.comab7ac022009-09-18 13:38:43 +0000643 *result = SkDoubleToFloat((double)a * b + (double)c * d);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 return true;
645 }
646
647 static inline bool rowcol3(const float row[], const float col[],
648 float* result) {
649 *result = row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
650 return true;
651 }
652
653 static inline int negifaddoverflows(float& result, float a, float b) {
654 result = a + b;
655 return 0;
656 }
657#else
658 static inline bool fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d,
659 SkFixed* result) {
660 Sk64 tmp1, tmp2;
661 tmp1.setMul(a, b);
662 tmp2.setMul(c, d);
663 tmp1.add(tmp2);
664 if (tmp1.isFixed()) {
665 *result = tmp1.getFixed();
666 return true;
667 }
668 return false;
669 }
670
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 static inline SkFixed fracmuladdmul(SkFixed a, SkFract b, SkFixed c,
672 SkFract d) {
673 Sk64 tmp1, tmp2;
674 tmp1.setMul(a, b);
675 tmp2.setMul(c, d);
676 tmp1.add(tmp2);
677 return tmp1.getFract();
678 }
679
680 static inline bool rowcol3(const SkFixed row[], const SkFixed col[],
681 SkFixed* result) {
682 Sk64 tmp1, tmp2;
683
684 tmp1.setMul(row[0], col[0]); // N * fixed
685 tmp2.setMul(row[1], col[3]); // N * fixed
686 tmp1.add(tmp2);
687
688 tmp2.setMul(row[2], col[6]); // N * fract
689 tmp2.roundRight(14); // make it fixed
690 tmp1.add(tmp2);
691
692 if (tmp1.isFixed()) {
693 *result = tmp1.getFixed();
694 return true;
695 }
696 return false;
697 }
698
699 static inline int negifaddoverflows(SkFixed& result, SkFixed a, SkFixed b) {
700 SkFixed c = a + b;
701 result = c;
702 return (c ^ a) & (c ^ b);
703 }
704#endif
705
706static void normalize_perspective(SkScalar mat[9]) {
707 if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > kMatrix22Elem) {
708 for (int i = 0; i < 9; i++)
709 mat[i] = SkScalarHalf(mat[i]);
710 }
711}
712
713bool SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000714 TypeMask aType = a.getPerspectiveTypeMaskOnly();
715 TypeMask bType = b.getPerspectiveTypeMaskOnly();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000717 if (a.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 *this = b;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000719 } else if (b.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 *this = a;
721 } else {
722 SkMatrix tmp;
723
724 if ((aType | bType) & kPerspective_Mask) {
725 if (!rowcol3(&a.fMat[0], &b.fMat[0], &tmp.fMat[kMScaleX])) {
726 return false;
727 }
728 if (!rowcol3(&a.fMat[0], &b.fMat[1], &tmp.fMat[kMSkewX])) {
729 return false;
730 }
731 if (!rowcol3(&a.fMat[0], &b.fMat[2], &tmp.fMat[kMTransX])) {
732 return false;
733 }
734
735 if (!rowcol3(&a.fMat[3], &b.fMat[0], &tmp.fMat[kMSkewY])) {
736 return false;
737 }
738 if (!rowcol3(&a.fMat[3], &b.fMat[1], &tmp.fMat[kMScaleY])) {
739 return false;
740 }
741 if (!rowcol3(&a.fMat[3], &b.fMat[2], &tmp.fMat[kMTransY])) {
742 return false;
743 }
744
745 if (!rowcol3(&a.fMat[6], &b.fMat[0], &tmp.fMat[kMPersp0])) {
746 return false;
747 }
748 if (!rowcol3(&a.fMat[6], &b.fMat[1], &tmp.fMat[kMPersp1])) {
749 return false;
750 }
751 if (!rowcol3(&a.fMat[6], &b.fMat[2], &tmp.fMat[kMPersp2])) {
752 return false;
753 }
754
755 normalize_perspective(tmp.fMat);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000756 tmp.setTypeMask(kUnknown_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 } else { // not perspective
758 if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMScaleX],
759 a.fMat[kMSkewX], b.fMat[kMSkewY], &tmp.fMat[kMScaleX])) {
760 return false;
761 }
762 if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMSkewX],
763 a.fMat[kMSkewX], b.fMat[kMScaleY], &tmp.fMat[kMSkewX])) {
764 return false;
765 }
766 if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMTransX],
767 a.fMat[kMSkewX], b.fMat[kMTransY], &tmp.fMat[kMTransX])) {
768 return false;
769 }
770 if (negifaddoverflows(tmp.fMat[kMTransX], tmp.fMat[kMTransX],
771 a.fMat[kMTransX]) < 0) {
772 return false;
773 }
774
775 if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMScaleX],
776 a.fMat[kMScaleY], b.fMat[kMSkewY], &tmp.fMat[kMSkewY])) {
777 return false;
778 }
779 if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMSkewX],
780 a.fMat[kMScaleY], b.fMat[kMScaleY], &tmp.fMat[kMScaleY])) {
781 return false;
782 }
783 if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMTransX],
784 a.fMat[kMScaleY], b.fMat[kMTransY], &tmp.fMat[kMTransY])) {
785 return false;
786 }
787 if (negifaddoverflows(tmp.fMat[kMTransY], tmp.fMat[kMTransY],
788 a.fMat[kMTransY]) < 0) {
789 return false;
790 }
791
792 tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0;
793 tmp.fMat[kMPersp2] = kMatrix22Elem;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000794 //SkDebugf("Concat mat non-persp type: %d\n", tmp.getType());
795 //SkASSERT(!(tmp.getType() & kPerspective_Mask));
796 tmp.setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 }
798 *this = tmp;
799 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 return true;
801}
802
803bool SkMatrix::preConcat(const SkMatrix& mat) {
804 // check for identity first, so we don't do a needless copy of ourselves
805 // to ourselves inside setConcat()
806 return mat.isIdentity() || this->setConcat(*this, mat);
807}
808
809bool SkMatrix::postConcat(const SkMatrix& mat) {
810 // check for identity first, so we don't do a needless copy of ourselves
811 // to ourselves inside setConcat()
812 return mat.isIdentity() || this->setConcat(mat, *this);
813}
814
815///////////////////////////////////////////////////////////////////////////////
816
reed@android.com0b9e2db2009-09-16 17:00:17 +0000817/* Matrix inversion is very expensive, but also the place where keeping
818 precision may be most important (here and matrix concat). Hence to avoid
819 bitmap blitting artifacts when walking the inverse, we use doubles for
820 the intermediate math, even though we know that is more expensive.
821 The fixed counter part is us using Sk64 for temp calculations.
822 */
823
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824#ifdef SK_SCALAR_IS_FLOAT
reed@android.com0b9e2db2009-09-16 17:00:17 +0000825 typedef double SkDetScalar;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 #define SkPerspMul(a, b) SkScalarMul(a, b)
reed@android.com0b9e2db2009-09-16 17:00:17 +0000827 #define SkScalarMulShift(a, b, s) SkDoubleToFloat((a) * (b))
828 static double sk_inv_determinant(const float mat[9], int isPerspective,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 int* /* (only used in Fixed case) */) {
830 double det;
831
832 if (isPerspective) {
833 det = mat[SkMatrix::kMScaleX] * ((double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp2] - (double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp1]) +
834 mat[SkMatrix::kMSkewX] * ((double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp0] - (double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp2]) +
835 mat[SkMatrix::kMTransX] * ((double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp1] - (double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp0]);
836 } else {
837 det = (double)mat[SkMatrix::kMScaleX] * mat[SkMatrix::kMScaleY] - (double)mat[SkMatrix::kMSkewX] * mat[SkMatrix::kMSkewY];
838 }
839
senorblanco@chromium.org0e21ec02010-07-20 15:20:01 +0000840 // Since the determinant is on the order of the cube of the matrix members,
841 // compare to the cube of the default nearly-zero constant (although an
842 // estimate of the condition number would be better if it wasn't so expensive).
843 if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 return 0;
845 }
reed@android.com0b9e2db2009-09-16 17:00:17 +0000846 return 1.0 / det;
847 }
reed@android.com0b9e2db2009-09-16 17:00:17 +0000848 // we declar a,b,c,d to all be doubles, because we want to perform
849 // double-precision muls and subtract, even though the original values are
850 // from the matrix, which are floats.
851 static float inline mul_diff_scale(double a, double b, double c, double d,
852 double scale) {
853 return SkDoubleToFloat((a * b - c * d) * scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 }
855#else
reed@android.com0b9e2db2009-09-16 17:00:17 +0000856 typedef SkFixed SkDetScalar;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 #define SkPerspMul(a, b) SkFractMul(a, b)
858 #define SkScalarMulShift(a, b, s) SkMulShift(a, b, s)
859 static void set_muladdmul(Sk64* dst, int32_t a, int32_t b, int32_t c,
860 int32_t d) {
861 Sk64 tmp;
862 dst->setMul(a, b);
863 tmp.setMul(c, d);
864 dst->add(tmp);
865 }
866
867 static SkFixed sk_inv_determinant(const SkFixed mat[9], int isPerspective,
868 int* shift) {
869 Sk64 tmp1, tmp2;
870
871 if (isPerspective) {
872 tmp1.setMul(mat[SkMatrix::kMScaleX], fracmuladdmul(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2], -mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1]));
873 tmp2.setMul(mat[SkMatrix::kMSkewX], fracmuladdmul(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0], -mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2]));
874 tmp1.add(tmp2);
875 tmp2.setMul(mat[SkMatrix::kMTransX], fracmuladdmul(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1], -mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]));
876 tmp1.add(tmp2);
877 } else {
878 tmp1.setMul(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY]);
879 tmp2.setMul(mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
880 tmp1.sub(tmp2);
881 }
882
883 int s = tmp1.getClzAbs();
884 *shift = s;
885
886 SkFixed denom;
887 if (s <= 32) {
888 denom = tmp1.getShiftRight(33 - s);
889 } else {
890 denom = (int32_t)tmp1.fLo << (s - 33);
891 }
892
893 if (denom == 0) {
894 return 0;
895 }
896 /** This could perhaps be a special fractdiv function, since both of its
897 arguments are known to have bit 31 clear and bit 30 set (when they
898 are made positive), thus eliminating the need for calling clz()
899 */
900 return SkFractDiv(SK_Fract1, denom);
901 }
902#endif
903
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000904void SkMatrix::SetAffineIdentity(SkScalar affine[6]) {
905 affine[kAScaleX] = SK_Scalar1;
906 affine[kASkewY] = 0;
907 affine[kASkewX] = 0;
908 affine[kAScaleY] = SK_Scalar1;
909 affine[kATransX] = 0;
910 affine[kATransY] = 0;
911}
912
913bool SkMatrix::asAffine(SkScalar affine[6]) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000914 if (this->hasPerspective()) {
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000915 return false;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000916 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000917 if (affine) {
918 affine[kAScaleX] = this->fMat[kMScaleX];
919 affine[kASkewY] = this->fMat[kMSkewY];
920 affine[kASkewX] = this->fMat[kMSkewX];
921 affine[kAScaleY] = this->fMat[kMScaleY];
922 affine[kATransX] = this->fMat[kMTransX];
923 affine[kATransY] = this->fMat[kMTransY];
924 }
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000925 return true;
926}
927
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000928bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
929 SkASSERT(!this->isIdentity());
reed@google.com2fb96cc2013-01-04 17:02:33 +0000930
931 TypeMask mask = this->getType();
932
933 if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
reed@google.come40591d2013-01-30 15:47:42 +0000934 bool invertible = true;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000935 if (inv) {
936 if (mask & kScale_Mask) {
937 SkScalar invX = fMat[kMScaleX];
938 SkScalar invY = fMat[kMScaleY];
939 if (0 == invX || 0 == invY) {
940 return false;
941 }
942 invX = SkScalarInvert(invX);
943 invY = SkScalarInvert(invY);
944
945 // Must be careful when writing to inv, since it may be the
946 // same memory as this.
947
948 inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
949 inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
950
951 inv->fMat[kMScaleX] = invX;
952 inv->fMat[kMScaleY] = invY;
953 inv->fMat[kMPersp2] = kMatrix22Elem;
954 inv->fMat[kMTransX] = -SkScalarMul(fMat[kMTransX], invX);
955 inv->fMat[kMTransY] = -SkScalarMul(fMat[kMTransY], invY);
956
957 inv->setTypeMask(mask | kRectStaysRect_Mask);
958 } else {
959 // translate only
960 inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
961 }
reed@google.come40591d2013-01-30 15:47:42 +0000962 } else { // inv is NULL, just check if we're invertible
963 if (!fMat[kMScaleX] || !fMat[kMScaleY]) {
964 invertible = false;
965 }
reed@google.com2fb96cc2013-01-04 17:02:33 +0000966 }
reed@google.come40591d2013-01-30 15:47:42 +0000967 return invertible;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000968 }
reed@google.com4a1362a2013-01-04 18:52:16 +0000969
reed@google.com2fb96cc2013-01-04 17:02:33 +0000970 int isPersp = mask & kPerspective_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 int shift;
reed@android.com0b9e2db2009-09-16 17:00:17 +0000972 SkDetScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973
974 if (scale == 0) { // underflow
975 return false;
976 }
977
978 if (inv) {
979 SkMatrix tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000980 if (inv == this) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 inv = &tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000982 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983
984 if (isPersp) {
985 shift = 61 - shift;
986 inv->fMat[kMScaleX] = SkScalarMulShift(SkPerspMul(fMat[kMScaleY], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransY], fMat[kMPersp1]), scale, shift);
987 inv->fMat[kMSkewX] = SkScalarMulShift(SkPerspMul(fMat[kMTransX], fMat[kMPersp1]) - SkPerspMul(fMat[kMSkewX], fMat[kMPersp2]), scale, shift);
988 inv->fMat[kMTransX] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMTransY]) - SkScalarMul(fMat[kMTransX], fMat[kMScaleY]), scale, shift);
989
990 inv->fMat[kMSkewY] = SkScalarMulShift(SkPerspMul(fMat[kMTransY], fMat[kMPersp0]) - SkPerspMul(fMat[kMSkewY], fMat[kMPersp2]), scale, shift);
991 inv->fMat[kMScaleY] = SkScalarMulShift(SkPerspMul(fMat[kMScaleX], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransX], fMat[kMPersp0]), scale, shift);
992 inv->fMat[kMTransY] = SkScalarMulShift(SkScalarMul(fMat[kMTransX], fMat[kMSkewY]) - SkScalarMul(fMat[kMScaleX], fMat[kMTransY]), scale, shift);
993
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000994 inv->fMat[kMPersp0] = SkScalarMulShift(SkScalarMul(fMat[kMSkewY], fMat[kMPersp1]) - SkScalarMul(fMat[kMScaleY], fMat[kMPersp0]), scale, shift);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995 inv->fMat[kMPersp1] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMPersp0]) - SkScalarMul(fMat[kMScaleX], fMat[kMPersp1]), scale, shift);
996 inv->fMat[kMPersp2] = SkScalarMulShift(SkScalarMul(fMat[kMScaleX], fMat[kMScaleY]) - SkScalarMul(fMat[kMSkewX], fMat[kMSkewY]), scale, shift);
997#ifdef SK_SCALAR_IS_FIXED
998 if (SkAbs32(inv->fMat[kMPersp2]) > SK_Fixed1) {
999 Sk64 tmp;
1000
1001 tmp.set(SK_Fract1);
1002 tmp.shiftLeft(16);
1003 tmp.div(inv->fMat[kMPersp2], Sk64::kRound_DivOption);
1004
1005 SkFract scale = tmp.get32();
1006
1007 for (int i = 0; i < 9; i++) {
1008 inv->fMat[i] = SkFractMul(inv->fMat[i], scale);
1009 }
1010 }
1011 inv->fMat[kMPersp2] = SkFixedToFract(inv->fMat[kMPersp2]);
1012#endif
1013 } else { // not perspective
1014#ifdef SK_SCALAR_IS_FIXED
1015 Sk64 tx, ty;
1016 int clzNumer;
1017
1018 // check the 2x2 for overflow
1019 {
1020 int32_t value = SkAbs32(fMat[kMScaleY]);
1021 value |= SkAbs32(fMat[kMSkewX]);
1022 value |= SkAbs32(fMat[kMScaleX]);
1023 value |= SkAbs32(fMat[kMSkewY]);
1024 clzNumer = SkCLZ(value);
1025 if (shift - clzNumer > 31)
1026 return false; // overflow
1027 }
1028
1029 set_muladdmul(&tx, fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX]);
1030 set_muladdmul(&ty, fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY]);
1031 // check tx,ty for overflow
1032 clzNumer = SkCLZ(SkAbs32(tx.fHi) | SkAbs32(ty.fHi));
1033 if (shift - clzNumer > 14) {
1034 return false; // overflow
1035 }
1036
1037 int fixedShift = 61 - shift;
1038 int sk64shift = 44 - shift + clzNumer;
1039
1040 inv->fMat[kMScaleX] = SkMulShift(fMat[kMScaleY], scale, fixedShift);
1041 inv->fMat[kMSkewX] = SkMulShift(-fMat[kMSkewX], scale, fixedShift);
1042 inv->fMat[kMTransX] = SkMulShift(tx.getShiftRight(33 - clzNumer), scale, sk64shift);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001043
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 inv->fMat[kMSkewY] = SkMulShift(-fMat[kMSkewY], scale, fixedShift);
1045 inv->fMat[kMScaleY] = SkMulShift(fMat[kMScaleX], scale, fixedShift);
1046 inv->fMat[kMTransY] = SkMulShift(ty.getShiftRight(33 - clzNumer), scale, sk64shift);
1047#else
reed@android.com0b9e2db2009-09-16 17:00:17 +00001048 inv->fMat[kMScaleX] = SkDoubleToFloat(fMat[kMScaleY] * scale);
1049 inv->fMat[kMSkewX] = SkDoubleToFloat(-fMat[kMSkewX] * scale);
1050 inv->fMat[kMTransX] = mul_diff_scale(fMat[kMSkewX], fMat[kMTransY],
1051 fMat[kMScaleY], fMat[kMTransX], scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001052
reed@android.com0b9e2db2009-09-16 17:00:17 +00001053 inv->fMat[kMSkewY] = SkDoubleToFloat(-fMat[kMSkewY] * scale);
1054 inv->fMat[kMScaleY] = SkDoubleToFloat(fMat[kMScaleX] * scale);
1055 inv->fMat[kMTransY] = mul_diff_scale(fMat[kMSkewY], fMat[kMTransX],
1056 fMat[kMScaleX], fMat[kMTransY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057#endif
1058 inv->fMat[kMPersp0] = 0;
1059 inv->fMat[kMPersp1] = 0;
1060 inv->fMat[kMPersp2] = kMatrix22Elem;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001061
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 }
1063
junov@chromium.org6fc56992012-07-12 14:01:32 +00001064 inv->setTypeMask(fTypeMask);
1065
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 if (inv == &tmp) {
1067 *(SkMatrix*)this = tmp;
1068 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 }
1070 return true;
1071}
1072
1073///////////////////////////////////////////////////////////////////////////////
1074
1075void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
1076 const SkPoint src[], int count) {
1077 SkASSERT(m.getType() == 0);
1078
1079 if (dst != src && count > 0)
1080 memcpy(dst, src, count * sizeof(SkPoint));
1081}
1082
1083void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
1084 const SkPoint src[], int count) {
1085 SkASSERT(m.getType() == kTranslate_Mask);
1086
1087 if (count > 0) {
1088 SkScalar tx = m.fMat[kMTransX];
1089 SkScalar ty = m.fMat[kMTransY];
1090 do {
1091 dst->fY = src->fY + ty;
1092 dst->fX = src->fX + tx;
1093 src += 1;
1094 dst += 1;
1095 } while (--count);
1096 }
1097}
1098
1099void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
1100 const SkPoint src[], int count) {
1101 SkASSERT(m.getType() == kScale_Mask);
1102
1103 if (count > 0) {
1104 SkScalar mx = m.fMat[kMScaleX];
1105 SkScalar my = m.fMat[kMScaleY];
1106 do {
1107 dst->fY = SkScalarMul(src->fY, my);
1108 dst->fX = SkScalarMul(src->fX, mx);
1109 src += 1;
1110 dst += 1;
1111 } while (--count);
1112 }
1113}
1114
1115void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
1116 const SkPoint src[], int count) {
1117 SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
1118
1119 if (count > 0) {
1120 SkScalar mx = m.fMat[kMScaleX];
1121 SkScalar my = m.fMat[kMScaleY];
1122 SkScalar tx = m.fMat[kMTransX];
1123 SkScalar ty = m.fMat[kMTransY];
1124 do {
1125 dst->fY = SkScalarMulAdd(src->fY, my, ty);
1126 dst->fX = SkScalarMulAdd(src->fX, mx, tx);
1127 src += 1;
1128 dst += 1;
1129 } while (--count);
1130 }
1131}
1132
1133void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
1134 const SkPoint src[], int count) {
1135 SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
1136
1137 if (count > 0) {
1138 SkScalar mx = m.fMat[kMScaleX];
1139 SkScalar my = m.fMat[kMScaleY];
1140 SkScalar kx = m.fMat[kMSkewX];
1141 SkScalar ky = m.fMat[kMSkewY];
1142 do {
1143 SkScalar sy = src->fY;
1144 SkScalar sx = src->fX;
1145 src += 1;
1146 dst->fY = SkScalarMul(sx, ky) + SkScalarMul(sy, my);
1147 dst->fX = SkScalarMul(sx, mx) + SkScalarMul(sy, kx);
1148 dst += 1;
1149 } while (--count);
1150 }
1151}
1152
1153void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
1154 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001155 SkASSERT(!m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156
1157 if (count > 0) {
1158 SkScalar mx = m.fMat[kMScaleX];
1159 SkScalar my = m.fMat[kMScaleY];
1160 SkScalar kx = m.fMat[kMSkewX];
1161 SkScalar ky = m.fMat[kMSkewY];
1162 SkScalar tx = m.fMat[kMTransX];
1163 SkScalar ty = m.fMat[kMTransY];
1164 do {
1165 SkScalar sy = src->fY;
1166 SkScalar sx = src->fX;
1167 src += 1;
1168 dst->fY = SkScalarMul(sx, ky) + SkScalarMulAdd(sy, my, ty);
1169 dst->fX = SkScalarMul(sx, mx) + SkScalarMulAdd(sy, kx, tx);
1170 dst += 1;
1171 } while (--count);
1172 }
1173}
1174
1175void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
1176 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001177 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178
1179#ifdef SK_SCALAR_IS_FIXED
1180 SkFixed persp2 = SkFractToFixed(m.fMat[kMPersp2]);
1181#endif
1182
1183 if (count > 0) {
1184 do {
1185 SkScalar sy = src->fY;
1186 SkScalar sx = src->fX;
1187 src += 1;
1188
1189 SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
1190 SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1191 SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
1192 SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1193#ifdef SK_SCALAR_IS_FIXED
1194 SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
1195 SkFractMul(sy, m.fMat[kMPersp1]) + persp2;
1196#else
1197 float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
1198 SkScalarMulAdd(sy, m.fMat[kMPersp1], m.fMat[kMPersp2]);
1199#endif
1200 if (z) {
1201 z = SkScalarFastInvert(z);
1202 }
1203
1204 dst->fY = SkScalarMul(y, z);
1205 dst->fX = SkScalarMul(x, z);
1206 dst += 1;
1207 } while (--count);
1208 }
1209}
1210
1211const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
1212 SkMatrix::Identity_pts, SkMatrix::Trans_pts,
1213 SkMatrix::Scale_pts, SkMatrix::ScaleTrans_pts,
1214 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
1215 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
1216 // repeat the persp proc 8 times
1217 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1218 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1219 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1220 SkMatrix::Persp_pts, SkMatrix::Persp_pts
1221};
1222
1223void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001224 SkASSERT((dst && src && count > 0) || 0 == count);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 // no partial overlap
bungeman@google.com3dc82c42013-10-11 19:11:10 +00001226 SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227
1228 this->getMapPtsProc()(*this, dst, src, count);
1229}
1230
1231///////////////////////////////////////////////////////////////////////////////
1232
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001233void SkMatrix::mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const {
1234 SkASSERT((dst && src && count > 0) || 0 == count);
1235 // no partial overlap
1236 SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= 3*count);
1237
1238 if (count > 0) {
1239 if (this->isIdentity()) {
1240 memcpy(dst, src, 3*count*sizeof(SkScalar));
1241 return;
1242 }
1243 do {
1244 SkScalar sx = src[0];
1245 SkScalar sy = src[1];
1246 SkScalar sw = src[2];
1247 src += 3;
1248
1249 SkScalar x = SkScalarMul(sx, fMat[kMScaleX]) +
1250 SkScalarMul(sy, fMat[kMSkewX]) +
1251 SkScalarMul(sw, fMat[kMTransX]);
1252 SkScalar y = SkScalarMul(sx, fMat[kMSkewY]) +
1253 SkScalarMul(sy, fMat[kMScaleY]) +
1254 SkScalarMul(sw, fMat[kMTransY]);
1255 SkScalar w = SkScalarMul(sx, fMat[kMPersp0]) +
1256 SkScalarMul(sy, fMat[kMPersp1]) +
1257 SkScalarMul(sw, fMat[kMPersp2]);
1258
1259 dst[0] = x;
1260 dst[1] = y;
1261 dst[2] = w;
1262 dst += 3;
1263 } while (--count);
1264 }
1265}
1266
1267///////////////////////////////////////////////////////////////////////////////
1268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001270 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 SkPoint origin;
1272
1273 MapXYProc proc = this->getMapXYProc();
1274 proc(*this, 0, 0, &origin);
1275
1276 for (int i = count - 1; i >= 0; --i) {
1277 SkPoint tmp;
1278
1279 proc(*this, src[i].fX, src[i].fY, &tmp);
1280 dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
1281 }
1282 } else {
1283 SkMatrix tmp = *this;
1284
1285 tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
1286 tmp.clearTypeMask(kTranslate_Mask);
1287 tmp.mapPoints(dst, src, count);
1288 }
1289}
1290
1291bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
1292 SkASSERT(dst && &src);
1293
1294 if (this->rectStaysRect()) {
1295 this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
1296 dst->sort();
1297 return true;
1298 } else {
1299 SkPoint quad[4];
1300
1301 src.toQuad(quad);
1302 this->mapPoints(quad, quad, 4);
1303 dst->set(quad, 4);
1304 return false;
1305 }
1306}
1307
1308SkScalar SkMatrix::mapRadius(SkScalar radius) const {
1309 SkVector vec[2];
1310
1311 vec[0].set(radius, 0);
1312 vec[1].set(0, radius);
1313 this->mapVectors(vec, 2);
1314
1315 SkScalar d0 = vec[0].length();
1316 SkScalar d1 = vec[1].length();
1317
1318 return SkScalarMean(d0, d1);
1319}
1320
1321///////////////////////////////////////////////////////////////////////////////
1322
1323void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1324 SkPoint* pt) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001325 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326
1327 SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
1328 SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1329 SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
1330 SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1331#ifdef SK_SCALAR_IS_FIXED
1332 SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
1333 SkFractMul(sy, m.fMat[kMPersp1]) +
1334 SkFractToFixed(m.fMat[kMPersp2]);
1335#else
1336 float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
1337 SkScalarMul(sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
1338#endif
1339 if (z) {
1340 z = SkScalarFastInvert(z);
1341 }
1342 pt->fX = SkScalarMul(x, z);
1343 pt->fY = SkScalarMul(y, z);
1344}
1345
1346#ifdef SK_SCALAR_IS_FIXED
1347static SkFixed fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d) {
1348 Sk64 tmp, tmp1;
1349
1350 tmp.setMul(a, b);
1351 tmp1.setMul(c, d);
1352 return tmp.addGetFixed(tmp1);
1353// tmp.add(tmp1);
1354// return tmp.getFixed();
1355}
1356#endif
1357
1358void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1359 SkPoint* pt) {
1360 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001361
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362#ifdef SK_SCALAR_IS_FIXED
1363 pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) +
1364 m.fMat[kMTransX];
1365 pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) +
1366 m.fMat[kMTransY];
1367#else
1368 pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
1369 SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
1370 pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
1371 SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
1372#endif
1373}
1374
1375void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1376 SkPoint* pt) {
1377 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
1378 SkASSERT(0 == m.fMat[kMTransX]);
1379 SkASSERT(0 == m.fMat[kMTransY]);
1380
1381#ifdef SK_SCALAR_IS_FIXED
1382 pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]);
1383 pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]);
1384#else
1385 pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
1386 SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
1387 pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
1388 SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
1389#endif
1390}
1391
1392void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1393 SkPoint* pt) {
1394 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1395 == kScale_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 pt->fX = SkScalarMulAdd(sx, m.fMat[kMScaleX], m.fMat[kMTransX]);
1398 pt->fY = SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
1399}
1400
1401void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1402 SkPoint* pt) {
1403 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1404 == kScale_Mask);
1405 SkASSERT(0 == m.fMat[kMTransX]);
1406 SkASSERT(0 == m.fMat[kMTransY]);
1407
1408 pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]);
1409 pt->fY = SkScalarMul(sy, m.fMat[kMScaleY]);
1410}
1411
1412void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1413 SkPoint* pt) {
1414 SkASSERT(m.getType() == kTranslate_Mask);
1415
1416 pt->fX = sx + m.fMat[kMTransX];
1417 pt->fY = sy + m.fMat[kMTransY];
1418}
1419
1420void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1421 SkPoint* pt) {
1422 SkASSERT(0 == m.getType());
1423
1424 pt->fX = sx;
1425 pt->fY = sy;
1426}
1427
1428const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
1429 SkMatrix::Identity_xy, SkMatrix::Trans_xy,
1430 SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy,
1431 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1432 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1433 // repeat the persp proc 8 times
1434 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1435 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1436 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1437 SkMatrix::Persp_xy, SkMatrix::Persp_xy
1438};
1439
1440///////////////////////////////////////////////////////////////////////////////
1441
1442// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
1443#ifdef SK_SCALAR_IS_FIXED
1444 typedef SkFract SkPerspElemType;
1445 #define PerspNearlyZero(x) (SkAbs32(x) < (SK_Fract1 >> 26))
1446#else
1447 typedef float SkPerspElemType;
1448 #define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26)))
1449#endif
1450
1451bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
1452 if (PerspNearlyZero(fMat[kMPersp0])) {
1453 if (stepX || stepY) {
1454 if (PerspNearlyZero(fMat[kMPersp1]) &&
1455 PerspNearlyZero(fMat[kMPersp2] - kMatrix22Elem)) {
1456 if (stepX) {
1457 *stepX = SkScalarToFixed(fMat[kMScaleX]);
1458 }
1459 if (stepY) {
1460 *stepY = SkScalarToFixed(fMat[kMSkewY]);
1461 }
1462 } else {
1463#ifdef SK_SCALAR_IS_FIXED
1464 SkFixed z = SkFractMul(y, fMat[kMPersp1]) +
1465 SkFractToFixed(fMat[kMPersp2]);
1466#else
1467 float z = y * fMat[kMPersp1] + fMat[kMPersp2];
1468#endif
1469 if (stepX) {
1470 *stepX = SkScalarToFixed(SkScalarDiv(fMat[kMScaleX], z));
1471 }
1472 if (stepY) {
1473 *stepY = SkScalarToFixed(SkScalarDiv(fMat[kMSkewY], z));
1474 }
1475 }
1476 }
1477 return true;
1478 }
1479 return false;
1480}
1481
1482///////////////////////////////////////////////////////////////////////////////
1483
1484#include "SkPerspIter.h"
1485
1486SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
1487 : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
1488 SkPoint pt;
1489
1490 SkMatrix::Persp_xy(m, x0, y0, &pt);
1491 fX = SkScalarToFixed(pt.fX);
1492 fY = SkScalarToFixed(pt.fY);
1493}
1494
1495int SkPerspIter::next() {
1496 int n = fCount;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001497
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 if (0 == n) {
1499 return 0;
1500 }
1501 SkPoint pt;
1502 SkFixed x = fX;
1503 SkFixed y = fY;
1504 SkFixed dx, dy;
1505
1506 if (n >= kCount) {
1507 n = kCount;
1508 fSX += SkIntToScalar(kCount);
1509 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1510 fX = SkScalarToFixed(pt.fX);
1511 fY = SkScalarToFixed(pt.fY);
1512 dx = (fX - x) >> kShift;
1513 dy = (fY - y) >> kShift;
1514 } else {
1515 fSX += SkIntToScalar(n);
1516 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1517 fX = SkScalarToFixed(pt.fX);
1518 fY = SkScalarToFixed(pt.fY);
1519 dx = (fX - x) / n;
1520 dy = (fY - y) / n;
1521 }
1522
1523 SkFixed* p = fStorage;
1524 for (int i = 0; i < n; i++) {
1525 *p++ = x; x += dx;
1526 *p++ = y; y += dy;
1527 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001528
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529 fCount -= n;
1530 return n;
1531}
1532
1533///////////////////////////////////////////////////////////////////////////////
1534
1535#ifdef SK_SCALAR_IS_FIXED
1536
1537static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1538 SkFixed x = SK_Fixed1, y = SK_Fixed1;
1539 SkPoint pt1, pt2;
1540 Sk64 w1, w2;
1541
1542 if (count > 1) {
1543 pt1.fX = poly[1].fX - poly[0].fX;
1544 pt1.fY = poly[1].fY - poly[0].fY;
1545 y = SkPoint::Length(pt1.fX, pt1.fY);
1546 if (y == 0) {
1547 return false;
1548 }
1549 switch (count) {
1550 case 2:
1551 break;
1552 case 3:
1553 pt2.fX = poly[0].fY - poly[2].fY;
1554 pt2.fY = poly[2].fX - poly[0].fX;
1555 goto CALC_X;
1556 default:
1557 pt2.fX = poly[0].fY - poly[3].fY;
1558 pt2.fY = poly[3].fX - poly[0].fX;
1559 CALC_X:
1560 w1.setMul(pt1.fX, pt2.fX);
1561 w2.setMul(pt1.fY, pt2.fY);
1562 w1.add(w2);
1563 w1.div(y, Sk64::kRound_DivOption);
1564 if (!w1.is32()) {
1565 return false;
1566 }
1567 x = w1.get32();
1568 break;
1569 }
1570 }
1571 pt->set(x, y);
1572 return true;
1573}
1574
1575bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1576 const SkPoint& scalePt) {
1577 // need to check if SkFixedDiv overflows...
1578
1579 const SkFixed scale = scalePt.fY;
1580 dst->fMat[kMScaleX] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
1581 dst->fMat[kMSkewY] = SkFixedDiv(srcPt[0].fX - srcPt[1].fX, scale);
1582 dst->fMat[kMPersp0] = 0;
1583 dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale);
1584 dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
1585 dst->fMat[kMPersp1] = 0;
1586 dst->fMat[kMTransX] = srcPt[0].fX;
1587 dst->fMat[kMTransY] = srcPt[0].fY;
1588 dst->fMat[kMPersp2] = SK_Fract1;
1589 dst->setTypeMask(kUnknown_Mask);
1590 return true;
1591}
1592
1593bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1594 const SkPoint& scale) {
1595 // really, need to check if SkFixedDiv overflow'd
1596
1597 dst->fMat[kMScaleX] = SkFixedDiv(srcPt[2].fX - srcPt[0].fX, scale.fX);
1598 dst->fMat[kMSkewY] = SkFixedDiv(srcPt[2].fY - srcPt[0].fY, scale.fX);
1599 dst->fMat[kMPersp0] = 0;
1600 dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale.fY);
1601 dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale.fY);
1602 dst->fMat[kMPersp1] = 0;
1603 dst->fMat[kMTransX] = srcPt[0].fX;
1604 dst->fMat[kMTransY] = srcPt[0].fY;
1605 dst->fMat[kMPersp2] = SK_Fract1;
1606 dst->setTypeMask(kUnknown_Mask);
1607 return true;
1608}
1609
1610bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1611 const SkPoint& scale) {
1612 SkFract a1, a2;
1613 SkFixed x0, y0, x1, y1, x2, y2;
1614
1615 x0 = srcPt[2].fX - srcPt[0].fX;
1616 y0 = srcPt[2].fY - srcPt[0].fY;
1617 x1 = srcPt[2].fX - srcPt[1].fX;
1618 y1 = srcPt[2].fY - srcPt[1].fY;
1619 x2 = srcPt[2].fX - srcPt[3].fX;
1620 y2 = srcPt[2].fY - srcPt[3].fY;
1621
1622 /* check if abs(x2) > abs(y2) */
1623 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1624 SkFixed denom = SkMulDiv(x1, y2, x2) - y1;
1625 if (0 == denom) {
1626 return false;
1627 }
1628 a1 = SkFractDiv(SkMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
1629 } else {
1630 SkFixed denom = x1 - SkMulDiv(y1, x2, y2);
1631 if (0 == denom) {
1632 return false;
1633 }
1634 a1 = SkFractDiv(x0 - x1 - SkMulDiv(y0 - y1, x2, y2), denom);
1635 }
1636
1637 /* check if abs(x1) > abs(y1) */
1638 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1639 SkFixed denom = y2 - SkMulDiv(x2, y1, x1);
1640 if (0 == denom) {
1641 return false;
1642 }
1643 a2 = SkFractDiv(y0 - y2 - SkMulDiv(x0 - x2, y1, x1), denom);
1644 } else {
1645 SkFixed denom = SkMulDiv(y2, x1, y1) - x2;
1646 if (0 == denom) {
1647 return false;
1648 }
1649 a2 = SkFractDiv(SkMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
1650 }
1651
1652 // need to check if SkFixedDiv overflows...
1653 dst->fMat[kMScaleX] = SkFixedDiv(SkFractMul(a2, srcPt[3].fX) +
1654 srcPt[3].fX - srcPt[0].fX, scale.fX);
1655 dst->fMat[kMSkewY] = SkFixedDiv(SkFractMul(a2, srcPt[3].fY) +
1656 srcPt[3].fY - srcPt[0].fY, scale.fX);
1657 dst->fMat[kMPersp0] = SkFixedDiv(a2, scale.fX);
1658 dst->fMat[kMSkewX] = SkFixedDiv(SkFractMul(a1, srcPt[1].fX) +
1659 srcPt[1].fX - srcPt[0].fX, scale.fY);
1660 dst->fMat[kMScaleY] = SkFixedDiv(SkFractMul(a1, srcPt[1].fY) +
1661 srcPt[1].fY - srcPt[0].fY, scale.fY);
1662 dst->fMat[kMPersp1] = SkFixedDiv(a1, scale.fY);
1663 dst->fMat[kMTransX] = srcPt[0].fX;
1664 dst->fMat[kMTransY] = srcPt[0].fY;
1665 dst->fMat[kMPersp2] = SK_Fract1;
1666 dst->setTypeMask(kUnknown_Mask);
1667 return true;
1668}
1669
1670#else /* Scalar is float */
1671
1672static inline bool checkForZero(float x) {
1673 return x*x == 0;
1674}
1675
1676static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1677 float x = 1, y = 1;
1678 SkPoint pt1, pt2;
1679
1680 if (count > 1) {
1681 pt1.fX = poly[1].fX - poly[0].fX;
1682 pt1.fY = poly[1].fY - poly[0].fY;
1683 y = SkPoint::Length(pt1.fX, pt1.fY);
1684 if (checkForZero(y)) {
1685 return false;
1686 }
1687 switch (count) {
1688 case 2:
1689 break;
1690 case 3:
1691 pt2.fX = poly[0].fY - poly[2].fY;
1692 pt2.fY = poly[2].fX - poly[0].fX;
1693 goto CALC_X;
1694 default:
1695 pt2.fX = poly[0].fY - poly[3].fY;
1696 pt2.fY = poly[3].fX - poly[0].fX;
1697 CALC_X:
1698 x = SkScalarDiv(SkScalarMul(pt1.fX, pt2.fX) +
1699 SkScalarMul(pt1.fY, pt2.fY), y);
1700 break;
1701 }
1702 }
1703 pt->set(x, y);
1704 return true;
1705}
1706
1707bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1708 const SkPoint& scale) {
1709 float invScale = 1 / scale.fY;
1710
1711 dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1712 dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
1713 dst->fMat[kMPersp0] = 0;
1714 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1715 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1716 dst->fMat[kMPersp1] = 0;
1717 dst->fMat[kMTransX] = srcPt[0].fX;
1718 dst->fMat[kMTransY] = srcPt[0].fY;
1719 dst->fMat[kMPersp2] = 1;
1720 dst->setTypeMask(kUnknown_Mask);
1721 return true;
1722}
1723
1724bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1725 const SkPoint& scale) {
1726 float invScale = 1 / scale.fX;
1727 dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
1728 dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
1729 dst->fMat[kMPersp0] = 0;
1730
1731 invScale = 1 / scale.fY;
1732 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1733 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1734 dst->fMat[kMPersp1] = 0;
1735
1736 dst->fMat[kMTransX] = srcPt[0].fX;
1737 dst->fMat[kMTransY] = srcPt[0].fY;
1738 dst->fMat[kMPersp2] = 1;
1739 dst->setTypeMask(kUnknown_Mask);
1740 return true;
1741}
1742
1743bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1744 const SkPoint& scale) {
1745 float a1, a2;
1746 float x0, y0, x1, y1, x2, y2;
1747
1748 x0 = srcPt[2].fX - srcPt[0].fX;
1749 y0 = srcPt[2].fY - srcPt[0].fY;
1750 x1 = srcPt[2].fX - srcPt[1].fX;
1751 y1 = srcPt[2].fY - srcPt[1].fY;
1752 x2 = srcPt[2].fX - srcPt[3].fX;
1753 y2 = srcPt[2].fY - srcPt[3].fY;
1754
1755 /* check if abs(x2) > abs(y2) */
1756 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1757 float denom = SkScalarMulDiv(x1, y2, x2) - y1;
1758 if (checkForZero(denom)) {
1759 return false;
1760 }
1761 a1 = SkScalarDiv(SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
1762 } else {
1763 float denom = x1 - SkScalarMulDiv(y1, x2, y2);
1764 if (checkForZero(denom)) {
1765 return false;
1766 }
1767 a1 = SkScalarDiv(x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2), denom);
1768 }
1769
1770 /* check if abs(x1) > abs(y1) */
1771 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1772 float denom = y2 - SkScalarMulDiv(x2, y1, x1);
1773 if (checkForZero(denom)) {
1774 return false;
1775 }
1776 a2 = SkScalarDiv(y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1), denom);
1777 } else {
1778 float denom = SkScalarMulDiv(y2, x1, y1) - x2;
1779 if (checkForZero(denom)) {
1780 return false;
1781 }
1782 a2 = SkScalarDiv(SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
1783 }
1784
1785 float invScale = 1 / scale.fX;
1786 dst->fMat[kMScaleX] = SkScalarMul(SkScalarMul(a2, srcPt[3].fX) +
1787 srcPt[3].fX - srcPt[0].fX, invScale);
1788 dst->fMat[kMSkewY] = SkScalarMul(SkScalarMul(a2, srcPt[3].fY) +
1789 srcPt[3].fY - srcPt[0].fY, invScale);
1790 dst->fMat[kMPersp0] = SkScalarMul(a2, invScale);
1791 invScale = 1 / scale.fY;
1792 dst->fMat[kMSkewX] = SkScalarMul(SkScalarMul(a1, srcPt[1].fX) +
1793 srcPt[1].fX - srcPt[0].fX, invScale);
1794 dst->fMat[kMScaleY] = SkScalarMul(SkScalarMul(a1, srcPt[1].fY) +
1795 srcPt[1].fY - srcPt[0].fY, invScale);
1796 dst->fMat[kMPersp1] = SkScalarMul(a1, invScale);
1797 dst->fMat[kMTransX] = srcPt[0].fX;
1798 dst->fMat[kMTransY] = srcPt[0].fY;
1799 dst->fMat[kMPersp2] = 1;
1800 dst->setTypeMask(kUnknown_Mask);
1801 return true;
1802}
1803
1804#endif
1805
1806typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
1807
1808/* Taken from Rob Johnson's original sample code in QuickDraw GX
1809*/
1810bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
1811 int count) {
1812 if ((unsigned)count > 4) {
1813 SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
1814 return false;
1815 }
1816
1817 if (0 == count) {
1818 this->reset();
1819 return true;
1820 }
1821 if (1 == count) {
1822 this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
1823 return true;
1824 }
1825
1826 SkPoint scale;
1827 if (!poly_to_point(&scale, src, count) ||
1828 SkScalarNearlyZero(scale.fX) ||
1829 SkScalarNearlyZero(scale.fY)) {
1830 return false;
1831 }
1832
1833 static const PolyMapProc gPolyMapProcs[] = {
1834 SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
1835 };
1836 PolyMapProc proc = gPolyMapProcs[count - 2];
1837
1838 SkMatrix tempMap, result;
1839 tempMap.setTypeMask(kUnknown_Mask);
1840
1841 if (!proc(src, &tempMap, scale)) {
1842 return false;
1843 }
1844 if (!tempMap.invert(&result)) {
1845 return false;
1846 }
1847 if (!proc(dst, &tempMap, scale)) {
1848 return false;
1849 }
1850 if (!result.setConcat(tempMap, result)) {
1851 return false;
1852 }
1853 *this = result;
1854 return true;
1855}
1856
1857///////////////////////////////////////////////////////////////////////////////
1858
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001859enum MinOrMax {
1860 kMin_MinOrMax,
1861 kMax_MinOrMax
1862};
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001863
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001864template <MinOrMax MIN_OR_MAX> SkScalar get_stretch_factor(SkMatrix::TypeMask typeMask,
1865 const SkScalar m[9]) {
1866 if (typeMask & SkMatrix::kPerspective_Mask) {
bsalomon@google.com38396322011-09-09 19:32:04 +00001867 return -SK_Scalar1;
1868 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001869 if (SkMatrix::kIdentity_Mask == typeMask) {
bsalomon@google.com38396322011-09-09 19:32:04 +00001870 return SK_Scalar1;
1871 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001872 if (!(typeMask & SkMatrix::kAffine_Mask)) {
1873 if (kMin_MinOrMax == MIN_OR_MAX) {
1874 return SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1875 SkScalarAbs(m[SkMatrix::kMScaleY]));
1876 } else {
1877 return SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1878 SkScalarAbs(m[SkMatrix::kMScaleY]));
1879 }
bsalomon@google.com38396322011-09-09 19:32:04 +00001880 }
1881 // ignore the translation part of the matrix, just look at 2x2 portion.
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001882 // compute singular values, take largest or smallest abs value.
bsalomon@google.com38396322011-09-09 19:32:04 +00001883 // [a b; b c] = A^T*A
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001884 SkScalar a = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX]) +
1885 SkScalarMul(m[SkMatrix::kMSkewY], m[SkMatrix::kMSkewY]);
1886 SkScalar b = SkScalarMul(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX]) +
1887 SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]);
1888 SkScalar c = SkScalarMul(m[SkMatrix::kMSkewX], m[SkMatrix::kMSkewX]) +
1889 SkScalarMul(m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]);
bsalomon@google.com38396322011-09-09 19:32:04 +00001890 // eigenvalues of A^T*A are the squared singular values of A.
1891 // characteristic equation is det((A^T*A) - l*I) = 0
1892 // l^2 - (a + c)l + (ac-b^2)
1893 // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001894 // and roots are guaranteed to be pos and real).
1895 SkScalar chosenRoot;
bsalomon@google.com38396322011-09-09 19:32:04 +00001896 SkScalar bSqd = SkScalarMul(b,b);
1897 // if upper left 2x2 is orthogonal save some math
jvanverth@google.comc490f802013-03-04 13:56:38 +00001898 if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001899 if (kMin_MinOrMax == MIN_OR_MAX) {
1900 chosenRoot = SkMinScalar(a, c);
1901 } else {
1902 chosenRoot = SkMaxScalar(a, c);
1903 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001904 } else {
bsalomon@google.com38396322011-09-09 19:32:04 +00001905 SkScalar aminusc = a - c;
1906 SkScalar apluscdiv2 = SkScalarHalf(a + c);
1907 SkScalar x = SkScalarHalf(SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd));
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001908 if (kMin_MinOrMax == MIN_OR_MAX) {
1909 chosenRoot = apluscdiv2 - x;
1910 } else {
1911 chosenRoot = apluscdiv2 + x;
1912 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001913 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001914 SkASSERT(chosenRoot >= 0);
1915 return SkScalarSqrt(chosenRoot);
1916}
1917
1918SkScalar SkMatrix::getMinStretch() const {
1919 return get_stretch_factor<kMin_MinOrMax>(this->getType(), fMat);
1920}
1921
1922SkScalar SkMatrix::getMaxStretch() const {
1923 return get_stretch_factor<kMax_MinOrMax>(this->getType(), fMat);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001924}
1925
commit-bot@chromium.org1f81fd62013-10-23 14:44:08 +00001926static void reset_identity_matrix(SkMatrix* identity) {
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +00001927 identity->reset();
1928}
1929
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001930const SkMatrix& SkMatrix::I() {
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +00001931 // If you can use C++11 now, you might consider replacing this with a constexpr constructor.
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001932 static SkMatrix gIdentity;
commit-bot@chromium.org1f81fd62013-10-23 14:44:08 +00001933 SK_DECLARE_STATIC_ONCE(once);
1934 SkOnce(&once, reset_identity_matrix, &gIdentity);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001935 return gIdentity;
tomhudson@google.com1f902872012-06-01 13:15:47 +00001936}
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001937
1938const SkMatrix& SkMatrix::InvalidMatrix() {
1939 static SkMatrix gInvalid;
1940 static bool gOnce;
1941 if (!gOnce) {
1942 gInvalid.setAll(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1943 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1944 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1945 gInvalid.getType(); // force the type to be computed
1946 gOnce = true;
1947 }
1948 return gInvalid;
1949}
1950
1951///////////////////////////////////////////////////////////////////////////////
1952
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001953size_t SkMatrix::writeToMemory(void* buffer) const {
reed@android.com0ad336f2009-06-29 16:02:20 +00001954 // TODO write less for simple matrices
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001955 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
reed@android.com0ad336f2009-06-29 16:02:20 +00001956 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001957 memcpy(buffer, fMat, sizeInMemory);
reed@android.com0ad336f2009-06-29 16:02:20 +00001958 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001959 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001960}
1961
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001962size_t SkMatrix::readFromMemory(const void* buffer, size_t length) {
1963 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
1964 if (length < sizeInMemory) {
1965 return 0;
1966 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001967 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001968 memcpy(fMat, buffer, sizeInMemory);
reed@android.comf2b98d62010-12-20 18:26:13 +00001969 this->setTypeMask(kUnknown_Mask);
1970 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001971 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001972}
1973
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001974#ifdef SK_DEVELOPER
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975void SkMatrix::dump() const {
1976 SkString str;
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001977 this->toString(&str);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 SkDebugf("%s\n", str.c_str());
1979}
1980
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001981void SkMatrix::toString(SkString* str) const {
1982 str->appendf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983#ifdef SK_SCALAR_IS_FLOAT
1984 fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
1985 fMat[6], fMat[7], fMat[8]);
1986#else
1987 SkFixedToFloat(fMat[0]), SkFixedToFloat(fMat[1]), SkFixedToFloat(fMat[2]),
1988 SkFixedToFloat(fMat[3]), SkFixedToFloat(fMat[4]), SkFixedToFloat(fMat[5]),
1989 SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8]));
1990#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991}
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001992#endif
reed@google.comad514302013-01-02 20:19:45 +00001993
1994///////////////////////////////////////////////////////////////////////////////
1995
1996#include "SkMatrixUtils.h"
1997
reed@google.comae573582013-01-03 15:22:40 +00001998bool SkTreatAsSprite(const SkMatrix& mat, int width, int height,
reed@google.comad514302013-01-02 20:19:45 +00001999 unsigned subpixelBits) {
reed@google.comae573582013-01-03 15:22:40 +00002000 // quick reject on affine or perspective
reed@google.comad514302013-01-02 20:19:45 +00002001 if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
2002 return false;
2003 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00002004
reed@google.comad514302013-01-02 20:19:45 +00002005 // quick success check
2006 if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
2007 return true;
2008 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00002009
reed@google.comad514302013-01-02 20:19:45 +00002010 // mapRect supports negative scales, so we eliminate those first
2011 if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
2012 return false;
2013 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00002014
reed@google.comad514302013-01-02 20:19:45 +00002015 SkRect dst;
reed@google.comae573582013-01-03 15:22:40 +00002016 SkIRect isrc = { 0, 0, width, height };
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00002017
reed@google.comad514302013-01-02 20:19:45 +00002018 {
reed@google.comae573582013-01-03 15:22:40 +00002019 SkRect src;
2020 src.set(isrc);
2021 mat.mapRect(&dst, src);
reed@google.comad514302013-01-02 20:19:45 +00002022 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00002023
reed@google.comae573582013-01-03 15:22:40 +00002024 // just apply the translate to isrc
2025 isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
2026 SkScalarRoundToInt(mat.getTranslateY()));
2027
reed@google.comad514302013-01-02 20:19:45 +00002028 if (subpixelBits) {
2029 isrc.fLeft <<= subpixelBits;
2030 isrc.fTop <<= subpixelBits;
2031 isrc.fRight <<= subpixelBits;
2032 isrc.fBottom <<= subpixelBits;
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00002033
reed@google.comad514302013-01-02 20:19:45 +00002034 const float scale = 1 << subpixelBits;
2035 dst.fLeft *= scale;
2036 dst.fTop *= scale;
2037 dst.fRight *= scale;
2038 dst.fBottom *= scale;
2039 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00002040
reed@google.comae573582013-01-03 15:22:40 +00002041 SkIRect idst;
reed@google.comad514302013-01-02 20:19:45 +00002042 dst.round(&idst);
2043 return isrc == idst;
2044}
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002045
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002046// A square matrix M can be decomposed (via polar decomposition) into two matrices --
2047// an orthogonal matrix Q and a symmetric matrix S. In turn we can decompose S into U*W*U^T,
2048// where U is another orthogonal matrix and W is a scale matrix. These can be recombined
2049// to give M = (Q*U)*W*U^T, i.e., the product of two orthogonal matrices and a scale matrix.
2050//
2051// The one wrinkle is that traditionally Q may contain a reflection -- the
2052// calculation has been rejiggered to put that reflection into W.
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002053bool SkDecomposeUpper2x2(const SkMatrix& matrix,
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002054 SkPoint* rotation1,
2055 SkPoint* scale,
2056 SkPoint* rotation2) {
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002057
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002058 SkScalar A = matrix[SkMatrix::kMScaleX];
2059 SkScalar B = matrix[SkMatrix::kMSkewX];
2060 SkScalar C = matrix[SkMatrix::kMSkewY];
2061 SkScalar D = matrix[SkMatrix::kMScaleY];
2062
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +00002063 if (is_degenerate_2x2(A, B, C, D)) {
2064 return false;
2065 }
2066
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002067 double w1, w2;
2068 SkScalar cos1, sin1;
2069 SkScalar cos2, sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002070
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002071 // do polar decomposition (M = Q*S)
2072 SkScalar cosQ, sinQ;
2073 double Sa, Sb, Sd;
2074 // if M is already symmetric (i.e., M = I*S)
2075 if (SkScalarNearlyEqual(B, C)) {
2076 cosQ = SK_Scalar1;
2077 sinQ = 0;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002078
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002079 Sa = A;
2080 Sb = B;
2081 Sd = D;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002082 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002083 cosQ = A + D;
2084 sinQ = C - B;
2085 SkScalar reciplen = SK_Scalar1/SkScalarSqrt(cosQ*cosQ + sinQ*sinQ);
2086 cosQ *= reciplen;
2087 sinQ *= reciplen;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002088
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002089 // S = Q^-1*M
2090 // we don't calc Sc since it's symmetric
2091 Sa = A*cosQ + C*sinQ;
2092 Sb = B*cosQ + D*sinQ;
2093 Sd = -B*sinQ + D*cosQ;
2094 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +00002095
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002096 // Now we need to compute eigenvalues of S (our scale factors)
2097 // and eigenvectors (bases for our rotation)
2098 // From this, should be able to reconstruct S as U*W*U^T
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00002099 if (SkScalarNearlyZero(SkDoubleToScalar(Sb))) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002100 // already diagonalized
2101 cos1 = SK_Scalar1;
2102 sin1 = 0;
2103 w1 = Sa;
2104 w2 = Sd;
2105 cos2 = cosQ;
2106 sin2 = sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00002107 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002108 double diff = Sa - Sd;
2109 double discriminant = sqrt(diff*diff + 4.0*Sb*Sb);
2110 double trace = Sa + Sd;
2111 if (diff > 0) {
2112 w1 = 0.5*(trace + discriminant);
2113 w2 = 0.5*(trace - discriminant);
2114 } else {
2115 w1 = 0.5*(trace - discriminant);
2116 w2 = 0.5*(trace + discriminant);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002117 }
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00002118
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00002119 cos1 = SkDoubleToScalar(Sb); sin1 = SkDoubleToScalar(w1 - Sa);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002120 SkScalar reciplen = SK_Scalar1/SkScalarSqrt(cos1*cos1 + sin1*sin1);
2121 cos1 *= reciplen;
2122 sin1 *= reciplen;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00002123
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002124 // rotation 2 is composition of Q and U
2125 cos2 = cos1*cosQ - sin1*sinQ;
2126 sin2 = sin1*cosQ + cos1*sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00002127
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002128 // rotation 1 is U^T
2129 sin1 = -sin1;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002130 }
2131
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002132 if (NULL != scale) {
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00002133 scale->fX = SkDoubleToScalar(w1);
2134 scale->fY = SkDoubleToScalar(w2);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002135 }
2136 if (NULL != rotation1) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00002137 rotation1->fX = cos1;
2138 rotation1->fY = sin1;
2139 }
2140 if (NULL != rotation2) {
2141 rotation2->fX = cos2;
2142 rotation2->fY = sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00002143 }
2144
2145 return true;
2146}