blob: 0929440e7a44be5a7919c8e815d296277a1a20e2 [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkFloatBits.h"
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +000010#include "SkOnce.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkString.h"
12
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000013// In a few places, we performed the following
14// a * b + c * d + e
15// as
16// a * b + (c * d + e)
17//
18// sdot and scross are indended to capture these compound operations into a
19// function, with an eye toward considering upscaling the intermediates to
20// doubles for more precision (as we do in concat and invert).
21//
22// However, these few lines that performed the last add before the "dot", cause
23// tiny image differences, so we guard that change until we see the impact on
24// chrome's layouttests.
25//
26#define SK_LEGACY_MATRIX_MATH_ORDER
reed@android.comab7ac022009-09-18 13:38:43 +000027
reed@google.com8f4d2302013-12-17 16:44:46 +000028static inline float SkDoubleToFloat(double x) {
29 return static_cast<float>(x);
30}
reed@android.com8a1c16f2008-12-17 15:59:43 +000031
32/* [scale-x skew-x trans-x] [X] [X']
33 [skew-y scale-y trans-y] * [Y] = [Y']
34 [persp-0 persp-1 persp-2] [1] [1 ]
35*/
36
37void SkMatrix::reset() {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000038 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000039 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 fMat[kMTransX] = fMat[kMTransY] =
41 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
43 this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
44}
45
reed@android.com8a1c16f2008-12-17 15:59:43 +000046// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
47enum {
48 kTranslate_Shift,
49 kScale_Shift,
50 kAffine_Shift,
51 kPerspective_Shift,
52 kRectStaysRect_Shift
53};
54
reed@google.com8f4d2302013-12-17 16:44:46 +000055static const int32_t kScalar1Int = 0x3f800000;
reed@android.com8a1c16f2008-12-17 15:59:43 +000056
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000057uint8_t SkMatrix::computePerspectiveTypeMask() const {
junov@chromium.org6fc56992012-07-12 14:01:32 +000058 // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
59 // is a win, but replacing those below is not. We don't yet understand
60 // that result.
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000061 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +000062 // If this is a perspective transform, we return true for all other
bsalomon@google.com19263b12012-07-13 13:36:38 +000063 // transform flags - this does not disable any optimizations, respects
rmistry@google.comfbfcd562012-08-23 18:09:54 +000064 // the rule that the type mask must be conservative, and speeds up
junov@chromium.org6fc56992012-07-12 14:01:32 +000065 // type mask computation.
66 return SkToU8(kORableMasks);
67 }
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000068
junov@chromium.org6fc56992012-07-12 14:01:32 +000069 return SkToU8(kOnlyPerspectiveValid_Mask | kUnknown_Mask);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000070}
71
reed@android.com8a1c16f2008-12-17 15:59:43 +000072uint8_t SkMatrix::computeTypeMask() const {
73 unsigned mask = 0;
74
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000075 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000076 // Once it is determined that that this is a perspective transform,
77 // all other flags are moot as far as optimizations are concerned.
78 return SkToU8(kORableMasks);
tomhudson@google.comac385252011-06-06 15:18:28 +000079 }
80
81 if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
82 mask |= kTranslate_Mask;
83 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000084
85 int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
86 int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
87 int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
88 int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
tomhudson@google.comac385252011-06-06 15:18:28 +000089
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 if (m01 | m10) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000091 // The skew components may be scale-inducing, unless we are dealing
92 // with a pure rotation. Testing for a pure rotation is expensive,
93 // so we opt for being conservative by always setting the scale bit.
94 // along with affine.
95 // By doing this, we are also ensuring that matrices have the same
96 // type masks as their inverses.
97 mask |= kAffine_Mask | kScale_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +000098
junov@chromium.org6fc56992012-07-12 14:01:32 +000099 // For rectStaysRect, in the affine case, we only need check that
100 // the primary diagonal is all zeros and that the secondary diagonal
101 // is all non-zero.
tomhudson@google.comac385252011-06-06 15:18:28 +0000102
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 // map non-zero to 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 m01 = m01 != 0;
105 m10 = m10 != 0;
tomhudson@google.comac385252011-06-06 15:18:28 +0000106
junov@chromium.org6fc56992012-07-12 14:01:32 +0000107 int dp0 = 0 == (m00 | m11) ; // true if both are 0
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 int ds1 = m01 & m10; // true if both are 1
tomhudson@google.comac385252011-06-06 15:18:28 +0000109
junov@chromium.org6fc56992012-07-12 14:01:32 +0000110 mask |= (dp0 & ds1) << kRectStaysRect_Shift;
111 } else {
112 // Only test for scale explicitly if not affine, since affine sets the
113 // scale bit.
114 if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
115 mask |= kScale_Mask;
116 }
117
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000118 // Not affine, therefore we already know secondary diagonal is
junov@chromium.org6fc56992012-07-12 14:01:32 +0000119 // all zeros, so we just need to check that primary diagonal is
120 // all non-zero.
121
122 // map non-zero to 1
123 m00 = m00 != 0;
124 m11 = m11 != 0;
125
126 // record if the (p)rimary diagonal is all non-zero
127 mask |= (m00 & m11) << kRectStaysRect_Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 }
129
130 return SkToU8(mask);
131}
132
133///////////////////////////////////////////////////////////////////////////////
134
reed@google.com3fb51872011-06-01 15:11:22 +0000135bool operator==(const SkMatrix& a, const SkMatrix& b) {
136 const SkScalar* SK_RESTRICT ma = a.fMat;
137 const SkScalar* SK_RESTRICT mb = b.fMat;
138
139 return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
140 ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
141 ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
142}
143
reed@google.com3fb51872011-06-01 15:11:22 +0000144///////////////////////////////////////////////////////////////////////////////
145
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000146// helper function to determine if upper-left 2x2 of matrix is degenerate
skia.committer@gmail.com16d53aa2013-07-30 07:01:00 +0000147static inline bool is_degenerate_2x2(SkScalar scaleX, SkScalar skewX,
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000148 SkScalar skewY, SkScalar scaleY) {
149 SkScalar perp_dot = scaleX*scaleY - skewX*skewY;
150 return SkScalarNearlyZero(perp_dot, SK_ScalarNearlyZero*SK_ScalarNearlyZero);
151}
152
153///////////////////////////////////////////////////////////////////////////////
154
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000155bool SkMatrix::isSimilarity(SkScalar tol) const {
156 // if identity or translate matrix
157 TypeMask mask = this->getType();
158 if (mask <= kTranslate_Mask) {
159 return true;
160 }
161 if (mask & kPerspective_Mask) {
162 return false;
163 }
164
165 SkScalar mx = fMat[kMScaleX];
166 SkScalar my = fMat[kMScaleY];
167 // if no skew, can just compare scale factors
168 if (!(mask & kAffine_Mask)) {
169 return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
170 }
171 SkScalar sx = fMat[kMSkewX];
172 SkScalar sy = fMat[kMSkewY];
173
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000174 if (is_degenerate_2x2(mx, sx, sy, my)) {
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000175 return false;
176 }
177
178 // it has scales and skews, but it could also be rotation, check it out.
179 SkVector vec[2];
180 vec[0].set(mx, sx);
181 vec[1].set(sy, my);
182
183 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
184 SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000185 SkScalarSquare(tol));
186}
187
188bool SkMatrix::preservesRightAngles(SkScalar tol) const {
189 TypeMask mask = this->getType();
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000190
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000191 if (mask <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
192 // identity, translate and/or scale
193 return true;
194 }
195 if (mask & kPerspective_Mask) {
196 return false;
197 }
198
199 SkASSERT(mask & kAffine_Mask);
200
201 SkScalar mx = fMat[kMScaleX];
202 SkScalar my = fMat[kMScaleY];
203 SkScalar sx = fMat[kMSkewX];
204 SkScalar sy = fMat[kMSkewY];
205
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000206 if (is_degenerate_2x2(mx, sx, sy, my)) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000207 return false;
208 }
209
210 // it has scales and skews, but it could also be rotation, check it out.
211 SkVector vec[2];
212 vec[0].set(mx, sx);
213 vec[1].set(sy, my);
214
215 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
216 SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
217 SkScalarSquare(tol));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000218}
219
220///////////////////////////////////////////////////////////////////////////////
221
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000222static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
223 return a * b + c * d;
224}
225
226static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
227 SkScalar e, SkScalar f) {
228 return a * b + c * d + e * f;
229}
230
231static inline SkScalar scross(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
232 return a * b - c * d;
233}
234
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
reed@google.comc0784db2013-12-13 21:16:12 +0000236 if (dx || dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 fMat[kMTransX] = dx;
238 fMat[kMTransY] = dy;
239
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000240 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000241 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
244 this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
245 } else {
246 this->reset();
247 }
248}
249
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000250void SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
251 if (!dx && !dy) {
252 return;
253 }
254
tomhudson@google.com8d430182011-06-06 19:11:19 +0000255 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 SkMatrix m;
257 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000258 this->preConcat(m);
259 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000260 fMat[kMTransX] += sdot(fMat[kMScaleX], dx, fMat[kMSkewX], dy);
261 fMat[kMTransY] += sdot(fMat[kMSkewY], dx, fMat[kMScaleY], dy);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000262 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264}
265
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000266void SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
267 if (!dx && !dy) {
268 return;
269 }
270
tomhudson@google.com8d430182011-06-06 19:11:19 +0000271 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 SkMatrix m;
273 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000274 this->postConcat(m);
275 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 fMat[kMTransX] += dx;
277 fMat[kMTransY] += dy;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000278 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280}
281
282///////////////////////////////////////////////////////////////////////////////
283
284void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000285 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000286 this->reset();
287 } else {
288 fMat[kMScaleX] = sx;
289 fMat[kMScaleY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000290 fMat[kMTransX] = px - sx * px;
291 fMat[kMTransY] = py - sy * py;
292 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000294 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000295 fMat[kMPersp0] = fMat[kMPersp1] = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000296
reed@google.comf244f902011-09-06 21:02:36 +0000297 this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299}
300
301void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000302 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000303 this->reset();
304 } else {
305 fMat[kMScaleX] = sx;
306 fMat[kMScaleY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000307 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308
reed@google.comf244f902011-09-06 21:02:36 +0000309 fMat[kMTransX] = fMat[kMTransY] =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000310 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000311 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312
reed@google.comf244f902011-09-06 21:02:36 +0000313 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315}
316
bsalomon@google.com5c638652011-07-18 19:31:59 +0000317bool SkMatrix::setIDiv(int divx, int divy) {
318 if (!divx || !divy) {
319 return false;
320 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000321 this->setScale(SkScalarInvert(divx), SkScalarInvert(divy));
bsalomon@google.com5c638652011-07-18 19:31:59 +0000322 return true;
323}
324
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000325void SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
326 if (1 == sx && 1 == sy) {
327 return;
328 }
329
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 SkMatrix m;
331 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000332 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333}
334
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000335void SkMatrix::preScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000336 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000337 return;
reed@google.comf244f902011-09-06 21:02:36 +0000338 }
339
reed@google.com3fb51872011-06-01 15:11:22 +0000340 // the assumption is that these multiplies are very cheap, and that
341 // a full concat and/or just computing the matrix type is more expensive.
342 // Also, the fixed-point case checks for overflow, but the float doesn't,
343 // so we can get away with these blind multiplies.
344
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000345 fMat[kMScaleX] *= sx;
346 fMat[kMSkewY] *= sx;
347 fMat[kMPersp0] *= sx;
reed@google.com3fb51872011-06-01 15:11:22 +0000348
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000349 fMat[kMSkewX] *= sy;
350 fMat[kMScaleY] *= sy;
351 fMat[kMPersp1] *= sy;
reed@google.com3fb51872011-06-01 15:11:22 +0000352
353 this->orTypeMask(kScale_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354}
355
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000356void SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000357 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000358 return;
reed@google.comf244f902011-09-06 21:02:36 +0000359 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 SkMatrix m;
361 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000362 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363}
364
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000365void SkMatrix::postScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000366 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000367 return;
reed@google.comf244f902011-09-06 21:02:36 +0000368 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 SkMatrix m;
370 m.setScale(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000371 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372}
373
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374// this guy perhaps can go away, if we have a fract/high-precision way to
375// scale matrices
376bool SkMatrix::postIDiv(int divx, int divy) {
377 if (divx == 0 || divy == 0) {
378 return false;
379 }
380
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 const float invX = 1.f / divx;
382 const float invY = 1.f / divy;
383
384 fMat[kMScaleX] *= invX;
385 fMat[kMSkewX] *= invX;
386 fMat[kMTransX] *= invX;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000387
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 fMat[kMScaleY] *= invY;
389 fMat[kMSkewY] *= invY;
390 fMat[kMTransY] *= invY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391
392 this->setTypeMask(kUnknown_Mask);
393 return true;
394}
395
396////////////////////////////////////////////////////////////////////////////////////
397
398void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV,
399 SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000400 const SkScalar oneMinusCosV = 1 - cosV;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401
402 fMat[kMScaleX] = cosV;
403 fMat[kMSkewX] = -sinV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000404 fMat[kMTransX] = sdot(sinV, py, oneMinusCosV, px);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405
406 fMat[kMSkewY] = sinV;
407 fMat[kMScaleY] = cosV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000408 fMat[kMTransY] = sdot(-sinV, px, oneMinusCosV, py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409
410 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000411 fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000412
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000413 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414}
415
416void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
417 fMat[kMScaleX] = cosV;
418 fMat[kMSkewX] = -sinV;
419 fMat[kMTransX] = 0;
420
421 fMat[kMSkewY] = sinV;
422 fMat[kMScaleY] = cosV;
423 fMat[kMTransY] = 0;
424
425 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000426 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000428 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429}
430
431void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
432 SkScalar sinV, cosV;
433 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
434 this->setSinCos(sinV, cosV, px, py);
435}
436
437void SkMatrix::setRotate(SkScalar degrees) {
438 SkScalar sinV, cosV;
439 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
440 this->setSinCos(sinV, cosV);
441}
442
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000443void SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 SkMatrix m;
445 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000446 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447}
448
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000449void SkMatrix::preRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 SkMatrix m;
451 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000452 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453}
454
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000455void SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 SkMatrix m;
457 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000458 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459}
460
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000461void SkMatrix::postRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 SkMatrix m;
463 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000464 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465}
466
467////////////////////////////////////////////////////////////////////////////////////
468
469void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000470 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 fMat[kMSkewX] = sx;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000472 fMat[kMTransX] = -sx * py;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473
474 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000475 fMat[kMScaleY] = 1;
476 fMat[kMTransY] = -sy * px;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477
478 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000479 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000480
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000481 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482}
483
484void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000485 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 fMat[kMSkewX] = sx;
487 fMat[kMTransX] = 0;
488
489 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000490 fMat[kMScaleY] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 fMat[kMTransY] = 0;
492
493 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000494 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000496 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497}
498
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000499void SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 SkMatrix m;
501 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000502 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503}
504
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000505void SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 SkMatrix m;
507 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000508 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509}
510
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000511void SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 SkMatrix m;
513 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000514 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515}
516
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000517void SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 SkMatrix m;
519 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000520 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521}
522
523///////////////////////////////////////////////////////////////////////////////
524
525bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst,
526 ScaleToFit align)
527{
528 if (src.isEmpty()) {
529 this->reset();
530 return false;
531 }
532
533 if (dst.isEmpty()) {
reed@android.com4516f472009-06-29 16:25:36 +0000534 sk_bzero(fMat, 8 * sizeof(SkScalar));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
536 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000537 SkScalar tx, sx = dst.width() / src.width();
538 SkScalar ty, sy = dst.height() / src.height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 bool xLarger = false;
540
541 if (align != kFill_ScaleToFit) {
542 if (sx > sy) {
543 xLarger = true;
544 sx = sy;
545 } else {
546 sy = sx;
547 }
548 }
549
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000550 tx = dst.fLeft - src.fLeft * sx;
551 ty = dst.fTop - src.fTop * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
553 SkScalar diff;
554
555 if (xLarger) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000556 diff = dst.width() - src.width() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000558 diff = dst.height() - src.height() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 if (align == kCenter_ScaleToFit) {
562 diff = SkScalarHalf(diff);
563 }
564
565 if (xLarger) {
566 tx += diff;
567 } else {
568 ty += diff;
569 }
570 }
571
572 fMat[kMScaleX] = sx;
573 fMat[kMScaleY] = sy;
574 fMat[kMTransX] = tx;
575 fMat[kMTransY] = ty;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000576 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 fMat[kMPersp0] = fMat[kMPersp1] = 0;
578
reed@google.com97cd69c2012-10-12 14:35:48 +0000579 unsigned mask = kRectStaysRect_Mask;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000580 if (sx != 1 || sy != 1) {
reed@google.com97cd69c2012-10-12 14:35:48 +0000581 mask |= kScale_Mask;
582 }
583 if (tx || ty) {
584 mask |= kTranslate_Mask;
585 }
586 this->setTypeMask(mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 }
588 // shared cleanup
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000589 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 return true;
591}
592
593///////////////////////////////////////////////////////////////////////////////
594
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000595static inline float muladdmul(float a, float b, float c, float d) {
596 return SkDoubleToFloat((double)a * b + (double)c * d);
reed@google.com8f4d2302013-12-17 16:44:46 +0000597}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000599static inline float rowcol3(const float row[], const float col[]) {
600 return row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
reed@google.com8f4d2302013-12-17 16:44:46 +0000601}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602
603static void normalize_perspective(SkScalar mat[9]) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000604 if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 for (int i = 0; i < 9; i++)
606 mat[i] = SkScalarHalf(mat[i]);
607 }
608}
609
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000610void SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000611 TypeMask aType = a.getPerspectiveTypeMaskOnly();
612 TypeMask bType = b.getPerspectiveTypeMaskOnly();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000614 if (a.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 *this = b;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000616 } else if (b.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 *this = a;
618 } else {
619 SkMatrix tmp;
620
621 if ((aType | bType) & kPerspective_Mask) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000622 tmp.fMat[kMScaleX] = rowcol3(&a.fMat[0], &b.fMat[0]);
623 tmp.fMat[kMSkewX] = rowcol3(&a.fMat[0], &b.fMat[1]);
624 tmp.fMat[kMTransX] = rowcol3(&a.fMat[0], &b.fMat[2]);
625 tmp.fMat[kMSkewY] = rowcol3(&a.fMat[3], &b.fMat[0]);
626 tmp.fMat[kMScaleY] = rowcol3(&a.fMat[3], &b.fMat[1]);
627 tmp.fMat[kMTransY] = rowcol3(&a.fMat[3], &b.fMat[2]);
628 tmp.fMat[kMPersp0] = rowcol3(&a.fMat[6], &b.fMat[0]);
629 tmp.fMat[kMPersp1] = rowcol3(&a.fMat[6], &b.fMat[1]);
630 tmp.fMat[kMPersp2] = rowcol3(&a.fMat[6], &b.fMat[2]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631
632 normalize_perspective(tmp.fMat);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000633 tmp.setTypeMask(kUnknown_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 } else { // not perspective
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000635 tmp.fMat[kMScaleX] = muladdmul(a.fMat[kMScaleX],
636 b.fMat[kMScaleX],
637 a.fMat[kMSkewX],
638 b.fMat[kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000640 tmp.fMat[kMSkewX] = muladdmul(a.fMat[kMScaleX],
641 b.fMat[kMSkewX],
642 a.fMat[kMSkewX],
643 b.fMat[kMScaleY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000645 tmp.fMat[kMTransX] = muladdmul(a.fMat[kMScaleX],
646 b.fMat[kMTransX],
647 a.fMat[kMSkewX],
648 b.fMat[kMTransY]);
649
650 tmp.fMat[kMTransX] += a.fMat[kMTransX];
651
652 tmp.fMat[kMSkewY] = muladdmul(a.fMat[kMSkewY],
653 b.fMat[kMScaleX],
654 a.fMat[kMScaleY],
655 b.fMat[kMSkewY]);
656
657 tmp.fMat[kMScaleY] = muladdmul(a.fMat[kMSkewY],
658 b.fMat[kMSkewX],
659 a.fMat[kMScaleY],
660 b.fMat[kMScaleY]);
661
662 tmp.fMat[kMTransY] = muladdmul(a.fMat[kMSkewY],
663 b.fMat[kMTransX],
664 a.fMat[kMScaleY],
665 b.fMat[kMTransY]);
666
667 tmp.fMat[kMTransY] += a.fMat[kMTransY];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000669 tmp.fMat[kMPersp2] = 1;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000670 //SkDebugf("Concat mat non-persp type: %d\n", tmp.getType());
671 //SkASSERT(!(tmp.getType() & kPerspective_Mask));
672 tmp.setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673 }
674 *this = tmp;
675 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676}
677
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000678void SkMatrix::preConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 // check for identity first, so we don't do a needless copy of ourselves
680 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000681 if(!mat.isIdentity()) {
682 this->setConcat(*this, mat);
683 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684}
685
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000686void SkMatrix::postConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 // check for identity first, so we don't do a needless copy of ourselves
688 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000689 if (!mat.isIdentity()) {
690 this->setConcat(mat, *this);
691 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692}
693
694///////////////////////////////////////////////////////////////////////////////
695
reed@android.com0b9e2db2009-09-16 17:00:17 +0000696/* Matrix inversion is very expensive, but also the place where keeping
697 precision may be most important (here and matrix concat). Hence to avoid
698 bitmap blitting artifacts when walking the inverse, we use doubles for
699 the intermediate math, even though we know that is more expensive.
reed@android.com0b9e2db2009-09-16 17:00:17 +0000700 */
701
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000702static inline SkScalar scross_dscale(SkScalar a, SkScalar b,
703 SkScalar c, SkScalar d, double scale) {
704 return SkDoubleToScalar(scross(a, b, c, d) * scale);
705}
706
707static inline double dcross(double a, double b, double c, double d) {
708 return a * b - c * d;
709}
710
711static inline SkScalar dcross_dscale(double a, double b,
712 double c, double d, double scale) {
713 return SkDoubleToScalar(dcross(a, b, c, d) * scale);
714}
715
716static double sk_inv_determinant(const float mat[9], int isPerspective) {
reed@google.com8f4d2302013-12-17 16:44:46 +0000717 double det;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718
reed@google.com8f4d2302013-12-17 16:44:46 +0000719 if (isPerspective) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000720 det = mat[SkMatrix::kMScaleX] *
721 dcross(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2],
722 mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1])
723 +
724 mat[SkMatrix::kMSkewX] *
725 dcross(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0],
726 mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2])
727 +
728 mat[SkMatrix::kMTransX] *
729 dcross(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1],
730 mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]);
reed@google.com8f4d2302013-12-17 16:44:46 +0000731 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000732 det = dcross(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY],
733 mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 }
735
reed@google.com8f4d2302013-12-17 16:44:46 +0000736 // Since the determinant is on the order of the cube of the matrix members,
737 // compare to the cube of the default nearly-zero constant (although an
738 // estimate of the condition number would be better if it wasn't so expensive).
739 if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
740 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 }
reed@google.com8f4d2302013-12-17 16:44:46 +0000742 return 1.0 / det;
743}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000745void SkMatrix::SetAffineIdentity(SkScalar affine[6]) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000746 affine[kAScaleX] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000747 affine[kASkewY] = 0;
748 affine[kASkewX] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000749 affine[kAScaleY] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000750 affine[kATransX] = 0;
751 affine[kATransY] = 0;
752}
753
754bool SkMatrix::asAffine(SkScalar affine[6]) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000755 if (this->hasPerspective()) {
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000756 return false;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000757 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000758 if (affine) {
759 affine[kAScaleX] = this->fMat[kMScaleX];
760 affine[kASkewY] = this->fMat[kMSkewY];
761 affine[kASkewX] = this->fMat[kMSkewX];
762 affine[kAScaleY] = this->fMat[kMScaleY];
763 affine[kATransX] = this->fMat[kMTransX];
764 affine[kATransY] = this->fMat[kMTransY];
765 }
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000766 return true;
767}
768
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000769bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
770 SkASSERT(!this->isIdentity());
reed@google.com2fb96cc2013-01-04 17:02:33 +0000771
772 TypeMask mask = this->getType();
773
774 if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
reed@google.come40591d2013-01-30 15:47:42 +0000775 bool invertible = true;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000776 if (inv) {
777 if (mask & kScale_Mask) {
778 SkScalar invX = fMat[kMScaleX];
779 SkScalar invY = fMat[kMScaleY];
780 if (0 == invX || 0 == invY) {
781 return false;
782 }
783 invX = SkScalarInvert(invX);
784 invY = SkScalarInvert(invY);
785
786 // Must be careful when writing to inv, since it may be the
787 // same memory as this.
788
789 inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
790 inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
791
792 inv->fMat[kMScaleX] = invX;
793 inv->fMat[kMScaleY] = invY;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000794 inv->fMat[kMPersp2] = 1;
795 inv->fMat[kMTransX] = -fMat[kMTransX] * invX;
796 inv->fMat[kMTransY] = -fMat[kMTransY] * invY;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000797
798 inv->setTypeMask(mask | kRectStaysRect_Mask);
799 } else {
800 // translate only
801 inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
802 }
reed@google.come40591d2013-01-30 15:47:42 +0000803 } else { // inv is NULL, just check if we're invertible
804 if (!fMat[kMScaleX] || !fMat[kMScaleY]) {
805 invertible = false;
806 }
reed@google.com2fb96cc2013-01-04 17:02:33 +0000807 }
reed@google.come40591d2013-01-30 15:47:42 +0000808 return invertible;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000809 }
reed@google.com4a1362a2013-01-04 18:52:16 +0000810
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000811 int isPersp = mask & kPerspective_Mask;
812 double scale = sk_inv_determinant(fMat, isPersp);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813
814 if (scale == 0) { // underflow
815 return false;
816 }
817
818 if (inv) {
819 SkMatrix tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000820 if (inv == this) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 inv = &tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000822 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823
824 if (isPersp) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000825 inv->fMat[kMScaleX] = scross_dscale(fMat[kMScaleY], fMat[kMPersp2], fMat[kMTransY], fMat[kMPersp1], scale);
826 inv->fMat[kMSkewX] = scross_dscale(fMat[kMTransX], fMat[kMPersp1], fMat[kMSkewX], fMat[kMPersp2], scale);
827 inv->fMat[kMTransX] = scross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMTransX], fMat[kMScaleY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000829 inv->fMat[kMSkewY] = scross_dscale(fMat[kMTransY], fMat[kMPersp0], fMat[kMSkewY], fMat[kMPersp2], scale);
830 inv->fMat[kMScaleY] = scross_dscale(fMat[kMScaleX], fMat[kMPersp2], fMat[kMTransX], fMat[kMPersp0], scale);
831 inv->fMat[kMTransY] = scross_dscale(fMat[kMTransX], fMat[kMSkewY], fMat[kMScaleX], fMat[kMTransY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000833 inv->fMat[kMPersp0] = scross_dscale(fMat[kMSkewY], fMat[kMPersp1], fMat[kMScaleY], fMat[kMPersp0], scale);
834 inv->fMat[kMPersp1] = scross_dscale(fMat[kMSkewX], fMat[kMPersp0], fMat[kMScaleX], fMat[kMPersp1], scale);
835 inv->fMat[kMPersp2] = scross_dscale(fMat[kMScaleX], fMat[kMScaleY], fMat[kMSkewX], fMat[kMSkewY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 } else { // not perspective
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000837 inv->fMat[kMScaleX] = SkDoubleToScalar(fMat[kMScaleY] * scale);
838 inv->fMat[kMSkewX] = SkDoubleToScalar(-fMat[kMSkewX] * scale);
839 inv->fMat[kMTransX] = dcross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMScaleY], fMat[kMTransX], scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000840
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000841 inv->fMat[kMSkewY] = SkDoubleToScalar(-fMat[kMSkewY] * scale);
842 inv->fMat[kMScaleY] = SkDoubleToScalar(fMat[kMScaleX] * scale);
843 inv->fMat[kMTransY] = dcross_dscale(fMat[kMSkewY], fMat[kMTransX], fMat[kMScaleX], fMat[kMTransY], scale);
reed@google.com83c6a222013-12-18 05:19:22 +0000844
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 inv->fMat[kMPersp0] = 0;
846 inv->fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000847 inv->fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 }
849
junov@chromium.org6fc56992012-07-12 14:01:32 +0000850 inv->setTypeMask(fTypeMask);
851
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 if (inv == &tmp) {
853 *(SkMatrix*)this = tmp;
854 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 }
856 return true;
857}
858
859///////////////////////////////////////////////////////////////////////////////
860
861void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
862 const SkPoint src[], int count) {
863 SkASSERT(m.getType() == 0);
864
865 if (dst != src && count > 0)
866 memcpy(dst, src, count * sizeof(SkPoint));
867}
868
869void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
870 const SkPoint src[], int count) {
871 SkASSERT(m.getType() == kTranslate_Mask);
872
873 if (count > 0) {
874 SkScalar tx = m.fMat[kMTransX];
875 SkScalar ty = m.fMat[kMTransY];
876 do {
877 dst->fY = src->fY + ty;
878 dst->fX = src->fX + tx;
879 src += 1;
880 dst += 1;
881 } while (--count);
882 }
883}
884
885void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
886 const SkPoint src[], int count) {
887 SkASSERT(m.getType() == kScale_Mask);
888
889 if (count > 0) {
890 SkScalar mx = m.fMat[kMScaleX];
891 SkScalar my = m.fMat[kMScaleY];
892 do {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000893 dst->fY = src->fY * my;
894 dst->fX = src->fX * mx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 src += 1;
896 dst += 1;
897 } while (--count);
898 }
899}
900
901void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
902 const SkPoint src[], int count) {
903 SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
904
905 if (count > 0) {
906 SkScalar mx = m.fMat[kMScaleX];
907 SkScalar my = m.fMat[kMScaleY];
908 SkScalar tx = m.fMat[kMTransX];
909 SkScalar ty = m.fMat[kMTransY];
910 do {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000911 dst->fY = src->fY * my + ty;
912 dst->fX = src->fX * mx + tx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 src += 1;
914 dst += 1;
915 } while (--count);
916 }
917}
918
919void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
920 const SkPoint src[], int count) {
921 SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
922
923 if (count > 0) {
924 SkScalar mx = m.fMat[kMScaleX];
925 SkScalar my = m.fMat[kMScaleY];
926 SkScalar kx = m.fMat[kMSkewX];
927 SkScalar ky = m.fMat[kMSkewY];
928 do {
929 SkScalar sy = src->fY;
930 SkScalar sx = src->fX;
931 src += 1;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000932 dst->fY = sdot(sx, ky, sy, my);
933 dst->fX = sdot(sx, mx, sy, kx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 dst += 1;
935 } while (--count);
936 }
937}
938
939void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
940 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000941 SkASSERT(!m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942
943 if (count > 0) {
944 SkScalar mx = m.fMat[kMScaleX];
945 SkScalar my = m.fMat[kMScaleY];
946 SkScalar kx = m.fMat[kMSkewX];
947 SkScalar ky = m.fMat[kMSkewY];
948 SkScalar tx = m.fMat[kMTransX];
949 SkScalar ty = m.fMat[kMTransY];
950 do {
951 SkScalar sy = src->fY;
952 SkScalar sx = src->fX;
953 src += 1;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000954#ifdef SK_LEGACY_MATRIX_MATH_ORDER
955 dst->fY = sx * ky + (sy * my + ty);
956 dst->fX = sx * mx + (sy * kx + tx);
957#else
958 dst->fY = sdot(sx, ky, sy, my) + ty;
959 dst->fX = sdot(sx, mx, sy, kx) + tx;
960#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 dst += 1;
962 } while (--count);
963 }
964}
965
966void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
967 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000968 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 if (count > 0) {
971 do {
972 SkScalar sy = src->fY;
973 SkScalar sx = src->fX;
974 src += 1;
975
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000976 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
977 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
978#ifdef SK_LEGACY_MATRIX_MATH_ORDER
979 SkScalar z = sx * m.fMat[kMPersp0] + (sy * m.fMat[kMPersp1] + m.fMat[kMPersp2]);
980#else
981 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
982#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 if (z) {
984 z = SkScalarFastInvert(z);
985 }
986
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000987 dst->fY = y * z;
988 dst->fX = x * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 dst += 1;
990 } while (--count);
991 }
992}
993
994const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
995 SkMatrix::Identity_pts, SkMatrix::Trans_pts,
996 SkMatrix::Scale_pts, SkMatrix::ScaleTrans_pts,
997 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
998 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
999 // repeat the persp proc 8 times
1000 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1001 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1002 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1003 SkMatrix::Persp_pts, SkMatrix::Persp_pts
1004};
1005
1006void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001007 SkASSERT((dst && src && count > 0) || 0 == count);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 // no partial overlap
bungeman@google.com3dc82c42013-10-11 19:11:10 +00001009 SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010
1011 this->getMapPtsProc()(*this, dst, src, count);
1012}
1013
1014///////////////////////////////////////////////////////////////////////////////
1015
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001016void SkMatrix::mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const {
1017 SkASSERT((dst && src && count > 0) || 0 == count);
1018 // no partial overlap
1019 SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= 3*count);
1020
1021 if (count > 0) {
1022 if (this->isIdentity()) {
1023 memcpy(dst, src, 3*count*sizeof(SkScalar));
1024 return;
1025 }
1026 do {
1027 SkScalar sx = src[0];
1028 SkScalar sy = src[1];
1029 SkScalar sw = src[2];
1030 src += 3;
1031
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001032 SkScalar x = sdot(sx, fMat[kMScaleX], sy, fMat[kMSkewX], sw, fMat[kMTransX]);
1033 SkScalar y = sdot(sx, fMat[kMSkewY], sy, fMat[kMScaleY], sw, fMat[kMTransY]);
1034 SkScalar w = sdot(sx, fMat[kMPersp0], sy, fMat[kMPersp1], sw, fMat[kMPersp2]);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001035
1036 dst[0] = x;
1037 dst[1] = y;
1038 dst[2] = w;
1039 dst += 3;
1040 } while (--count);
1041 }
1042}
1043
1044///////////////////////////////////////////////////////////////////////////////
1045
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001047 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 SkPoint origin;
1049
1050 MapXYProc proc = this->getMapXYProc();
1051 proc(*this, 0, 0, &origin);
1052
1053 for (int i = count - 1; i >= 0; --i) {
1054 SkPoint tmp;
1055
1056 proc(*this, src[i].fX, src[i].fY, &tmp);
1057 dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
1058 }
1059 } else {
1060 SkMatrix tmp = *this;
1061
1062 tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
1063 tmp.clearTypeMask(kTranslate_Mask);
1064 tmp.mapPoints(dst, src, count);
1065 }
1066}
1067
1068bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
1069 SkASSERT(dst && &src);
1070
1071 if (this->rectStaysRect()) {
1072 this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
1073 dst->sort();
1074 return true;
1075 } else {
1076 SkPoint quad[4];
1077
1078 src.toQuad(quad);
1079 this->mapPoints(quad, quad, 4);
1080 dst->set(quad, 4);
1081 return false;
1082 }
1083}
1084
1085SkScalar SkMatrix::mapRadius(SkScalar radius) const {
1086 SkVector vec[2];
1087
1088 vec[0].set(radius, 0);
1089 vec[1].set(0, radius);
1090 this->mapVectors(vec, 2);
1091
1092 SkScalar d0 = vec[0].length();
1093 SkScalar d1 = vec[1].length();
1094
reed@google.com6e252d42013-12-18 05:06:52 +00001095 // return geometric mean
1096 return SkScalarSqrt(d0 * d1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097}
1098
1099///////////////////////////////////////////////////////////////////////////////
1100
1101void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1102 SkPoint* pt) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001103 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001105 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1106 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1107 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 if (z) {
1109 z = SkScalarFastInvert(z);
1110 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001111 pt->fX = x * z;
1112 pt->fY = y * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113}
1114
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1116 SkPoint* pt) {
1117 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001118
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001119#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1120 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1121 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1122#else
1123 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1124 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1125#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126}
1127
1128void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1129 SkPoint* pt) {
1130 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
1131 SkASSERT(0 == m.fMat[kMTransX]);
1132 SkASSERT(0 == m.fMat[kMTransY]);
1133
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001134#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1135 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1136 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1137#else
1138 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1139 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1140#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141}
1142
1143void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1144 SkPoint* pt) {
1145 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1146 == kScale_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001147
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001148 pt->fX = sx * m.fMat[kMScaleX] + m.fMat[kMTransX];
1149 pt->fY = sy * m.fMat[kMScaleY] + m.fMat[kMTransY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150}
1151
1152void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1153 SkPoint* pt) {
1154 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1155 == kScale_Mask);
1156 SkASSERT(0 == m.fMat[kMTransX]);
1157 SkASSERT(0 == m.fMat[kMTransY]);
1158
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001159 pt->fX = sx * m.fMat[kMScaleX];
1160 pt->fY = sy * m.fMat[kMScaleY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161}
1162
1163void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1164 SkPoint* pt) {
1165 SkASSERT(m.getType() == kTranslate_Mask);
1166
1167 pt->fX = sx + m.fMat[kMTransX];
1168 pt->fY = sy + m.fMat[kMTransY];
1169}
1170
1171void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1172 SkPoint* pt) {
1173 SkASSERT(0 == m.getType());
1174
1175 pt->fX = sx;
1176 pt->fY = sy;
1177}
1178
1179const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
1180 SkMatrix::Identity_xy, SkMatrix::Trans_xy,
1181 SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy,
1182 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1183 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1184 // repeat the persp proc 8 times
1185 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1186 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1187 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1188 SkMatrix::Persp_xy, SkMatrix::Persp_xy
1189};
1190
1191///////////////////////////////////////////////////////////////////////////////
1192
1193// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
reed@google.com83c6a222013-12-18 05:19:22 +00001194#define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26)))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195
1196bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
1197 if (PerspNearlyZero(fMat[kMPersp0])) {
1198 if (stepX || stepY) {
1199 if (PerspNearlyZero(fMat[kMPersp1]) &&
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001200 PerspNearlyZero(fMat[kMPersp2] - 1)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 if (stepX) {
1202 *stepX = SkScalarToFixed(fMat[kMScaleX]);
1203 }
1204 if (stepY) {
1205 *stepY = SkScalarToFixed(fMat[kMSkewY]);
1206 }
1207 } else {
reed@google.com83c6a222013-12-18 05:19:22 +00001208 SkScalar z = y * fMat[kMPersp1] + fMat[kMPersp2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 if (stepX) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001210 *stepX = SkScalarToFixed(fMat[kMScaleX] / z);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 }
1212 if (stepY) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001213 *stepY = SkScalarToFixed(fMat[kMSkewY] / z);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 }
1215 }
1216 }
1217 return true;
1218 }
1219 return false;
1220}
1221
1222///////////////////////////////////////////////////////////////////////////////
1223
1224#include "SkPerspIter.h"
1225
1226SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
1227 : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
1228 SkPoint pt;
1229
1230 SkMatrix::Persp_xy(m, x0, y0, &pt);
1231 fX = SkScalarToFixed(pt.fX);
1232 fY = SkScalarToFixed(pt.fY);
1233}
1234
1235int SkPerspIter::next() {
1236 int n = fCount;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001237
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 if (0 == n) {
1239 return 0;
1240 }
1241 SkPoint pt;
1242 SkFixed x = fX;
1243 SkFixed y = fY;
1244 SkFixed dx, dy;
1245
1246 if (n >= kCount) {
1247 n = kCount;
1248 fSX += SkIntToScalar(kCount);
1249 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1250 fX = SkScalarToFixed(pt.fX);
1251 fY = SkScalarToFixed(pt.fY);
1252 dx = (fX - x) >> kShift;
1253 dy = (fY - y) >> kShift;
1254 } else {
1255 fSX += SkIntToScalar(n);
1256 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1257 fX = SkScalarToFixed(pt.fX);
1258 fY = SkScalarToFixed(pt.fY);
1259 dx = (fX - x) / n;
1260 dy = (fY - y) / n;
1261 }
1262
1263 SkFixed* p = fStorage;
1264 for (int i = 0; i < n; i++) {
1265 *p++ = x; x += dx;
1266 *p++ = y; y += dy;
1267 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 fCount -= n;
1270 return n;
1271}
1272
1273///////////////////////////////////////////////////////////////////////////////
1274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275static inline bool checkForZero(float x) {
1276 return x*x == 0;
1277}
1278
1279static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1280 float x = 1, y = 1;
1281 SkPoint pt1, pt2;
1282
1283 if (count > 1) {
1284 pt1.fX = poly[1].fX - poly[0].fX;
1285 pt1.fY = poly[1].fY - poly[0].fY;
1286 y = SkPoint::Length(pt1.fX, pt1.fY);
1287 if (checkForZero(y)) {
1288 return false;
1289 }
1290 switch (count) {
1291 case 2:
1292 break;
1293 case 3:
1294 pt2.fX = poly[0].fY - poly[2].fY;
1295 pt2.fY = poly[2].fX - poly[0].fX;
1296 goto CALC_X;
1297 default:
1298 pt2.fX = poly[0].fY - poly[3].fY;
1299 pt2.fY = poly[3].fX - poly[0].fX;
1300 CALC_X:
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001301 x = sdot(pt1.fX, pt2.fX, pt1.fY, pt2.fY) / y;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 break;
1303 }
1304 }
1305 pt->set(x, y);
1306 return true;
1307}
1308
1309bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1310 const SkPoint& scale) {
1311 float invScale = 1 / scale.fY;
1312
1313 dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1314 dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
1315 dst->fMat[kMPersp0] = 0;
1316 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1317 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1318 dst->fMat[kMPersp1] = 0;
1319 dst->fMat[kMTransX] = srcPt[0].fX;
1320 dst->fMat[kMTransY] = srcPt[0].fY;
1321 dst->fMat[kMPersp2] = 1;
1322 dst->setTypeMask(kUnknown_Mask);
1323 return true;
1324}
1325
1326bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1327 const SkPoint& scale) {
1328 float invScale = 1 / scale.fX;
1329 dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
1330 dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
1331 dst->fMat[kMPersp0] = 0;
1332
1333 invScale = 1 / scale.fY;
1334 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1335 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1336 dst->fMat[kMPersp1] = 0;
1337
1338 dst->fMat[kMTransX] = srcPt[0].fX;
1339 dst->fMat[kMTransY] = srcPt[0].fY;
1340 dst->fMat[kMPersp2] = 1;
1341 dst->setTypeMask(kUnknown_Mask);
1342 return true;
1343}
1344
1345bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1346 const SkPoint& scale) {
1347 float a1, a2;
1348 float x0, y0, x1, y1, x2, y2;
1349
1350 x0 = srcPt[2].fX - srcPt[0].fX;
1351 y0 = srcPt[2].fY - srcPt[0].fY;
1352 x1 = srcPt[2].fX - srcPt[1].fX;
1353 y1 = srcPt[2].fY - srcPt[1].fY;
1354 x2 = srcPt[2].fX - srcPt[3].fX;
1355 y2 = srcPt[2].fY - srcPt[3].fY;
1356
1357 /* check if abs(x2) > abs(y2) */
1358 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1359 float denom = SkScalarMulDiv(x1, y2, x2) - y1;
1360 if (checkForZero(denom)) {
1361 return false;
1362 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001363 a1 = (SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 } else {
1365 float denom = x1 - SkScalarMulDiv(y1, x2, y2);
1366 if (checkForZero(denom)) {
1367 return false;
1368 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001369 a1 = (x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 }
1371
1372 /* check if abs(x1) > abs(y1) */
1373 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1374 float denom = y2 - SkScalarMulDiv(x2, y1, x1);
1375 if (checkForZero(denom)) {
1376 return false;
1377 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001378 a2 = (y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 } else {
1380 float denom = SkScalarMulDiv(y2, x1, y1) - x2;
1381 if (checkForZero(denom)) {
1382 return false;
1383 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001384 a2 = (SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385 }
1386
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001387 float invScale = SkScalarInvert(scale.fX);
1388 dst->fMat[kMScaleX] = (a2 * srcPt[3].fX + srcPt[3].fX - srcPt[0].fX) * invScale;
1389 dst->fMat[kMSkewY] = (a2 * srcPt[3].fY + srcPt[3].fY - srcPt[0].fY) * invScale;
1390 dst->fMat[kMPersp0] = a2 * invScale;
1391
1392 invScale = SkScalarInvert(scale.fY);
1393 dst->fMat[kMSkewX] = (a1 * srcPt[1].fX + srcPt[1].fX - srcPt[0].fX) * invScale;
1394 dst->fMat[kMScaleY] = (a1 * srcPt[1].fY + srcPt[1].fY - srcPt[0].fY) * invScale;
1395 dst->fMat[kMPersp1] = a1 * invScale;
1396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 dst->fMat[kMTransX] = srcPt[0].fX;
1398 dst->fMat[kMTransY] = srcPt[0].fY;
1399 dst->fMat[kMPersp2] = 1;
1400 dst->setTypeMask(kUnknown_Mask);
1401 return true;
1402}
1403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
1405
1406/* Taken from Rob Johnson's original sample code in QuickDraw GX
1407*/
1408bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
1409 int count) {
1410 if ((unsigned)count > 4) {
1411 SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
1412 return false;
1413 }
1414
1415 if (0 == count) {
1416 this->reset();
1417 return true;
1418 }
1419 if (1 == count) {
1420 this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
1421 return true;
1422 }
1423
1424 SkPoint scale;
1425 if (!poly_to_point(&scale, src, count) ||
1426 SkScalarNearlyZero(scale.fX) ||
1427 SkScalarNearlyZero(scale.fY)) {
1428 return false;
1429 }
1430
1431 static const PolyMapProc gPolyMapProcs[] = {
1432 SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
1433 };
1434 PolyMapProc proc = gPolyMapProcs[count - 2];
1435
1436 SkMatrix tempMap, result;
1437 tempMap.setTypeMask(kUnknown_Mask);
1438
1439 if (!proc(src, &tempMap, scale)) {
1440 return false;
1441 }
1442 if (!tempMap.invert(&result)) {
1443 return false;
1444 }
1445 if (!proc(dst, &tempMap, scale)) {
1446 return false;
1447 }
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001448 this->setConcat(tempMap, result);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 return true;
1450}
1451
1452///////////////////////////////////////////////////////////////////////////////
1453
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001454enum MinOrMax {
1455 kMin_MinOrMax,
1456 kMax_MinOrMax
1457};
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001458
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001459template <MinOrMax MIN_OR_MAX> SkScalar get_stretch_factor(SkMatrix::TypeMask typeMask,
1460 const SkScalar m[9]) {
1461 if (typeMask & SkMatrix::kPerspective_Mask) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001462 return -1;
bsalomon@google.com38396322011-09-09 19:32:04 +00001463 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001464 if (SkMatrix::kIdentity_Mask == typeMask) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001465 return 1;
bsalomon@google.com38396322011-09-09 19:32:04 +00001466 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001467 if (!(typeMask & SkMatrix::kAffine_Mask)) {
1468 if (kMin_MinOrMax == MIN_OR_MAX) {
1469 return SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1470 SkScalarAbs(m[SkMatrix::kMScaleY]));
1471 } else {
1472 return SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1473 SkScalarAbs(m[SkMatrix::kMScaleY]));
1474 }
bsalomon@google.com38396322011-09-09 19:32:04 +00001475 }
1476 // ignore the translation part of the matrix, just look at 2x2 portion.
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001477 // compute singular values, take largest or smallest abs value.
bsalomon@google.com38396322011-09-09 19:32:04 +00001478 // [a b; b c] = A^T*A
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001479 SkScalar a = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX],
1480 m[SkMatrix::kMSkewY], m[SkMatrix::kMSkewY]);
1481 SkScalar b = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX],
1482 m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]);
1483 SkScalar c = sdot(m[SkMatrix::kMSkewX], m[SkMatrix::kMSkewX],
1484 m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]);
bsalomon@google.com38396322011-09-09 19:32:04 +00001485 // eigenvalues of A^T*A are the squared singular values of A.
1486 // characteristic equation is det((A^T*A) - l*I) = 0
1487 // l^2 - (a + c)l + (ac-b^2)
1488 // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001489 // and roots are guaranteed to be pos and real).
1490 SkScalar chosenRoot;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001491 SkScalar bSqd = b * b;
bsalomon@google.com38396322011-09-09 19:32:04 +00001492 // if upper left 2x2 is orthogonal save some math
jvanverth@google.comc490f802013-03-04 13:56:38 +00001493 if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001494 if (kMin_MinOrMax == MIN_OR_MAX) {
1495 chosenRoot = SkMinScalar(a, c);
1496 } else {
1497 chosenRoot = SkMaxScalar(a, c);
1498 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001499 } else {
bsalomon@google.com38396322011-09-09 19:32:04 +00001500 SkScalar aminusc = a - c;
1501 SkScalar apluscdiv2 = SkScalarHalf(a + c);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001502 SkScalar x = SkScalarHalf(SkScalarSqrt(aminusc * aminusc + 4 * bSqd));
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001503 if (kMin_MinOrMax == MIN_OR_MAX) {
1504 chosenRoot = apluscdiv2 - x;
1505 } else {
1506 chosenRoot = apluscdiv2 + x;
1507 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001508 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001509 SkASSERT(chosenRoot >= 0);
1510 return SkScalarSqrt(chosenRoot);
1511}
1512
1513SkScalar SkMatrix::getMinStretch() const {
1514 return get_stretch_factor<kMin_MinOrMax>(this->getType(), fMat);
1515}
1516
1517SkScalar SkMatrix::getMaxStretch() const {
1518 return get_stretch_factor<kMax_MinOrMax>(this->getType(), fMat);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001519}
1520
commit-bot@chromium.org1f81fd62013-10-23 14:44:08 +00001521static void reset_identity_matrix(SkMatrix* identity) {
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +00001522 identity->reset();
1523}
1524
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001525const SkMatrix& SkMatrix::I() {
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +00001526 // If you can use C++11 now, you might consider replacing this with a constexpr constructor.
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001527 static SkMatrix gIdentity;
commit-bot@chromium.org1f81fd62013-10-23 14:44:08 +00001528 SK_DECLARE_STATIC_ONCE(once);
1529 SkOnce(&once, reset_identity_matrix, &gIdentity);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001530 return gIdentity;
tomhudson@google.com1f902872012-06-01 13:15:47 +00001531}
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001532
1533const SkMatrix& SkMatrix::InvalidMatrix() {
1534 static SkMatrix gInvalid;
1535 static bool gOnce;
1536 if (!gOnce) {
1537 gInvalid.setAll(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1538 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1539 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1540 gInvalid.getType(); // force the type to be computed
1541 gOnce = true;
1542 }
1543 return gInvalid;
1544}
1545
1546///////////////////////////////////////////////////////////////////////////////
1547
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001548size_t SkMatrix::writeToMemory(void* buffer) const {
reed@android.com0ad336f2009-06-29 16:02:20 +00001549 // TODO write less for simple matrices
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001550 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
reed@android.com0ad336f2009-06-29 16:02:20 +00001551 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001552 memcpy(buffer, fMat, sizeInMemory);
reed@android.com0ad336f2009-06-29 16:02:20 +00001553 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001554 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001555}
1556
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001557size_t SkMatrix::readFromMemory(const void* buffer, size_t length) {
1558 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
1559 if (length < sizeInMemory) {
1560 return 0;
1561 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001562 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001563 memcpy(fMat, buffer, sizeInMemory);
reed@android.comf2b98d62010-12-20 18:26:13 +00001564 this->setTypeMask(kUnknown_Mask);
1565 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001566 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001567}
1568
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001569#ifdef SK_DEVELOPER
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570void SkMatrix::dump() const {
1571 SkString str;
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001572 this->toString(&str);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 SkDebugf("%s\n", str.c_str());
1574}
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001575#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001577#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001578void SkMatrix::toString(SkString* str) const {
1579 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 +00001580 fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
1581 fMat[6], fMat[7], fMat[8]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582}
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001583#endif
reed@google.comad514302013-01-02 20:19:45 +00001584
1585///////////////////////////////////////////////////////////////////////////////
1586
1587#include "SkMatrixUtils.h"
1588
reed@google.comae573582013-01-03 15:22:40 +00001589bool SkTreatAsSprite(const SkMatrix& mat, int width, int height,
reed@google.comad514302013-01-02 20:19:45 +00001590 unsigned subpixelBits) {
reed@google.comae573582013-01-03 15:22:40 +00001591 // quick reject on affine or perspective
reed@google.comad514302013-01-02 20:19:45 +00001592 if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
1593 return false;
1594 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001595
reed@google.comad514302013-01-02 20:19:45 +00001596 // quick success check
1597 if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
1598 return true;
1599 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001600
reed@google.comad514302013-01-02 20:19:45 +00001601 // mapRect supports negative scales, so we eliminate those first
1602 if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
1603 return false;
1604 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001605
reed@google.comad514302013-01-02 20:19:45 +00001606 SkRect dst;
reed@google.comae573582013-01-03 15:22:40 +00001607 SkIRect isrc = { 0, 0, width, height };
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00001608
reed@google.comad514302013-01-02 20:19:45 +00001609 {
reed@google.comae573582013-01-03 15:22:40 +00001610 SkRect src;
1611 src.set(isrc);
1612 mat.mapRect(&dst, src);
reed@google.comad514302013-01-02 20:19:45 +00001613 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001614
reed@google.comae573582013-01-03 15:22:40 +00001615 // just apply the translate to isrc
1616 isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
1617 SkScalarRoundToInt(mat.getTranslateY()));
1618
reed@google.comad514302013-01-02 20:19:45 +00001619 if (subpixelBits) {
1620 isrc.fLeft <<= subpixelBits;
1621 isrc.fTop <<= subpixelBits;
1622 isrc.fRight <<= subpixelBits;
1623 isrc.fBottom <<= subpixelBits;
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001624
reed@google.comad514302013-01-02 20:19:45 +00001625 const float scale = 1 << subpixelBits;
1626 dst.fLeft *= scale;
1627 dst.fTop *= scale;
1628 dst.fRight *= scale;
1629 dst.fBottom *= scale;
1630 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001631
reed@google.comae573582013-01-03 15:22:40 +00001632 SkIRect idst;
reed@google.comad514302013-01-02 20:19:45 +00001633 dst.round(&idst);
1634 return isrc == idst;
1635}
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001636
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001637// A square matrix M can be decomposed (via polar decomposition) into two matrices --
1638// an orthogonal matrix Q and a symmetric matrix S. In turn we can decompose S into U*W*U^T,
1639// where U is another orthogonal matrix and W is a scale matrix. These can be recombined
1640// to give M = (Q*U)*W*U^T, i.e., the product of two orthogonal matrices and a scale matrix.
1641//
1642// The one wrinkle is that traditionally Q may contain a reflection -- the
1643// calculation has been rejiggered to put that reflection into W.
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001644bool SkDecomposeUpper2x2(const SkMatrix& matrix,
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001645 SkPoint* rotation1,
1646 SkPoint* scale,
1647 SkPoint* rotation2) {
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001648
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001649 SkScalar A = matrix[SkMatrix::kMScaleX];
1650 SkScalar B = matrix[SkMatrix::kMSkewX];
1651 SkScalar C = matrix[SkMatrix::kMSkewY];
1652 SkScalar D = matrix[SkMatrix::kMScaleY];
1653
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +00001654 if (is_degenerate_2x2(A, B, C, D)) {
1655 return false;
1656 }
1657
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001658 double w1, w2;
1659 SkScalar cos1, sin1;
1660 SkScalar cos2, sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001661
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001662 // do polar decomposition (M = Q*S)
1663 SkScalar cosQ, sinQ;
1664 double Sa, Sb, Sd;
1665 // if M is already symmetric (i.e., M = I*S)
1666 if (SkScalarNearlyEqual(B, C)) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001667 cosQ = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001668 sinQ = 0;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001669
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001670 Sa = A;
1671 Sb = B;
1672 Sd = D;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001673 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001674 cosQ = A + D;
1675 sinQ = C - B;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001676 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cosQ*cosQ + sinQ*sinQ));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001677 cosQ *= reciplen;
1678 sinQ *= reciplen;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001679
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001680 // S = Q^-1*M
1681 // we don't calc Sc since it's symmetric
1682 Sa = A*cosQ + C*sinQ;
1683 Sb = B*cosQ + D*sinQ;
1684 Sd = -B*sinQ + D*cosQ;
1685 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +00001686
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001687 // Now we need to compute eigenvalues of S (our scale factors)
1688 // and eigenvectors (bases for our rotation)
1689 // From this, should be able to reconstruct S as U*W*U^T
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001690 if (SkScalarNearlyZero(SkDoubleToScalar(Sb))) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001691 // already diagonalized
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001692 cos1 = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001693 sin1 = 0;
1694 w1 = Sa;
1695 w2 = Sd;
1696 cos2 = cosQ;
1697 sin2 = sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001698 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001699 double diff = Sa - Sd;
1700 double discriminant = sqrt(diff*diff + 4.0*Sb*Sb);
1701 double trace = Sa + Sd;
1702 if (diff > 0) {
1703 w1 = 0.5*(trace + discriminant);
1704 w2 = 0.5*(trace - discriminant);
1705 } else {
1706 w1 = 0.5*(trace - discriminant);
1707 w2 = 0.5*(trace + discriminant);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001708 }
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001709
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001710 cos1 = SkDoubleToScalar(Sb); sin1 = SkDoubleToScalar(w1 - Sa);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001711 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cos1*cos1 + sin1*sin1));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001712 cos1 *= reciplen;
1713 sin1 *= reciplen;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001714
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001715 // rotation 2 is composition of Q and U
1716 cos2 = cos1*cosQ - sin1*sinQ;
1717 sin2 = sin1*cosQ + cos1*sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001718
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001719 // rotation 1 is U^T
1720 sin1 = -sin1;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001721 }
1722
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001723 if (NULL != scale) {
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001724 scale->fX = SkDoubleToScalar(w1);
1725 scale->fY = SkDoubleToScalar(w2);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001726 }
1727 if (NULL != rotation1) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001728 rotation1->fX = cos1;
1729 rotation1->fY = sin1;
1730 }
1731 if (NULL != rotation2) {
1732 rotation2->fX = cos2;
1733 rotation2->fY = sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001734 }
1735
1736 return true;
1737}