blob: 2a295870ddee6d6f7a5538d3a62c42a2f21e062a [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"
10#include "SkString.h"
11
mtkleinada36352014-06-02 10:59:35 -070012#include <stddef.h>
13
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000014// In a few places, we performed the following
15// a * b + c * d + e
16// as
17// a * b + (c * d + e)
18//
19// sdot and scross are indended to capture these compound operations into a
20// function, with an eye toward considering upscaling the intermediates to
21// doubles for more precision (as we do in concat and invert).
22//
23// However, these few lines that performed the last add before the "dot", cause
24// tiny image differences, so we guard that change until we see the impact on
25// chrome's layouttests.
26//
27#define SK_LEGACY_MATRIX_MATH_ORDER
reed@android.comab7ac022009-09-18 13:38:43 +000028
reed@google.com8f4d2302013-12-17 16:44:46 +000029static inline float SkDoubleToFloat(double x) {
30 return static_cast<float>(x);
31}
reed@android.com8a1c16f2008-12-17 15:59:43 +000032
33/* [scale-x skew-x trans-x] [X] [X']
34 [skew-y scale-y trans-y] * [Y] = [Y']
35 [persp-0 persp-1 persp-2] [1] [1 ]
36*/
37
38void SkMatrix::reset() {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000039 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000040 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +000041 fMat[kMTransX] = fMat[kMTransY] =
42 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000043
44 this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
45}
46
reed@android.com8a1c16f2008-12-17 15:59:43 +000047// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
48enum {
49 kTranslate_Shift,
50 kScale_Shift,
51 kAffine_Shift,
52 kPerspective_Shift,
53 kRectStaysRect_Shift
54};
55
reed@google.com8f4d2302013-12-17 16:44:46 +000056static const int32_t kScalar1Int = 0x3f800000;
reed@android.com8a1c16f2008-12-17 15:59:43 +000057
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000058uint8_t SkMatrix::computePerspectiveTypeMask() const {
junov@chromium.org6fc56992012-07-12 14:01:32 +000059 // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
60 // is a win, but replacing those below is not. We don't yet understand
61 // that result.
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000062 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +000063 // If this is a perspective transform, we return true for all other
bsalomon@google.com19263b12012-07-13 13:36:38 +000064 // transform flags - this does not disable any optimizations, respects
rmistry@google.comfbfcd562012-08-23 18:09:54 +000065 // the rule that the type mask must be conservative, and speeds up
junov@chromium.org6fc56992012-07-12 14:01:32 +000066 // type mask computation.
67 return SkToU8(kORableMasks);
68 }
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000069
junov@chromium.org6fc56992012-07-12 14:01:32 +000070 return SkToU8(kOnlyPerspectiveValid_Mask | kUnknown_Mask);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000071}
72
reed@android.com8a1c16f2008-12-17 15:59:43 +000073uint8_t SkMatrix::computeTypeMask() const {
74 unsigned mask = 0;
75
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000076 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000077 // Once it is determined that that this is a perspective transform,
78 // all other flags are moot as far as optimizations are concerned.
79 return SkToU8(kORableMasks);
tomhudson@google.comac385252011-06-06 15:18:28 +000080 }
81
82 if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
83 mask |= kTranslate_Mask;
84 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
86 int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
87 int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
88 int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
89 int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
tomhudson@google.comac385252011-06-06 15:18:28 +000090
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 if (m01 | m10) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000092 // The skew components may be scale-inducing, unless we are dealing
93 // with a pure rotation. Testing for a pure rotation is expensive,
94 // so we opt for being conservative by always setting the scale bit.
95 // along with affine.
96 // By doing this, we are also ensuring that matrices have the same
97 // type masks as their inverses.
98 mask |= kAffine_Mask | kScale_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +000099
junov@chromium.org6fc56992012-07-12 14:01:32 +0000100 // For rectStaysRect, in the affine case, we only need check that
101 // the primary diagonal is all zeros and that the secondary diagonal
102 // is all non-zero.
tomhudson@google.comac385252011-06-06 15:18:28 +0000103
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 // map non-zero to 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 m01 = m01 != 0;
106 m10 = m10 != 0;
tomhudson@google.comac385252011-06-06 15:18:28 +0000107
junov@chromium.org6fc56992012-07-12 14:01:32 +0000108 int dp0 = 0 == (m00 | m11) ; // true if both are 0
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 int ds1 = m01 & m10; // true if both are 1
tomhudson@google.comac385252011-06-06 15:18:28 +0000110
junov@chromium.org6fc56992012-07-12 14:01:32 +0000111 mask |= (dp0 & ds1) << kRectStaysRect_Shift;
112 } else {
113 // Only test for scale explicitly if not affine, since affine sets the
114 // scale bit.
115 if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
116 mask |= kScale_Mask;
117 }
118
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000119 // Not affine, therefore we already know secondary diagonal is
junov@chromium.org6fc56992012-07-12 14:01:32 +0000120 // all zeros, so we just need to check that primary diagonal is
121 // all non-zero.
122
123 // map non-zero to 1
124 m00 = m00 != 0;
125 m11 = m11 != 0;
126
127 // record if the (p)rimary diagonal is all non-zero
128 mask |= (m00 & m11) << kRectStaysRect_Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 }
130
131 return SkToU8(mask);
132}
133
134///////////////////////////////////////////////////////////////////////////////
135
reed@google.com3fb51872011-06-01 15:11:22 +0000136bool operator==(const SkMatrix& a, const SkMatrix& b) {
137 const SkScalar* SK_RESTRICT ma = a.fMat;
138 const SkScalar* SK_RESTRICT mb = b.fMat;
139
140 return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
141 ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
142 ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
143}
144
reed@google.com3fb51872011-06-01 15:11:22 +0000145///////////////////////////////////////////////////////////////////////////////
146
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000147// helper function to determine if upper-left 2x2 of matrix is degenerate
skia.committer@gmail.com16d53aa2013-07-30 07:01:00 +0000148static inline bool is_degenerate_2x2(SkScalar scaleX, SkScalar skewX,
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000149 SkScalar skewY, SkScalar scaleY) {
150 SkScalar perp_dot = scaleX*scaleY - skewX*skewY;
151 return SkScalarNearlyZero(perp_dot, SK_ScalarNearlyZero*SK_ScalarNearlyZero);
152}
153
154///////////////////////////////////////////////////////////////////////////////
155
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000156bool SkMatrix::isSimilarity(SkScalar tol) const {
157 // if identity or translate matrix
158 TypeMask mask = this->getType();
159 if (mask <= kTranslate_Mask) {
160 return true;
161 }
162 if (mask & kPerspective_Mask) {
163 return false;
164 }
165
166 SkScalar mx = fMat[kMScaleX];
167 SkScalar my = fMat[kMScaleY];
168 // if no skew, can just compare scale factors
169 if (!(mask & kAffine_Mask)) {
170 return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
171 }
172 SkScalar sx = fMat[kMSkewX];
173 SkScalar sy = fMat[kMSkewY];
174
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000175 if (is_degenerate_2x2(mx, sx, sy, my)) {
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000176 return false;
177 }
178
jvanverth17a845f2014-09-02 13:15:40 -0700179 // upper 2x2 is rotation/reflection + uniform scale if basis vectors
180 // are 90 degree rotations of each other
181 return (SkScalarNearlyEqual(mx, my, tol) && SkScalarNearlyEqual(sx, -sy, tol))
182 || (SkScalarNearlyEqual(mx, -my, tol) && SkScalarNearlyEqual(sx, sy, tol));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000183}
184
185bool SkMatrix::preservesRightAngles(SkScalar tol) const {
186 TypeMask mask = this->getType();
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000187
jvanverth17a845f2014-09-02 13:15:40 -0700188 if (mask <= kTranslate_Mask) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000189 // identity, translate and/or scale
190 return true;
191 }
192 if (mask & kPerspective_Mask) {
193 return false;
194 }
195
jvanverth17a845f2014-09-02 13:15:40 -0700196 SkASSERT(mask & (kAffine_Mask | kScale_Mask));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000197
198 SkScalar mx = fMat[kMScaleX];
199 SkScalar my = fMat[kMScaleY];
200 SkScalar sx = fMat[kMSkewX];
201 SkScalar sy = fMat[kMSkewY];
202
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000203 if (is_degenerate_2x2(mx, sx, sy, my)) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000204 return false;
205 }
206
jvanverth17a845f2014-09-02 13:15:40 -0700207 // upper 2x2 is scale + rotation/reflection if basis vectors are orthogonal
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000208 SkVector vec[2];
jvanverth17a845f2014-09-02 13:15:40 -0700209 vec[0].set(mx, sy);
210 vec[1].set(sx, my);
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000211
jvanverth17a845f2014-09-02 13:15:40 -0700212 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000213}
214
215///////////////////////////////////////////////////////////////////////////////
216
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000217static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
218 return a * b + c * d;
219}
220
221static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
222 SkScalar e, SkScalar f) {
223 return a * b + c * d + e * f;
224}
225
226static inline SkScalar scross(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
227 return a * b - c * d;
228}
229
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
reed@google.comc0784db2013-12-13 21:16:12 +0000231 if (dx || dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 fMat[kMTransX] = dx;
233 fMat[kMTransY] = dy;
234
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000235 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000236 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238
239 this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
240 } else {
241 this->reset();
242 }
243}
244
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000245void SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
246 if (!dx && !dy) {
247 return;
248 }
249
tomhudson@google.com8d430182011-06-06 19:11:19 +0000250 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 SkMatrix m;
252 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000253 this->preConcat(m);
254 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000255 fMat[kMTransX] += sdot(fMat[kMScaleX], dx, fMat[kMSkewX], dy);
256 fMat[kMTransY] += sdot(fMat[kMSkewY], dx, fMat[kMScaleY], dy);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000257 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259}
260
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000261void SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
262 if (!dx && !dy) {
263 return;
264 }
265
tomhudson@google.com8d430182011-06-06 19:11:19 +0000266 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 SkMatrix m;
268 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000269 this->postConcat(m);
270 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 fMat[kMTransX] += dx;
272 fMat[kMTransY] += dy;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000273 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275}
276
277///////////////////////////////////////////////////////////////////////////////
278
279void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000280 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000281 this->reset();
282 } else {
283 fMat[kMScaleX] = sx;
284 fMat[kMScaleY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000285 fMat[kMTransX] = px - sx * px;
286 fMat[kMTransY] = py - sy * py;
287 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000289 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000290 fMat[kMPersp0] = fMat[kMPersp1] = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000291
reed@google.comf244f902011-09-06 21:02:36 +0000292 this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
293 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294}
295
296void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000297 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000298 this->reset();
299 } else {
300 fMat[kMScaleX] = sx;
301 fMat[kMScaleY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000302 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303
reed@google.comf244f902011-09-06 21:02:36 +0000304 fMat[kMTransX] = fMat[kMTransY] =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000305 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000306 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307
reed@google.comf244f902011-09-06 21:02:36 +0000308 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310}
311
bsalomon@google.com5c638652011-07-18 19:31:59 +0000312bool SkMatrix::setIDiv(int divx, int divy) {
313 if (!divx || !divy) {
314 return false;
315 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000316 this->setScale(SkScalarInvert(divx), SkScalarInvert(divy));
bsalomon@google.com5c638652011-07-18 19:31:59 +0000317 return true;
318}
319
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000320void SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
321 if (1 == sx && 1 == sy) {
322 return;
323 }
324
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 SkMatrix m;
326 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000327 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328}
329
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000330void SkMatrix::preScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000331 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000332 return;
reed@google.comf244f902011-09-06 21:02:36 +0000333 }
334
reed@google.com3fb51872011-06-01 15:11:22 +0000335 // the assumption is that these multiplies are very cheap, and that
336 // a full concat and/or just computing the matrix type is more expensive.
337 // Also, the fixed-point case checks for overflow, but the float doesn't,
338 // so we can get away with these blind multiplies.
339
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000340 fMat[kMScaleX] *= sx;
341 fMat[kMSkewY] *= sx;
342 fMat[kMPersp0] *= sx;
reed@google.com3fb51872011-06-01 15:11:22 +0000343
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000344 fMat[kMSkewX] *= sy;
345 fMat[kMScaleY] *= sy;
346 fMat[kMPersp1] *= sy;
reed@google.com3fb51872011-06-01 15:11:22 +0000347
348 this->orTypeMask(kScale_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349}
350
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000351void SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000352 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000353 return;
reed@google.comf244f902011-09-06 21:02:36 +0000354 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 SkMatrix m;
356 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000357 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358}
359
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000360void SkMatrix::postScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000361 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000362 return;
reed@google.comf244f902011-09-06 21:02:36 +0000363 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 SkMatrix m;
365 m.setScale(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000366 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367}
368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369// this guy perhaps can go away, if we have a fract/high-precision way to
370// scale matrices
371bool SkMatrix::postIDiv(int divx, int divy) {
372 if (divx == 0 || divy == 0) {
373 return false;
374 }
375
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 const float invX = 1.f / divx;
377 const float invY = 1.f / divy;
378
379 fMat[kMScaleX] *= invX;
380 fMat[kMSkewX] *= invX;
381 fMat[kMTransX] *= invX;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000382
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 fMat[kMScaleY] *= invY;
384 fMat[kMSkewY] *= invY;
385 fMat[kMTransY] *= invY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386
387 this->setTypeMask(kUnknown_Mask);
388 return true;
389}
390
391////////////////////////////////////////////////////////////////////////////////////
392
393void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV,
394 SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000395 const SkScalar oneMinusCosV = 1 - cosV;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396
397 fMat[kMScaleX] = cosV;
398 fMat[kMSkewX] = -sinV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000399 fMat[kMTransX] = sdot(sinV, py, oneMinusCosV, px);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400
401 fMat[kMSkewY] = sinV;
402 fMat[kMScaleY] = cosV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000403 fMat[kMTransY] = sdot(-sinV, px, oneMinusCosV, py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404
405 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000406 fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000407
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000408 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409}
410
411void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
412 fMat[kMScaleX] = cosV;
413 fMat[kMSkewX] = -sinV;
414 fMat[kMTransX] = 0;
415
416 fMat[kMSkewY] = sinV;
417 fMat[kMScaleY] = cosV;
418 fMat[kMTransY] = 0;
419
420 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000421 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000423 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424}
425
426void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
427 SkScalar sinV, cosV;
428 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
429 this->setSinCos(sinV, cosV, px, py);
430}
431
432void SkMatrix::setRotate(SkScalar degrees) {
433 SkScalar sinV, cosV;
434 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
435 this->setSinCos(sinV, cosV);
436}
437
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000438void SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 SkMatrix m;
440 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000441 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442}
443
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000444void SkMatrix::preRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 SkMatrix m;
446 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000447 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448}
449
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000450void SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 SkMatrix m;
452 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000453 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454}
455
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000456void SkMatrix::postRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 SkMatrix m;
458 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000459 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460}
461
462////////////////////////////////////////////////////////////////////////////////////
463
464void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000465 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 fMat[kMSkewX] = sx;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000467 fMat[kMTransX] = -sx * py;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468
469 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000470 fMat[kMScaleY] = 1;
471 fMat[kMTransY] = -sy * px;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472
473 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000474 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000476 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477}
478
479void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000480 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 fMat[kMSkewX] = sx;
482 fMat[kMTransX] = 0;
483
484 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000485 fMat[kMScaleY] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 fMat[kMTransY] = 0;
487
488 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000489 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000491 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492}
493
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000494void SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 SkMatrix m;
496 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000497 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498}
499
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000500void SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 SkMatrix m;
502 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000503 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504}
505
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000506void SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 SkMatrix m;
508 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000509 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510}
511
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000512void SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 SkMatrix m;
514 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000515 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516}
517
518///////////////////////////////////////////////////////////////////////////////
519
520bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst,
521 ScaleToFit align)
522{
523 if (src.isEmpty()) {
524 this->reset();
525 return false;
526 }
527
528 if (dst.isEmpty()) {
reed@android.com4516f472009-06-29 16:25:36 +0000529 sk_bzero(fMat, 8 * sizeof(SkScalar));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
531 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000532 SkScalar tx, sx = dst.width() / src.width();
533 SkScalar ty, sy = dst.height() / src.height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 bool xLarger = false;
535
536 if (align != kFill_ScaleToFit) {
537 if (sx > sy) {
538 xLarger = true;
539 sx = sy;
540 } else {
541 sy = sx;
542 }
543 }
544
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000545 tx = dst.fLeft - src.fLeft * sx;
546 ty = dst.fTop - src.fTop * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
548 SkScalar diff;
549
550 if (xLarger) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000551 diff = dst.width() - src.width() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000553 diff = dst.height() - src.height() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000555
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 if (align == kCenter_ScaleToFit) {
557 diff = SkScalarHalf(diff);
558 }
559
560 if (xLarger) {
561 tx += diff;
562 } else {
563 ty += diff;
564 }
565 }
566
567 fMat[kMScaleX] = sx;
568 fMat[kMScaleY] = sy;
569 fMat[kMTransX] = tx;
570 fMat[kMTransY] = ty;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000571 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 fMat[kMPersp0] = fMat[kMPersp1] = 0;
573
reed@google.com97cd69c2012-10-12 14:35:48 +0000574 unsigned mask = kRectStaysRect_Mask;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000575 if (sx != 1 || sy != 1) {
reed@google.com97cd69c2012-10-12 14:35:48 +0000576 mask |= kScale_Mask;
577 }
578 if (tx || ty) {
579 mask |= kTranslate_Mask;
580 }
581 this->setTypeMask(mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 }
583 // shared cleanup
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000584 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 return true;
586}
587
588///////////////////////////////////////////////////////////////////////////////
589
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000590static inline float muladdmul(float a, float b, float c, float d) {
591 return SkDoubleToFloat((double)a * b + (double)c * d);
reed@google.com8f4d2302013-12-17 16:44:46 +0000592}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000594static inline float rowcol3(const float row[], const float col[]) {
595 return row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
reed@google.com8f4d2302013-12-17 16:44:46 +0000596}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597
598static void normalize_perspective(SkScalar mat[9]) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000599 if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 for (int i = 0; i < 9; i++)
601 mat[i] = SkScalarHalf(mat[i]);
602 }
603}
604
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000605void SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000606 TypeMask aType = a.getPerspectiveTypeMaskOnly();
607 TypeMask bType = b.getPerspectiveTypeMaskOnly();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000609 if (a.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 *this = b;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000611 } else if (b.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 *this = a;
613 } else {
614 SkMatrix tmp;
615
616 if ((aType | bType) & kPerspective_Mask) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000617 tmp.fMat[kMScaleX] = rowcol3(&a.fMat[0], &b.fMat[0]);
618 tmp.fMat[kMSkewX] = rowcol3(&a.fMat[0], &b.fMat[1]);
619 tmp.fMat[kMTransX] = rowcol3(&a.fMat[0], &b.fMat[2]);
620 tmp.fMat[kMSkewY] = rowcol3(&a.fMat[3], &b.fMat[0]);
621 tmp.fMat[kMScaleY] = rowcol3(&a.fMat[3], &b.fMat[1]);
622 tmp.fMat[kMTransY] = rowcol3(&a.fMat[3], &b.fMat[2]);
623 tmp.fMat[kMPersp0] = rowcol3(&a.fMat[6], &b.fMat[0]);
624 tmp.fMat[kMPersp1] = rowcol3(&a.fMat[6], &b.fMat[1]);
625 tmp.fMat[kMPersp2] = rowcol3(&a.fMat[6], &b.fMat[2]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626
627 normalize_perspective(tmp.fMat);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000628 tmp.setTypeMask(kUnknown_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 } else { // not perspective
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000630 tmp.fMat[kMScaleX] = muladdmul(a.fMat[kMScaleX],
631 b.fMat[kMScaleX],
632 a.fMat[kMSkewX],
633 b.fMat[kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000635 tmp.fMat[kMSkewX] = muladdmul(a.fMat[kMScaleX],
636 b.fMat[kMSkewX],
637 a.fMat[kMSkewX],
638 b.fMat[kMScaleY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000640 tmp.fMat[kMTransX] = muladdmul(a.fMat[kMScaleX],
641 b.fMat[kMTransX],
642 a.fMat[kMSkewX],
643 b.fMat[kMTransY]);
644
645 tmp.fMat[kMTransX] += a.fMat[kMTransX];
646
647 tmp.fMat[kMSkewY] = muladdmul(a.fMat[kMSkewY],
648 b.fMat[kMScaleX],
649 a.fMat[kMScaleY],
650 b.fMat[kMSkewY]);
651
652 tmp.fMat[kMScaleY] = muladdmul(a.fMat[kMSkewY],
653 b.fMat[kMSkewX],
654 a.fMat[kMScaleY],
655 b.fMat[kMScaleY]);
656
657 tmp.fMat[kMTransY] = muladdmul(a.fMat[kMSkewY],
658 b.fMat[kMTransX],
659 a.fMat[kMScaleY],
660 b.fMat[kMTransY]);
661
662 tmp.fMat[kMTransY] += a.fMat[kMTransY];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000664 tmp.fMat[kMPersp2] = 1;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000665 //SkDebugf("Concat mat non-persp type: %d\n", tmp.getType());
666 //SkASSERT(!(tmp.getType() & kPerspective_Mask));
667 tmp.setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 }
669 *this = tmp;
670 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671}
672
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000673void SkMatrix::preConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 // check for identity first, so we don't do a needless copy of ourselves
675 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000676 if(!mat.isIdentity()) {
677 this->setConcat(*this, mat);
678 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679}
680
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000681void SkMatrix::postConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 // check for identity first, so we don't do a needless copy of ourselves
683 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000684 if (!mat.isIdentity()) {
685 this->setConcat(mat, *this);
686 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
689///////////////////////////////////////////////////////////////////////////////
690
reed@android.com0b9e2db2009-09-16 17:00:17 +0000691/* Matrix inversion is very expensive, but also the place where keeping
692 precision may be most important (here and matrix concat). Hence to avoid
693 bitmap blitting artifacts when walking the inverse, we use doubles for
694 the intermediate math, even though we know that is more expensive.
reed@android.com0b9e2db2009-09-16 17:00:17 +0000695 */
696
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000697static inline SkScalar scross_dscale(SkScalar a, SkScalar b,
698 SkScalar c, SkScalar d, double scale) {
699 return SkDoubleToScalar(scross(a, b, c, d) * scale);
700}
701
702static inline double dcross(double a, double b, double c, double d) {
703 return a * b - c * d;
704}
705
706static inline SkScalar dcross_dscale(double a, double b,
707 double c, double d, double scale) {
708 return SkDoubleToScalar(dcross(a, b, c, d) * scale);
709}
710
711static double sk_inv_determinant(const float mat[9], int isPerspective) {
reed@google.com8f4d2302013-12-17 16:44:46 +0000712 double det;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713
reed@google.com8f4d2302013-12-17 16:44:46 +0000714 if (isPerspective) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000715 det = mat[SkMatrix::kMScaleX] *
716 dcross(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2],
717 mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1])
718 +
719 mat[SkMatrix::kMSkewX] *
720 dcross(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0],
721 mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2])
722 +
723 mat[SkMatrix::kMTransX] *
724 dcross(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1],
725 mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]);
reed@google.com8f4d2302013-12-17 16:44:46 +0000726 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000727 det = dcross(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY],
728 mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 }
730
reed@google.com8f4d2302013-12-17 16:44:46 +0000731 // Since the determinant is on the order of the cube of the matrix members,
732 // compare to the cube of the default nearly-zero constant (although an
733 // estimate of the condition number would be better if it wasn't so expensive).
734 if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
735 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 }
reed@google.com8f4d2302013-12-17 16:44:46 +0000737 return 1.0 / det;
738}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000740void SkMatrix::SetAffineIdentity(SkScalar affine[6]) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000741 affine[kAScaleX] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000742 affine[kASkewY] = 0;
743 affine[kASkewX] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000744 affine[kAScaleY] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000745 affine[kATransX] = 0;
746 affine[kATransY] = 0;
747}
748
749bool SkMatrix::asAffine(SkScalar affine[6]) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000750 if (this->hasPerspective()) {
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000751 return false;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000752 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000753 if (affine) {
754 affine[kAScaleX] = this->fMat[kMScaleX];
755 affine[kASkewY] = this->fMat[kMSkewY];
756 affine[kASkewX] = this->fMat[kMSkewX];
757 affine[kAScaleY] = this->fMat[kMScaleY];
758 affine[kATransX] = this->fMat[kMTransX];
759 affine[kATransY] = this->fMat[kMTransY];
760 }
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000761 return true;
762}
763
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000764bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
765 SkASSERT(!this->isIdentity());
reed@google.com2fb96cc2013-01-04 17:02:33 +0000766
767 TypeMask mask = this->getType();
768
769 if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
reed@google.come40591d2013-01-30 15:47:42 +0000770 bool invertible = true;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000771 if (inv) {
772 if (mask & kScale_Mask) {
773 SkScalar invX = fMat[kMScaleX];
774 SkScalar invY = fMat[kMScaleY];
775 if (0 == invX || 0 == invY) {
776 return false;
777 }
778 invX = SkScalarInvert(invX);
779 invY = SkScalarInvert(invY);
780
781 // Must be careful when writing to inv, since it may be the
782 // same memory as this.
783
784 inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
785 inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
786
787 inv->fMat[kMScaleX] = invX;
788 inv->fMat[kMScaleY] = invY;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000789 inv->fMat[kMPersp2] = 1;
790 inv->fMat[kMTransX] = -fMat[kMTransX] * invX;
791 inv->fMat[kMTransY] = -fMat[kMTransY] * invY;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000792
793 inv->setTypeMask(mask | kRectStaysRect_Mask);
794 } else {
795 // translate only
796 inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
797 }
reed@google.come40591d2013-01-30 15:47:42 +0000798 } else { // inv is NULL, just check if we're invertible
799 if (!fMat[kMScaleX] || !fMat[kMScaleY]) {
800 invertible = false;
801 }
reed@google.com2fb96cc2013-01-04 17:02:33 +0000802 }
reed@google.come40591d2013-01-30 15:47:42 +0000803 return invertible;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000804 }
reed@google.com4a1362a2013-01-04 18:52:16 +0000805
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000806 int isPersp = mask & kPerspective_Mask;
807 double scale = sk_inv_determinant(fMat, isPersp);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808
809 if (scale == 0) { // underflow
810 return false;
811 }
812
813 if (inv) {
814 SkMatrix tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000815 if (inv == this) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 inv = &tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000817 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818
819 if (isPersp) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000820 inv->fMat[kMScaleX] = scross_dscale(fMat[kMScaleY], fMat[kMPersp2], fMat[kMTransY], fMat[kMPersp1], scale);
821 inv->fMat[kMSkewX] = scross_dscale(fMat[kMTransX], fMat[kMPersp1], fMat[kMSkewX], fMat[kMPersp2], scale);
822 inv->fMat[kMTransX] = scross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMTransX], fMat[kMScaleY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000824 inv->fMat[kMSkewY] = scross_dscale(fMat[kMTransY], fMat[kMPersp0], fMat[kMSkewY], fMat[kMPersp2], scale);
825 inv->fMat[kMScaleY] = scross_dscale(fMat[kMScaleX], fMat[kMPersp2], fMat[kMTransX], fMat[kMPersp0], scale);
826 inv->fMat[kMTransY] = scross_dscale(fMat[kMTransX], fMat[kMSkewY], fMat[kMScaleX], fMat[kMTransY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000828 inv->fMat[kMPersp0] = scross_dscale(fMat[kMSkewY], fMat[kMPersp1], fMat[kMScaleY], fMat[kMPersp0], scale);
829 inv->fMat[kMPersp1] = scross_dscale(fMat[kMSkewX], fMat[kMPersp0], fMat[kMScaleX], fMat[kMPersp1], scale);
830 inv->fMat[kMPersp2] = scross_dscale(fMat[kMScaleX], fMat[kMScaleY], fMat[kMSkewX], fMat[kMSkewY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 } else { // not perspective
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000832 inv->fMat[kMScaleX] = SkDoubleToScalar(fMat[kMScaleY] * scale);
833 inv->fMat[kMSkewX] = SkDoubleToScalar(-fMat[kMSkewX] * scale);
834 inv->fMat[kMTransX] = dcross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMScaleY], fMat[kMTransX], scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000835
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000836 inv->fMat[kMSkewY] = SkDoubleToScalar(-fMat[kMSkewY] * scale);
837 inv->fMat[kMScaleY] = SkDoubleToScalar(fMat[kMScaleX] * scale);
838 inv->fMat[kMTransY] = dcross_dscale(fMat[kMSkewY], fMat[kMTransX], fMat[kMScaleX], fMat[kMTransY], scale);
reed@google.com83c6a222013-12-18 05:19:22 +0000839
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 inv->fMat[kMPersp0] = 0;
841 inv->fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000842 inv->fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 }
844
junov@chromium.org6fc56992012-07-12 14:01:32 +0000845 inv->setTypeMask(fTypeMask);
846
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 if (inv == &tmp) {
848 *(SkMatrix*)this = tmp;
849 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 }
851 return true;
852}
853
854///////////////////////////////////////////////////////////////////////////////
855
856void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
857 const SkPoint src[], int count) {
858 SkASSERT(m.getType() == 0);
859
860 if (dst != src && count > 0)
861 memcpy(dst, src, count * sizeof(SkPoint));
862}
863
864void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
865 const SkPoint src[], int count) {
866 SkASSERT(m.getType() == kTranslate_Mask);
867
868 if (count > 0) {
869 SkScalar tx = m.fMat[kMTransX];
870 SkScalar ty = m.fMat[kMTransY];
871 do {
872 dst->fY = src->fY + ty;
873 dst->fX = src->fX + tx;
874 src += 1;
875 dst += 1;
876 } while (--count);
877 }
878}
879
880void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
881 const SkPoint src[], int count) {
882 SkASSERT(m.getType() == kScale_Mask);
883
884 if (count > 0) {
885 SkScalar mx = m.fMat[kMScaleX];
886 SkScalar my = m.fMat[kMScaleY];
887 do {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000888 dst->fY = src->fY * my;
889 dst->fX = src->fX * mx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 src += 1;
891 dst += 1;
892 } while (--count);
893 }
894}
895
896void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
897 const SkPoint src[], int count) {
898 SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
899
900 if (count > 0) {
901 SkScalar mx = m.fMat[kMScaleX];
902 SkScalar my = m.fMat[kMScaleY];
903 SkScalar tx = m.fMat[kMTransX];
904 SkScalar ty = m.fMat[kMTransY];
905 do {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000906 dst->fY = src->fY * my + ty;
907 dst->fX = src->fX * mx + tx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 src += 1;
909 dst += 1;
910 } while (--count);
911 }
912}
913
914void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
915 const SkPoint src[], int count) {
916 SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
917
918 if (count > 0) {
919 SkScalar mx = m.fMat[kMScaleX];
920 SkScalar my = m.fMat[kMScaleY];
921 SkScalar kx = m.fMat[kMSkewX];
922 SkScalar ky = m.fMat[kMSkewY];
923 do {
924 SkScalar sy = src->fY;
925 SkScalar sx = src->fX;
926 src += 1;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000927 dst->fY = sdot(sx, ky, sy, my);
928 dst->fX = sdot(sx, mx, sy, kx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 dst += 1;
930 } while (--count);
931 }
932}
933
934void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
935 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000936 SkASSERT(!m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937
938 if (count > 0) {
939 SkScalar mx = m.fMat[kMScaleX];
940 SkScalar my = m.fMat[kMScaleY];
941 SkScalar kx = m.fMat[kMSkewX];
942 SkScalar ky = m.fMat[kMSkewY];
943 SkScalar tx = m.fMat[kMTransX];
944 SkScalar ty = m.fMat[kMTransY];
945 do {
946 SkScalar sy = src->fY;
947 SkScalar sx = src->fX;
948 src += 1;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000949#ifdef SK_LEGACY_MATRIX_MATH_ORDER
950 dst->fY = sx * ky + (sy * my + ty);
951 dst->fX = sx * mx + (sy * kx + tx);
952#else
953 dst->fY = sdot(sx, ky, sy, my) + ty;
954 dst->fX = sdot(sx, mx, sy, kx) + tx;
955#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 dst += 1;
957 } while (--count);
958 }
959}
960
961void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
962 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000963 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965 if (count > 0) {
966 do {
967 SkScalar sy = src->fY;
968 SkScalar sx = src->fX;
969 src += 1;
970
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000971 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
972 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
973#ifdef SK_LEGACY_MATRIX_MATH_ORDER
974 SkScalar z = sx * m.fMat[kMPersp0] + (sy * m.fMat[kMPersp1] + m.fMat[kMPersp2]);
975#else
976 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
977#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 if (z) {
979 z = SkScalarFastInvert(z);
980 }
981
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000982 dst->fY = y * z;
983 dst->fX = x * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 dst += 1;
985 } while (--count);
986 }
987}
988
989const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
990 SkMatrix::Identity_pts, SkMatrix::Trans_pts,
991 SkMatrix::Scale_pts, SkMatrix::ScaleTrans_pts,
992 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
993 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
994 // repeat the persp proc 8 times
995 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
996 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
997 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
998 SkMatrix::Persp_pts, SkMatrix::Persp_pts
999};
1000
1001void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001002 SkASSERT((dst && src && count > 0) || 0 == count);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 // no partial overlap
bungeman@google.com3dc82c42013-10-11 19:11:10 +00001004 SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005
1006 this->getMapPtsProc()(*this, dst, src, count);
1007}
1008
1009///////////////////////////////////////////////////////////////////////////////
1010
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001011void SkMatrix::mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const {
1012 SkASSERT((dst && src && count > 0) || 0 == count);
1013 // no partial overlap
1014 SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= 3*count);
1015
1016 if (count > 0) {
1017 if (this->isIdentity()) {
1018 memcpy(dst, src, 3*count*sizeof(SkScalar));
1019 return;
1020 }
1021 do {
1022 SkScalar sx = src[0];
1023 SkScalar sy = src[1];
1024 SkScalar sw = src[2];
1025 src += 3;
1026
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001027 SkScalar x = sdot(sx, fMat[kMScaleX], sy, fMat[kMSkewX], sw, fMat[kMTransX]);
1028 SkScalar y = sdot(sx, fMat[kMSkewY], sy, fMat[kMScaleY], sw, fMat[kMTransY]);
1029 SkScalar w = sdot(sx, fMat[kMPersp0], sy, fMat[kMPersp1], sw, fMat[kMPersp2]);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001030
1031 dst[0] = x;
1032 dst[1] = y;
1033 dst[2] = w;
1034 dst += 3;
1035 } while (--count);
1036 }
1037}
1038
1039///////////////////////////////////////////////////////////////////////////////
1040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001042 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 SkPoint origin;
1044
1045 MapXYProc proc = this->getMapXYProc();
1046 proc(*this, 0, 0, &origin);
1047
1048 for (int i = count - 1; i >= 0; --i) {
1049 SkPoint tmp;
1050
1051 proc(*this, src[i].fX, src[i].fY, &tmp);
1052 dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
1053 }
1054 } else {
1055 SkMatrix tmp = *this;
1056
1057 tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
1058 tmp.clearTypeMask(kTranslate_Mask);
1059 tmp.mapPoints(dst, src, count);
1060 }
1061}
1062
1063bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
1064 SkASSERT(dst && &src);
1065
1066 if (this->rectStaysRect()) {
1067 this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
1068 dst->sort();
1069 return true;
1070 } else {
1071 SkPoint quad[4];
1072
1073 src.toQuad(quad);
1074 this->mapPoints(quad, quad, 4);
1075 dst->set(quad, 4);
1076 return false;
1077 }
1078}
1079
1080SkScalar SkMatrix::mapRadius(SkScalar radius) const {
1081 SkVector vec[2];
1082
1083 vec[0].set(radius, 0);
1084 vec[1].set(0, radius);
1085 this->mapVectors(vec, 2);
1086
1087 SkScalar d0 = vec[0].length();
1088 SkScalar d1 = vec[1].length();
1089
reed@google.com6e252d42013-12-18 05:06:52 +00001090 // return geometric mean
1091 return SkScalarSqrt(d0 * d1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092}
1093
1094///////////////////////////////////////////////////////////////////////////////
1095
1096void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1097 SkPoint* pt) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001098 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001100 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1101 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1102 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 if (z) {
1104 z = SkScalarFastInvert(z);
1105 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001106 pt->fX = x * z;
1107 pt->fY = y * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108}
1109
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1111 SkPoint* pt) {
1112 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001113
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001114#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1115 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1116 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1117#else
1118 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1119 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1120#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121}
1122
1123void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1124 SkPoint* pt) {
1125 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
1126 SkASSERT(0 == m.fMat[kMTransX]);
1127 SkASSERT(0 == m.fMat[kMTransY]);
1128
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001129#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1130 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1131 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1132#else
1133 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1134 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1135#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136}
1137
1138void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1139 SkPoint* pt) {
1140 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1141 == kScale_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001142
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001143 pt->fX = sx * m.fMat[kMScaleX] + m.fMat[kMTransX];
1144 pt->fY = sy * m.fMat[kMScaleY] + m.fMat[kMTransY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145}
1146
1147void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1148 SkPoint* pt) {
1149 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1150 == kScale_Mask);
1151 SkASSERT(0 == m.fMat[kMTransX]);
1152 SkASSERT(0 == m.fMat[kMTransY]);
1153
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001154 pt->fX = sx * m.fMat[kMScaleX];
1155 pt->fY = sy * m.fMat[kMScaleY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156}
1157
1158void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1159 SkPoint* pt) {
1160 SkASSERT(m.getType() == kTranslate_Mask);
1161
1162 pt->fX = sx + m.fMat[kMTransX];
1163 pt->fY = sy + m.fMat[kMTransY];
1164}
1165
1166void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1167 SkPoint* pt) {
1168 SkASSERT(0 == m.getType());
1169
1170 pt->fX = sx;
1171 pt->fY = sy;
1172}
1173
1174const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
1175 SkMatrix::Identity_xy, SkMatrix::Trans_xy,
1176 SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy,
1177 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1178 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1179 // repeat the persp proc 8 times
1180 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1181 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1182 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1183 SkMatrix::Persp_xy, SkMatrix::Persp_xy
1184};
1185
1186///////////////////////////////////////////////////////////////////////////////
1187
1188// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
reed@google.com83c6a222013-12-18 05:19:22 +00001189#define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26)))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190
1191bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
1192 if (PerspNearlyZero(fMat[kMPersp0])) {
1193 if (stepX || stepY) {
1194 if (PerspNearlyZero(fMat[kMPersp1]) &&
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001195 PerspNearlyZero(fMat[kMPersp2] - 1)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 if (stepX) {
1197 *stepX = SkScalarToFixed(fMat[kMScaleX]);
1198 }
1199 if (stepY) {
1200 *stepY = SkScalarToFixed(fMat[kMSkewY]);
1201 }
1202 } else {
reed@google.com83c6a222013-12-18 05:19:22 +00001203 SkScalar z = y * fMat[kMPersp1] + fMat[kMPersp2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 if (stepX) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001205 *stepX = SkScalarToFixed(fMat[kMScaleX] / z);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 }
1207 if (stepY) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001208 *stepY = SkScalarToFixed(fMat[kMSkewY] / z);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 }
1210 }
1211 }
1212 return true;
1213 }
1214 return false;
1215}
1216
1217///////////////////////////////////////////////////////////////////////////////
1218
1219#include "SkPerspIter.h"
1220
1221SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
1222 : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
1223 SkPoint pt;
1224
1225 SkMatrix::Persp_xy(m, x0, y0, &pt);
1226 fX = SkScalarToFixed(pt.fX);
1227 fY = SkScalarToFixed(pt.fY);
1228}
1229
1230int SkPerspIter::next() {
1231 int n = fCount;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001232
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 if (0 == n) {
1234 return 0;
1235 }
1236 SkPoint pt;
1237 SkFixed x = fX;
1238 SkFixed y = fY;
1239 SkFixed dx, dy;
1240
1241 if (n >= kCount) {
1242 n = kCount;
1243 fSX += SkIntToScalar(kCount);
1244 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1245 fX = SkScalarToFixed(pt.fX);
1246 fY = SkScalarToFixed(pt.fY);
1247 dx = (fX - x) >> kShift;
1248 dy = (fY - y) >> kShift;
1249 } else {
1250 fSX += SkIntToScalar(n);
1251 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1252 fX = SkScalarToFixed(pt.fX);
1253 fY = SkScalarToFixed(pt.fY);
1254 dx = (fX - x) / n;
1255 dy = (fY - y) / n;
1256 }
1257
1258 SkFixed* p = fStorage;
1259 for (int i = 0; i < n; i++) {
1260 *p++ = x; x += dx;
1261 *p++ = y; y += dy;
1262 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001263
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 fCount -= n;
1265 return n;
1266}
1267
1268///////////////////////////////////////////////////////////////////////////////
1269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270static inline bool checkForZero(float x) {
1271 return x*x == 0;
1272}
1273
1274static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1275 float x = 1, y = 1;
1276 SkPoint pt1, pt2;
1277
1278 if (count > 1) {
1279 pt1.fX = poly[1].fX - poly[0].fX;
1280 pt1.fY = poly[1].fY - poly[0].fY;
1281 y = SkPoint::Length(pt1.fX, pt1.fY);
1282 if (checkForZero(y)) {
1283 return false;
1284 }
1285 switch (count) {
1286 case 2:
1287 break;
1288 case 3:
1289 pt2.fX = poly[0].fY - poly[2].fY;
1290 pt2.fY = poly[2].fX - poly[0].fX;
1291 goto CALC_X;
1292 default:
1293 pt2.fX = poly[0].fY - poly[3].fY;
1294 pt2.fY = poly[3].fX - poly[0].fX;
1295 CALC_X:
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001296 x = sdot(pt1.fX, pt2.fX, pt1.fY, pt2.fY) / y;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 break;
1298 }
1299 }
1300 pt->set(x, y);
1301 return true;
1302}
1303
1304bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1305 const SkPoint& scale) {
1306 float invScale = 1 / scale.fY;
1307
1308 dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1309 dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
1310 dst->fMat[kMPersp0] = 0;
1311 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1312 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1313 dst->fMat[kMPersp1] = 0;
1314 dst->fMat[kMTransX] = srcPt[0].fX;
1315 dst->fMat[kMTransY] = srcPt[0].fY;
1316 dst->fMat[kMPersp2] = 1;
1317 dst->setTypeMask(kUnknown_Mask);
1318 return true;
1319}
1320
1321bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1322 const SkPoint& scale) {
1323 float invScale = 1 / scale.fX;
1324 dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
1325 dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
1326 dst->fMat[kMPersp0] = 0;
1327
1328 invScale = 1 / scale.fY;
1329 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1330 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1331 dst->fMat[kMPersp1] = 0;
1332
1333 dst->fMat[kMTransX] = srcPt[0].fX;
1334 dst->fMat[kMTransY] = srcPt[0].fY;
1335 dst->fMat[kMPersp2] = 1;
1336 dst->setTypeMask(kUnknown_Mask);
1337 return true;
1338}
1339
1340bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1341 const SkPoint& scale) {
1342 float a1, a2;
1343 float x0, y0, x1, y1, x2, y2;
1344
1345 x0 = srcPt[2].fX - srcPt[0].fX;
1346 y0 = srcPt[2].fY - srcPt[0].fY;
1347 x1 = srcPt[2].fX - srcPt[1].fX;
1348 y1 = srcPt[2].fY - srcPt[1].fY;
1349 x2 = srcPt[2].fX - srcPt[3].fX;
1350 y2 = srcPt[2].fY - srcPt[3].fY;
1351
1352 /* check if abs(x2) > abs(y2) */
1353 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1354 float denom = SkScalarMulDiv(x1, y2, x2) - y1;
1355 if (checkForZero(denom)) {
1356 return false;
1357 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001358 a1 = (SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359 } else {
1360 float denom = x1 - SkScalarMulDiv(y1, x2, y2);
1361 if (checkForZero(denom)) {
1362 return false;
1363 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001364 a1 = (x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365 }
1366
1367 /* check if abs(x1) > abs(y1) */
1368 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1369 float denom = y2 - SkScalarMulDiv(x2, y1, x1);
1370 if (checkForZero(denom)) {
1371 return false;
1372 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001373 a2 = (y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 } else {
1375 float denom = SkScalarMulDiv(y2, x1, y1) - x2;
1376 if (checkForZero(denom)) {
1377 return false;
1378 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001379 a2 = (SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 }
1381
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001382 float invScale = SkScalarInvert(scale.fX);
1383 dst->fMat[kMScaleX] = (a2 * srcPt[3].fX + srcPt[3].fX - srcPt[0].fX) * invScale;
1384 dst->fMat[kMSkewY] = (a2 * srcPt[3].fY + srcPt[3].fY - srcPt[0].fY) * invScale;
1385 dst->fMat[kMPersp0] = a2 * invScale;
1386
1387 invScale = SkScalarInvert(scale.fY);
1388 dst->fMat[kMSkewX] = (a1 * srcPt[1].fX + srcPt[1].fX - srcPt[0].fX) * invScale;
1389 dst->fMat[kMScaleY] = (a1 * srcPt[1].fY + srcPt[1].fY - srcPt[0].fY) * invScale;
1390 dst->fMat[kMPersp1] = a1 * invScale;
1391
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392 dst->fMat[kMTransX] = srcPt[0].fX;
1393 dst->fMat[kMTransY] = srcPt[0].fY;
1394 dst->fMat[kMPersp2] = 1;
1395 dst->setTypeMask(kUnknown_Mask);
1396 return true;
1397}
1398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
1400
1401/* Taken from Rob Johnson's original sample code in QuickDraw GX
1402*/
1403bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
1404 int count) {
1405 if ((unsigned)count > 4) {
1406 SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
1407 return false;
1408 }
1409
1410 if (0 == count) {
1411 this->reset();
1412 return true;
1413 }
1414 if (1 == count) {
1415 this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
1416 return true;
1417 }
1418
1419 SkPoint scale;
1420 if (!poly_to_point(&scale, src, count) ||
1421 SkScalarNearlyZero(scale.fX) ||
1422 SkScalarNearlyZero(scale.fY)) {
1423 return false;
1424 }
1425
1426 static const PolyMapProc gPolyMapProcs[] = {
1427 SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
1428 };
1429 PolyMapProc proc = gPolyMapProcs[count - 2];
1430
1431 SkMatrix tempMap, result;
1432 tempMap.setTypeMask(kUnknown_Mask);
1433
1434 if (!proc(src, &tempMap, scale)) {
1435 return false;
1436 }
1437 if (!tempMap.invert(&result)) {
1438 return false;
1439 }
1440 if (!proc(dst, &tempMap, scale)) {
1441 return false;
1442 }
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001443 this->setConcat(tempMap, result);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 return true;
1445}
1446
1447///////////////////////////////////////////////////////////////////////////////
1448
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001449enum MinMaxOrBoth {
1450 kMin_MinMaxOrBoth,
1451 kMax_MinMaxOrBoth,
1452 kBoth_MinMaxOrBoth
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001453};
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001454
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001455template <MinMaxOrBoth MIN_MAX_OR_BOTH> bool get_scale_factor(SkMatrix::TypeMask typeMask,
1456 const SkScalar m[9],
1457 SkScalar results[/*1 or 2*/]) {
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001458 if (typeMask & SkMatrix::kPerspective_Mask) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001459 return false;
bsalomon@google.com38396322011-09-09 19:32:04 +00001460 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001461 if (SkMatrix::kIdentity_Mask == typeMask) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001462 results[0] = SK_Scalar1;
1463 if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1464 results[1] = SK_Scalar1;
1465 }
1466 return true;
bsalomon@google.com38396322011-09-09 19:32:04 +00001467 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001468 if (!(typeMask & SkMatrix::kAffine_Mask)) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001469 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1470 results[0] = SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1471 SkScalarAbs(m[SkMatrix::kMScaleY]));
1472 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1473 results[0] = SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1474 SkScalarAbs(m[SkMatrix::kMScaleY]));
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001475 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001476 results[0] = SkScalarAbs(m[SkMatrix::kMScaleX]);
1477 results[1] = SkScalarAbs(m[SkMatrix::kMScaleY]);
1478 if (results[0] > results[1]) {
1479 SkTSwap(results[0], results[1]);
1480 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001481 }
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001482 return true;
bsalomon@google.com38396322011-09-09 19:32:04 +00001483 }
1484 // ignore the translation part of the matrix, just look at 2x2 portion.
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001485 // compute singular values, take largest or smallest abs value.
bsalomon@google.com38396322011-09-09 19:32:04 +00001486 // [a b; b c] = A^T*A
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001487 SkScalar a = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX],
1488 m[SkMatrix::kMSkewY], m[SkMatrix::kMSkewY]);
1489 SkScalar b = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX],
1490 m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]);
1491 SkScalar c = sdot(m[SkMatrix::kMSkewX], m[SkMatrix::kMSkewX],
1492 m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]);
bsalomon@google.com38396322011-09-09 19:32:04 +00001493 // eigenvalues of A^T*A are the squared singular values of A.
1494 // characteristic equation is det((A^T*A) - l*I) = 0
1495 // l^2 - (a + c)l + (ac-b^2)
1496 // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001497 // and roots are guaranteed to be pos and real).
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001498 SkScalar bSqd = b * b;
bsalomon@google.com38396322011-09-09 19:32:04 +00001499 // if upper left 2x2 is orthogonal save some math
jvanverth@google.comc490f802013-03-04 13:56:38 +00001500 if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001501 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1502 results[0] = SkMinScalar(a, c);
1503 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1504 results[0] = SkMaxScalar(a, c);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001505 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001506 results[0] = a;
1507 results[1] = c;
1508 if (results[0] > results[1]) {
1509 SkTSwap(results[0], results[1]);
1510 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001511 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001512 } else {
bsalomon@google.com38396322011-09-09 19:32:04 +00001513 SkScalar aminusc = a - c;
1514 SkScalar apluscdiv2 = SkScalarHalf(a + c);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001515 SkScalar x = SkScalarHalf(SkScalarSqrt(aminusc * aminusc + 4 * bSqd));
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001516 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1517 results[0] = apluscdiv2 - x;
1518 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1519 results[0] = apluscdiv2 + x;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001520 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001521 results[0] = apluscdiv2 - x;
1522 results[1] = apluscdiv2 + x;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001523 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001524 }
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001525 SkASSERT(results[0] >= 0);
1526 results[0] = SkScalarSqrt(results[0]);
1527 if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1528 SkASSERT(results[1] >= 0);
1529 results[1] = SkScalarSqrt(results[1]);
1530 }
1531 return true;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001532}
1533
commit-bot@chromium.org18786512014-05-20 14:53:45 +00001534SkScalar SkMatrix::getMinScale() const {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001535 SkScalar factor;
1536 if (get_scale_factor<kMin_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
1537 return factor;
1538 } else {
1539 return -1;
1540 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001541}
1542
commit-bot@chromium.org18786512014-05-20 14:53:45 +00001543SkScalar SkMatrix::getMaxScale() const {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001544 SkScalar factor;
1545 if (get_scale_factor<kMax_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
1546 return factor;
1547 } else {
1548 return -1;
1549 }
1550}
1551
1552bool SkMatrix::getMinMaxScales(SkScalar scaleFactors[2]) const {
1553 return get_scale_factor<kBoth_MinMaxOrBoth>(this->getType(), fMat, scaleFactors);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001554}
1555
mtklein78358bf2014-06-02 08:44:27 -07001556namespace {
1557
mtkleinada36352014-06-02 10:59:35 -07001558struct PODMatrix {
1559 SkScalar matrix[9];
1560 uint32_t typemask;
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +00001561
mtkleinada36352014-06-02 10:59:35 -07001562 const SkMatrix& asSkMatrix() const { return *reinterpret_cast<const SkMatrix*>(this); }
1563};
1564SK_COMPILE_ASSERT(sizeof(PODMatrix) == sizeof(SkMatrix), PODMatrixSizeMismatch);
mtklein78358bf2014-06-02 08:44:27 -07001565
1566} // namespace
1567
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001568const SkMatrix& SkMatrix::I() {
mtkleinada36352014-06-02 10:59:35 -07001569 SK_COMPILE_ASSERT(offsetof(SkMatrix, fMat) == offsetof(PODMatrix, matrix), BadfMat);
1570 SK_COMPILE_ASSERT(offsetof(SkMatrix, fTypeMask) == offsetof(PODMatrix, typemask), BadfTypeMask);
1571
1572 static const PODMatrix identity = { {SK_Scalar1, 0, 0,
1573 0, SK_Scalar1, 0,
1574 0, 0, SK_Scalar1 },
1575 kIdentity_Mask | kRectStaysRect_Mask};
1576 SkASSERT(identity.asSkMatrix().isIdentity());
1577 return identity.asSkMatrix();
tomhudson@google.com1f902872012-06-01 13:15:47 +00001578}
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001579
1580const SkMatrix& SkMatrix::InvalidMatrix() {
mtkleinada36352014-06-02 10:59:35 -07001581 SK_COMPILE_ASSERT(offsetof(SkMatrix, fMat) == offsetof(PODMatrix, matrix), BadfMat);
1582 SK_COMPILE_ASSERT(offsetof(SkMatrix, fTypeMask) == offsetof(PODMatrix, typemask), BadfTypeMask);
1583
1584 static const PODMatrix invalid =
1585 { {SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1586 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1587 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax },
1588 kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask };
1589 return invalid.asSkMatrix();
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001590}
1591
1592///////////////////////////////////////////////////////////////////////////////
1593
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001594size_t SkMatrix::writeToMemory(void* buffer) const {
reed@android.com0ad336f2009-06-29 16:02:20 +00001595 // TODO write less for simple matrices
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001596 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
reed@android.com0ad336f2009-06-29 16:02:20 +00001597 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001598 memcpy(buffer, fMat, sizeInMemory);
reed@android.com0ad336f2009-06-29 16:02:20 +00001599 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001600 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001601}
1602
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001603size_t SkMatrix::readFromMemory(const void* buffer, size_t length) {
1604 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
1605 if (length < sizeInMemory) {
1606 return 0;
1607 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001608 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001609 memcpy(fMat, buffer, sizeInMemory);
reed@android.comf2b98d62010-12-20 18:26:13 +00001610 this->setTypeMask(kUnknown_Mask);
1611 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001612 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001613}
1614
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001615#ifdef SK_DEVELOPER
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616void SkMatrix::dump() const {
1617 SkString str;
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001618 this->toString(&str);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 SkDebugf("%s\n", str.c_str());
1620}
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001621#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001623#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001624void SkMatrix::toString(SkString* str) const {
1625 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 +00001626 fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
1627 fMat[6], fMat[7], fMat[8]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628}
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001629#endif
reed@google.comad514302013-01-02 20:19:45 +00001630
1631///////////////////////////////////////////////////////////////////////////////
1632
1633#include "SkMatrixUtils.h"
1634
reed@google.comae573582013-01-03 15:22:40 +00001635bool SkTreatAsSprite(const SkMatrix& mat, int width, int height,
reed@google.comad514302013-01-02 20:19:45 +00001636 unsigned subpixelBits) {
reed@google.comae573582013-01-03 15:22:40 +00001637 // quick reject on affine or perspective
reed@google.comad514302013-01-02 20:19:45 +00001638 if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
1639 return false;
1640 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001641
reed@google.comad514302013-01-02 20:19:45 +00001642 // quick success check
1643 if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
1644 return true;
1645 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001646
reed@google.comad514302013-01-02 20:19:45 +00001647 // mapRect supports negative scales, so we eliminate those first
1648 if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
1649 return false;
1650 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001651
reed@google.comad514302013-01-02 20:19:45 +00001652 SkRect dst;
reed@google.comae573582013-01-03 15:22:40 +00001653 SkIRect isrc = { 0, 0, width, height };
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00001654
reed@google.comad514302013-01-02 20:19:45 +00001655 {
reed@google.comae573582013-01-03 15:22:40 +00001656 SkRect src;
1657 src.set(isrc);
1658 mat.mapRect(&dst, src);
reed@google.comad514302013-01-02 20:19:45 +00001659 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001660
reed@google.comae573582013-01-03 15:22:40 +00001661 // just apply the translate to isrc
1662 isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
1663 SkScalarRoundToInt(mat.getTranslateY()));
1664
reed@google.comad514302013-01-02 20:19:45 +00001665 if (subpixelBits) {
1666 isrc.fLeft <<= subpixelBits;
1667 isrc.fTop <<= subpixelBits;
1668 isrc.fRight <<= subpixelBits;
1669 isrc.fBottom <<= subpixelBits;
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001670
reed@google.comad514302013-01-02 20:19:45 +00001671 const float scale = 1 << subpixelBits;
1672 dst.fLeft *= scale;
1673 dst.fTop *= scale;
1674 dst.fRight *= scale;
1675 dst.fBottom *= scale;
1676 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001677
reed@google.comae573582013-01-03 15:22:40 +00001678 SkIRect idst;
reed@google.comad514302013-01-02 20:19:45 +00001679 dst.round(&idst);
1680 return isrc == idst;
1681}
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001682
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001683// A square matrix M can be decomposed (via polar decomposition) into two matrices --
1684// an orthogonal matrix Q and a symmetric matrix S. In turn we can decompose S into U*W*U^T,
1685// where U is another orthogonal matrix and W is a scale matrix. These can be recombined
1686// to give M = (Q*U)*W*U^T, i.e., the product of two orthogonal matrices and a scale matrix.
1687//
1688// The one wrinkle is that traditionally Q may contain a reflection -- the
1689// calculation has been rejiggered to put that reflection into W.
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001690bool SkDecomposeUpper2x2(const SkMatrix& matrix,
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001691 SkPoint* rotation1,
1692 SkPoint* scale,
1693 SkPoint* rotation2) {
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001694
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001695 SkScalar A = matrix[SkMatrix::kMScaleX];
1696 SkScalar B = matrix[SkMatrix::kMSkewX];
1697 SkScalar C = matrix[SkMatrix::kMSkewY];
1698 SkScalar D = matrix[SkMatrix::kMScaleY];
1699
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +00001700 if (is_degenerate_2x2(A, B, C, D)) {
1701 return false;
1702 }
1703
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001704 double w1, w2;
1705 SkScalar cos1, sin1;
1706 SkScalar cos2, sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001707
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001708 // do polar decomposition (M = Q*S)
1709 SkScalar cosQ, sinQ;
1710 double Sa, Sb, Sd;
1711 // if M is already symmetric (i.e., M = I*S)
1712 if (SkScalarNearlyEqual(B, C)) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001713 cosQ = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001714 sinQ = 0;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001715
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001716 Sa = A;
1717 Sb = B;
1718 Sd = D;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001719 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001720 cosQ = A + D;
1721 sinQ = C - B;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001722 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cosQ*cosQ + sinQ*sinQ));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001723 cosQ *= reciplen;
1724 sinQ *= reciplen;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001725
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001726 // S = Q^-1*M
1727 // we don't calc Sc since it's symmetric
1728 Sa = A*cosQ + C*sinQ;
1729 Sb = B*cosQ + D*sinQ;
1730 Sd = -B*sinQ + D*cosQ;
1731 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +00001732
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001733 // Now we need to compute eigenvalues of S (our scale factors)
1734 // and eigenvectors (bases for our rotation)
1735 // From this, should be able to reconstruct S as U*W*U^T
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001736 if (SkScalarNearlyZero(SkDoubleToScalar(Sb))) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001737 // already diagonalized
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001738 cos1 = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001739 sin1 = 0;
1740 w1 = Sa;
1741 w2 = Sd;
1742 cos2 = cosQ;
1743 sin2 = sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001744 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001745 double diff = Sa - Sd;
1746 double discriminant = sqrt(diff*diff + 4.0*Sb*Sb);
1747 double trace = Sa + Sd;
1748 if (diff > 0) {
1749 w1 = 0.5*(trace + discriminant);
1750 w2 = 0.5*(trace - discriminant);
1751 } else {
1752 w1 = 0.5*(trace - discriminant);
1753 w2 = 0.5*(trace + discriminant);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001754 }
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001755
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001756 cos1 = SkDoubleToScalar(Sb); sin1 = SkDoubleToScalar(w1 - Sa);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001757 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cos1*cos1 + sin1*sin1));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001758 cos1 *= reciplen;
1759 sin1 *= reciplen;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001760
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001761 // rotation 2 is composition of Q and U
1762 cos2 = cos1*cosQ - sin1*sinQ;
1763 sin2 = sin1*cosQ + cos1*sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001764
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001765 // rotation 1 is U^T
1766 sin1 = -sin1;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001767 }
1768
bsalomon49f085d2014-09-05 13:34:00 -07001769 if (scale) {
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001770 scale->fX = SkDoubleToScalar(w1);
1771 scale->fY = SkDoubleToScalar(w2);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001772 }
bsalomon49f085d2014-09-05 13:34:00 -07001773 if (rotation1) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001774 rotation1->fX = cos1;
1775 rotation1->fY = sin1;
1776 }
bsalomon49f085d2014-09-05 13:34:00 -07001777 if (rotation2) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001778 rotation2->fX = cos2;
1779 rotation2->fY = sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001780 }
1781
1782 return true;
1783}