blob: a43e00f3af562630723e9456a90af12c25750a6c [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
reed451e8222014-12-13 08:46:48 -080014static void normalize_perspective(SkScalar mat[9]) {
15 // If it was interesting to never store the last element, we could divide all 8 other
16 // elements here by the 9th, making it 1.0...
17 //
18 // When SkScalar was SkFixed, we would sometimes rescale the entire matrix to keep its
19 // component values from getting too large. This is not a concern when using floats/doubles,
20 // so we do nothing now.
21
22 // Disable this for now, but it could be enabled.
23#if 0
24 if (0 == mat[SkMatrix::kMPersp0] && 0 == mat[SkMatrix::kMPersp1]) {
25 SkScalar p2 = mat[SkMatrix::kMPersp2];
26 if (p2 != 0 && p2 != 1) {
27 double inv = 1.0 / p2;
28 for (int i = 0; i < 6; ++i) {
29 mat[i] = SkDoubleToScalar(mat[i] * inv);
30 }
31 mat[SkMatrix::kMPersp2] = 1;
32 }
33 }
34#endif
35}
36
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000037// In a few places, we performed the following
38// a * b + c * d + e
39// as
40// a * b + (c * d + e)
41//
42// sdot and scross are indended to capture these compound operations into a
43// function, with an eye toward considering upscaling the intermediates to
44// doubles for more precision (as we do in concat and invert).
45//
46// However, these few lines that performed the last add before the "dot", cause
47// tiny image differences, so we guard that change until we see the impact on
48// chrome's layouttests.
49//
50#define SK_LEGACY_MATRIX_MATH_ORDER
reed@android.comab7ac022009-09-18 13:38:43 +000051
reed@google.com8f4d2302013-12-17 16:44:46 +000052static inline float SkDoubleToFloat(double x) {
53 return static_cast<float>(x);
54}
reed@android.com8a1c16f2008-12-17 15:59:43 +000055
56/* [scale-x skew-x trans-x] [X] [X']
57 [skew-y scale-y trans-y] * [Y] = [Y']
58 [persp-0 persp-1 persp-2] [1] [1 ]
59*/
60
61void SkMatrix::reset() {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000062 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000063 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 fMat[kMTransX] = fMat[kMTransY] =
65 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
67}
68
reed451e8222014-12-13 08:46:48 -080069void SkMatrix::set9(const SkScalar buffer[]) {
70 memcpy(fMat, buffer, 9 * sizeof(SkScalar));
71 normalize_perspective(fMat);
72 this->setTypeMask(kUnknown_Mask);
73}
74
75void SkMatrix::setAffine(const SkScalar buffer[]) {
76 fMat[kMScaleX] = buffer[kAScaleX];
77 fMat[kMSkewX] = buffer[kASkewX];
78 fMat[kMTransX] = buffer[kATransX];
79 fMat[kMSkewY] = buffer[kASkewY];
80 fMat[kMScaleY] = buffer[kAScaleY];
81 fMat[kMTransY] = buffer[kATransY];
82 fMat[kMPersp0] = 0;
83 fMat[kMPersp1] = 0;
84 fMat[kMPersp2] = 1;
85 this->setTypeMask(kUnknown_Mask);
86}
87
reed@android.com8a1c16f2008-12-17 15:59:43 +000088// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
89enum {
90 kTranslate_Shift,
91 kScale_Shift,
92 kAffine_Shift,
93 kPerspective_Shift,
94 kRectStaysRect_Shift
95};
96
reed@google.com8f4d2302013-12-17 16:44:46 +000097static const int32_t kScalar1Int = 0x3f800000;
reed@android.com8a1c16f2008-12-17 15:59:43 +000098
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000099uint8_t SkMatrix::computePerspectiveTypeMask() const {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000100 // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
101 // is a win, but replacing those below is not. We don't yet understand
102 // that result.
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000103 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000104 // If this is a perspective transform, we return true for all other
bsalomon@google.com19263b12012-07-13 13:36:38 +0000105 // transform flags - this does not disable any optimizations, respects
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000106 // the rule that the type mask must be conservative, and speeds up
junov@chromium.org6fc56992012-07-12 14:01:32 +0000107 // type mask computation.
108 return SkToU8(kORableMasks);
109 }
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000110
junov@chromium.org6fc56992012-07-12 14:01:32 +0000111 return SkToU8(kOnlyPerspectiveValid_Mask | kUnknown_Mask);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000112}
113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114uint8_t SkMatrix::computeTypeMask() const {
115 unsigned mask = 0;
116
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000117 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000118 // Once it is determined that that this is a perspective transform,
119 // all other flags are moot as far as optimizations are concerned.
120 return SkToU8(kORableMasks);
tomhudson@google.comac385252011-06-06 15:18:28 +0000121 }
122
123 if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
124 mask |= kTranslate_Mask;
125 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126
127 int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
128 int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
129 int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
130 int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
tomhudson@google.comac385252011-06-06 15:18:28 +0000131
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 if (m01 | m10) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000133 // The skew components may be scale-inducing, unless we are dealing
134 // with a pure rotation. Testing for a pure rotation is expensive,
135 // so we opt for being conservative by always setting the scale bit.
136 // along with affine.
137 // By doing this, we are also ensuring that matrices have the same
138 // type masks as their inverses.
139 mask |= kAffine_Mask | kScale_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
junov@chromium.org6fc56992012-07-12 14:01:32 +0000141 // For rectStaysRect, in the affine case, we only need check that
142 // the primary diagonal is all zeros and that the secondary diagonal
143 // is all non-zero.
tomhudson@google.comac385252011-06-06 15:18:28 +0000144
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 // map non-zero to 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 m01 = m01 != 0;
147 m10 = m10 != 0;
tomhudson@google.comac385252011-06-06 15:18:28 +0000148
junov@chromium.org6fc56992012-07-12 14:01:32 +0000149 int dp0 = 0 == (m00 | m11) ; // true if both are 0
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 int ds1 = m01 & m10; // true if both are 1
tomhudson@google.comac385252011-06-06 15:18:28 +0000151
junov@chromium.org6fc56992012-07-12 14:01:32 +0000152 mask |= (dp0 & ds1) << kRectStaysRect_Shift;
153 } else {
154 // Only test for scale explicitly if not affine, since affine sets the
155 // scale bit.
156 if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
157 mask |= kScale_Mask;
158 }
159
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000160 // Not affine, therefore we already know secondary diagonal is
junov@chromium.org6fc56992012-07-12 14:01:32 +0000161 // all zeros, so we just need to check that primary diagonal is
162 // all non-zero.
163
164 // map non-zero to 1
165 m00 = m00 != 0;
166 m11 = m11 != 0;
167
168 // record if the (p)rimary diagonal is all non-zero
169 mask |= (m00 & m11) << kRectStaysRect_Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 }
171
172 return SkToU8(mask);
173}
174
175///////////////////////////////////////////////////////////////////////////////
176
reed@google.com3fb51872011-06-01 15:11:22 +0000177bool operator==(const SkMatrix& a, const SkMatrix& b) {
178 const SkScalar* SK_RESTRICT ma = a.fMat;
179 const SkScalar* SK_RESTRICT mb = b.fMat;
180
181 return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
182 ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
183 ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
184}
185
reed@google.com3fb51872011-06-01 15:11:22 +0000186///////////////////////////////////////////////////////////////////////////////
187
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000188// helper function to determine if upper-left 2x2 of matrix is degenerate
skia.committer@gmail.com16d53aa2013-07-30 07:01:00 +0000189static inline bool is_degenerate_2x2(SkScalar scaleX, SkScalar skewX,
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000190 SkScalar skewY, SkScalar scaleY) {
191 SkScalar perp_dot = scaleX*scaleY - skewX*skewY;
192 return SkScalarNearlyZero(perp_dot, SK_ScalarNearlyZero*SK_ScalarNearlyZero);
193}
194
195///////////////////////////////////////////////////////////////////////////////
196
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000197bool SkMatrix::isSimilarity(SkScalar tol) const {
198 // if identity or translate matrix
199 TypeMask mask = this->getType();
200 if (mask <= kTranslate_Mask) {
201 return true;
202 }
203 if (mask & kPerspective_Mask) {
204 return false;
205 }
206
207 SkScalar mx = fMat[kMScaleX];
208 SkScalar my = fMat[kMScaleY];
209 // if no skew, can just compare scale factors
210 if (!(mask & kAffine_Mask)) {
211 return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
212 }
213 SkScalar sx = fMat[kMSkewX];
214 SkScalar sy = fMat[kMSkewY];
215
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000216 if (is_degenerate_2x2(mx, sx, sy, my)) {
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000217 return false;
218 }
219
jvanverth17a845f2014-09-02 13:15:40 -0700220 // upper 2x2 is rotation/reflection + uniform scale if basis vectors
221 // are 90 degree rotations of each other
222 return (SkScalarNearlyEqual(mx, my, tol) && SkScalarNearlyEqual(sx, -sy, tol))
223 || (SkScalarNearlyEqual(mx, -my, tol) && SkScalarNearlyEqual(sx, sy, tol));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000224}
225
226bool SkMatrix::preservesRightAngles(SkScalar tol) const {
227 TypeMask mask = this->getType();
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000228
jvanverth17a845f2014-09-02 13:15:40 -0700229 if (mask <= kTranslate_Mask) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000230 // identity, translate and/or scale
231 return true;
232 }
233 if (mask & kPerspective_Mask) {
234 return false;
235 }
236
jvanverth17a845f2014-09-02 13:15:40 -0700237 SkASSERT(mask & (kAffine_Mask | kScale_Mask));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000238
239 SkScalar mx = fMat[kMScaleX];
240 SkScalar my = fMat[kMScaleY];
241 SkScalar sx = fMat[kMSkewX];
242 SkScalar sy = fMat[kMSkewY];
243
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000244 if (is_degenerate_2x2(mx, sx, sy, my)) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000245 return false;
246 }
247
jvanverth17a845f2014-09-02 13:15:40 -0700248 // upper 2x2 is scale + rotation/reflection if basis vectors are orthogonal
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000249 SkVector vec[2];
jvanverth17a845f2014-09-02 13:15:40 -0700250 vec[0].set(mx, sy);
251 vec[1].set(sx, my);
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000252
jvanverth17a845f2014-09-02 13:15:40 -0700253 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000254}
255
256///////////////////////////////////////////////////////////////////////////////
257
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000258static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
259 return a * b + c * d;
260}
261
262static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
263 SkScalar e, SkScalar f) {
264 return a * b + c * d + e * f;
265}
266
267static inline SkScalar scross(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
268 return a * b - c * d;
269}
270
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
reed@google.comc0784db2013-12-13 21:16:12 +0000272 if (dx || dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 fMat[kMTransX] = dx;
274 fMat[kMTransY] = dy;
275
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000276 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000277 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279
280 this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
281 } else {
282 this->reset();
283 }
284}
285
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000286void SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
287 if (!dx && !dy) {
288 return;
289 }
290
tomhudson@google.com8d430182011-06-06 19:11:19 +0000291 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 SkMatrix m;
293 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000294 this->preConcat(m);
295 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000296 fMat[kMTransX] += sdot(fMat[kMScaleX], dx, fMat[kMSkewX], dy);
297 fMat[kMTransY] += sdot(fMat[kMSkewY], dx, fMat[kMScaleY], dy);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000298 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300}
301
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000302void SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
303 if (!dx && !dy) {
304 return;
305 }
306
tomhudson@google.com8d430182011-06-06 19:11:19 +0000307 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 SkMatrix m;
309 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000310 this->postConcat(m);
311 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 fMat[kMTransX] += dx;
313 fMat[kMTransY] += dy;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000314 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316}
317
318///////////////////////////////////////////////////////////////////////////////
319
320void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000321 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000322 this->reset();
323 } else {
reedd1f0ebd2014-10-02 12:58:43 -0700324 this->setScaleTranslate(sx, sy, px - sx * px, py - sy * py);
reed@google.comf244f902011-09-06 21:02:36 +0000325 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326}
327
328void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000329 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000330 this->reset();
331 } else {
332 fMat[kMScaleX] = sx;
333 fMat[kMScaleY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000334 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335
reed@google.comf244f902011-09-06 21:02:36 +0000336 fMat[kMTransX] = fMat[kMTransY] =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000337 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000338 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339
reed@google.comf244f902011-09-06 21:02:36 +0000340 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
341 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342}
343
bsalomon@google.com5c638652011-07-18 19:31:59 +0000344bool SkMatrix::setIDiv(int divx, int divy) {
345 if (!divx || !divy) {
346 return false;
347 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000348 this->setScale(SkScalarInvert(divx), SkScalarInvert(divy));
bsalomon@google.com5c638652011-07-18 19:31:59 +0000349 return true;
350}
351
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000352void SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
353 if (1 == sx && 1 == sy) {
354 return;
355 }
356
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 SkMatrix m;
358 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000359 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360}
361
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000362void SkMatrix::preScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000363 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000364 return;
reed@google.comf244f902011-09-06 21:02:36 +0000365 }
366
reed@google.com3fb51872011-06-01 15:11:22 +0000367 // the assumption is that these multiplies are very cheap, and that
368 // a full concat and/or just computing the matrix type is more expensive.
369 // Also, the fixed-point case checks for overflow, but the float doesn't,
370 // so we can get away with these blind multiplies.
371
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000372 fMat[kMScaleX] *= sx;
373 fMat[kMSkewY] *= sx;
374 fMat[kMPersp0] *= sx;
reed@google.com3fb51872011-06-01 15:11:22 +0000375
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000376 fMat[kMSkewX] *= sy;
377 fMat[kMScaleY] *= sy;
378 fMat[kMPersp1] *= sy;
reed@google.com3fb51872011-06-01 15:11:22 +0000379
380 this->orTypeMask(kScale_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381}
382
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000383void SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000384 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000385 return;
reed@google.comf244f902011-09-06 21:02:36 +0000386 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 SkMatrix m;
388 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000389 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390}
391
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000392void SkMatrix::postScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000393 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000394 return;
reed@google.comf244f902011-09-06 21:02:36 +0000395 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 SkMatrix m;
397 m.setScale(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000398 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399}
400
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401// this guy perhaps can go away, if we have a fract/high-precision way to
402// scale matrices
403bool SkMatrix::postIDiv(int divx, int divy) {
404 if (divx == 0 || divy == 0) {
405 return false;
406 }
407
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 const float invX = 1.f / divx;
409 const float invY = 1.f / divy;
410
411 fMat[kMScaleX] *= invX;
412 fMat[kMSkewX] *= invX;
413 fMat[kMTransX] *= invX;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000414
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 fMat[kMScaleY] *= invY;
416 fMat[kMSkewY] *= invY;
417 fMat[kMTransY] *= invY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418
419 this->setTypeMask(kUnknown_Mask);
420 return true;
421}
422
423////////////////////////////////////////////////////////////////////////////////////
424
reedd1f0ebd2014-10-02 12:58:43 -0700425void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000426 const SkScalar oneMinusCosV = 1 - cosV;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427
428 fMat[kMScaleX] = cosV;
429 fMat[kMSkewX] = -sinV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000430 fMat[kMTransX] = sdot(sinV, py, oneMinusCosV, px);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431
432 fMat[kMSkewY] = sinV;
433 fMat[kMScaleY] = cosV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000434 fMat[kMTransY] = sdot(-sinV, px, oneMinusCosV, py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435
436 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000437 fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000438
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000439 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440}
441
442void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
443 fMat[kMScaleX] = cosV;
444 fMat[kMSkewX] = -sinV;
445 fMat[kMTransX] = 0;
446
447 fMat[kMSkewY] = sinV;
448 fMat[kMScaleY] = cosV;
449 fMat[kMTransY] = 0;
450
451 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000452 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000454 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455}
456
457void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
458 SkScalar sinV, cosV;
459 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
460 this->setSinCos(sinV, cosV, px, py);
461}
462
463void SkMatrix::setRotate(SkScalar degrees) {
464 SkScalar sinV, cosV;
465 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
466 this->setSinCos(sinV, cosV);
467}
468
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000469void SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470 SkMatrix m;
471 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000472 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473}
474
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000475void SkMatrix::preRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 SkMatrix m;
477 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000478 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479}
480
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000481void SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 SkMatrix m;
483 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000484 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485}
486
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000487void SkMatrix::postRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 SkMatrix m;
489 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000490 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491}
492
493////////////////////////////////////////////////////////////////////////////////////
494
495void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000496 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 fMat[kMSkewX] = sx;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000498 fMat[kMTransX] = -sx * py;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000501 fMat[kMScaleY] = 1;
502 fMat[kMTransY] = -sy * px;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503
504 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000505 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000507 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508}
509
510void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000511 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 fMat[kMSkewX] = sx;
513 fMat[kMTransX] = 0;
514
515 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000516 fMat[kMScaleY] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 fMat[kMTransY] = 0;
518
519 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000520 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000522 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000525void SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 SkMatrix m;
527 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000528 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529}
530
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000531void SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 SkMatrix m;
533 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000534 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535}
536
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000537void SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 SkMatrix m;
539 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000540 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541}
542
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000543void SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 SkMatrix m;
545 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000546 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547}
548
549///////////////////////////////////////////////////////////////////////////////
550
reedd1f0ebd2014-10-02 12:58:43 -0700551bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 if (src.isEmpty()) {
553 this->reset();
554 return false;
555 }
556
557 if (dst.isEmpty()) {
reed@android.com4516f472009-06-29 16:25:36 +0000558 sk_bzero(fMat, 8 * sizeof(SkScalar));
reedd1f0ebd2014-10-02 12:58:43 -0700559 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
561 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000562 SkScalar tx, sx = dst.width() / src.width();
563 SkScalar ty, sy = dst.height() / src.height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 bool xLarger = false;
565
566 if (align != kFill_ScaleToFit) {
567 if (sx > sy) {
568 xLarger = true;
569 sx = sy;
570 } else {
571 sy = sx;
572 }
573 }
574
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000575 tx = dst.fLeft - src.fLeft * sx;
576 ty = dst.fTop - src.fTop * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
578 SkScalar diff;
579
580 if (xLarger) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000581 diff = dst.width() - src.width() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000583 diff = dst.height() - src.height() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 if (align == kCenter_ScaleToFit) {
587 diff = SkScalarHalf(diff);
588 }
589
590 if (xLarger) {
591 tx += diff;
592 } else {
593 ty += diff;
594 }
595 }
596
reedd1f0ebd2014-10-02 12:58:43 -0700597 this->setScaleTranslate(sx, sy, tx, ty);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 return true;
600}
601
602///////////////////////////////////////////////////////////////////////////////
603
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000604static inline float muladdmul(float a, float b, float c, float d) {
605 return SkDoubleToFloat((double)a * b + (double)c * d);
reed@google.com8f4d2302013-12-17 16:44:46 +0000606}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000608static inline float rowcol3(const float row[], const float col[]) {
609 return row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
reed@google.com8f4d2302013-12-17 16:44:46 +0000610}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
reedd1f0ebd2014-10-02 12:58:43 -0700612static bool only_scale_and_translate(unsigned mask) {
613 return 0 == (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask));
614}
615
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000616void SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
reedd1f0ebd2014-10-02 12:58:43 -0700617 TypeMask aType = a.getType();
618 TypeMask bType = b.getType();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000620 if (a.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 *this = b;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000622 } else if (b.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 *this = a;
reedd1f0ebd2014-10-02 12:58:43 -0700624 } else if (only_scale_and_translate(aType | bType)) {
625 this->setScaleTranslate(a.fMat[kMScaleX] * b.fMat[kMScaleX],
626 a.fMat[kMScaleY] * b.fMat[kMScaleY],
627 a.fMat[kMScaleX] * b.fMat[kMTransX] + a.fMat[kMTransX],
628 a.fMat[kMScaleY] * b.fMat[kMTransY] + a.fMat[kMTransY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 } else {
630 SkMatrix tmp;
631
632 if ((aType | bType) & kPerspective_Mask) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000633 tmp.fMat[kMScaleX] = rowcol3(&a.fMat[0], &b.fMat[0]);
634 tmp.fMat[kMSkewX] = rowcol3(&a.fMat[0], &b.fMat[1]);
635 tmp.fMat[kMTransX] = rowcol3(&a.fMat[0], &b.fMat[2]);
636 tmp.fMat[kMSkewY] = rowcol3(&a.fMat[3], &b.fMat[0]);
637 tmp.fMat[kMScaleY] = rowcol3(&a.fMat[3], &b.fMat[1]);
638 tmp.fMat[kMTransY] = rowcol3(&a.fMat[3], &b.fMat[2]);
639 tmp.fMat[kMPersp0] = rowcol3(&a.fMat[6], &b.fMat[0]);
640 tmp.fMat[kMPersp1] = rowcol3(&a.fMat[6], &b.fMat[1]);
641 tmp.fMat[kMPersp2] = rowcol3(&a.fMat[6], &b.fMat[2]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642
643 normalize_perspective(tmp.fMat);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000644 tmp.setTypeMask(kUnknown_Mask);
reedd1f0ebd2014-10-02 12:58:43 -0700645 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000646 tmp.fMat[kMScaleX] = muladdmul(a.fMat[kMScaleX],
647 b.fMat[kMScaleX],
648 a.fMat[kMSkewX],
649 b.fMat[kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000651 tmp.fMat[kMSkewX] = muladdmul(a.fMat[kMScaleX],
652 b.fMat[kMSkewX],
653 a.fMat[kMSkewX],
654 b.fMat[kMScaleY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000656 tmp.fMat[kMTransX] = muladdmul(a.fMat[kMScaleX],
657 b.fMat[kMTransX],
658 a.fMat[kMSkewX],
reedd1f0ebd2014-10-02 12:58:43 -0700659 b.fMat[kMTransY]) + a.fMat[kMTransX];
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000660
661 tmp.fMat[kMSkewY] = muladdmul(a.fMat[kMSkewY],
662 b.fMat[kMScaleX],
663 a.fMat[kMScaleY],
664 b.fMat[kMSkewY]);
665
666 tmp.fMat[kMScaleY] = muladdmul(a.fMat[kMSkewY],
667 b.fMat[kMSkewX],
668 a.fMat[kMScaleY],
669 b.fMat[kMScaleY]);
670
671 tmp.fMat[kMTransY] = muladdmul(a.fMat[kMSkewY],
672 b.fMat[kMTransX],
673 a.fMat[kMScaleY],
reedd1f0ebd2014-10-02 12:58:43 -0700674 b.fMat[kMTransY]) + a.fMat[kMTransY];
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000675
reedd1f0ebd2014-10-02 12:58:43 -0700676 tmp.fMat[kMPersp0] = 0;
677 tmp.fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000678 tmp.fMat[kMPersp2] = 1;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000679 //SkDebugf("Concat mat non-persp type: %d\n", tmp.getType());
680 //SkASSERT(!(tmp.getType() & kPerspective_Mask));
681 tmp.setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 }
683 *this = tmp;
684 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685}
686
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000687void SkMatrix::preConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000688 // check for identity first, so we don't do a needless copy of ourselves
689 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000690 if(!mat.isIdentity()) {
691 this->setConcat(*this, mat);
692 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693}
694
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000695void SkMatrix::postConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 // check for identity first, so we don't do a needless copy of ourselves
697 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000698 if (!mat.isIdentity()) {
699 this->setConcat(mat, *this);
700 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701}
702
703///////////////////////////////////////////////////////////////////////////////
704
reed@android.com0b9e2db2009-09-16 17:00:17 +0000705/* Matrix inversion is very expensive, but also the place where keeping
706 precision may be most important (here and matrix concat). Hence to avoid
707 bitmap blitting artifacts when walking the inverse, we use doubles for
708 the intermediate math, even though we know that is more expensive.
reed@android.com0b9e2db2009-09-16 17:00:17 +0000709 */
710
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000711static inline SkScalar scross_dscale(SkScalar a, SkScalar b,
712 SkScalar c, SkScalar d, double scale) {
713 return SkDoubleToScalar(scross(a, b, c, d) * scale);
714}
715
716static inline double dcross(double a, double b, double c, double d) {
717 return a * b - c * d;
718}
719
720static inline SkScalar dcross_dscale(double a, double b,
721 double c, double d, double scale) {
722 return SkDoubleToScalar(dcross(a, b, c, d) * scale);
723}
724
725static double sk_inv_determinant(const float mat[9], int isPerspective) {
reed@google.com8f4d2302013-12-17 16:44:46 +0000726 double det;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727
reed@google.com8f4d2302013-12-17 16:44:46 +0000728 if (isPerspective) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000729 det = mat[SkMatrix::kMScaleX] *
730 dcross(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2],
731 mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1])
732 +
733 mat[SkMatrix::kMSkewX] *
734 dcross(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0],
735 mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2])
736 +
737 mat[SkMatrix::kMTransX] *
738 dcross(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1],
739 mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]);
reed@google.com8f4d2302013-12-17 16:44:46 +0000740 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000741 det = dcross(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY],
742 mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 }
744
reed@google.com8f4d2302013-12-17 16:44:46 +0000745 // Since the determinant is on the order of the cube of the matrix members,
746 // compare to the cube of the default nearly-zero constant (although an
747 // estimate of the condition number would be better if it wasn't so expensive).
748 if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
749 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 }
reed@google.com8f4d2302013-12-17 16:44:46 +0000751 return 1.0 / det;
752}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000754void SkMatrix::SetAffineIdentity(SkScalar affine[6]) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000755 affine[kAScaleX] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000756 affine[kASkewY] = 0;
757 affine[kASkewX] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000758 affine[kAScaleY] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000759 affine[kATransX] = 0;
760 affine[kATransY] = 0;
761}
762
763bool SkMatrix::asAffine(SkScalar affine[6]) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000764 if (this->hasPerspective()) {
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000765 return false;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000766 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000767 if (affine) {
768 affine[kAScaleX] = this->fMat[kMScaleX];
769 affine[kASkewY] = this->fMat[kMSkewY];
770 affine[kASkewX] = this->fMat[kMSkewX];
771 affine[kAScaleY] = this->fMat[kMScaleY];
772 affine[kATransX] = this->fMat[kMTransX];
773 affine[kATransY] = this->fMat[kMTransY];
774 }
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000775 return true;
776}
777
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000778bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
779 SkASSERT(!this->isIdentity());
reed@google.com2fb96cc2013-01-04 17:02:33 +0000780
781 TypeMask mask = this->getType();
782
783 if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
reed@google.come40591d2013-01-30 15:47:42 +0000784 bool invertible = true;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000785 if (inv) {
786 if (mask & kScale_Mask) {
787 SkScalar invX = fMat[kMScaleX];
788 SkScalar invY = fMat[kMScaleY];
789 if (0 == invX || 0 == invY) {
790 return false;
791 }
792 invX = SkScalarInvert(invX);
793 invY = SkScalarInvert(invY);
794
795 // Must be careful when writing to inv, since it may be the
796 // same memory as this.
797
798 inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
799 inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
800
801 inv->fMat[kMScaleX] = invX;
802 inv->fMat[kMScaleY] = invY;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000803 inv->fMat[kMPersp2] = 1;
804 inv->fMat[kMTransX] = -fMat[kMTransX] * invX;
805 inv->fMat[kMTransY] = -fMat[kMTransY] * invY;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000806
807 inv->setTypeMask(mask | kRectStaysRect_Mask);
808 } else {
809 // translate only
810 inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
811 }
reed@google.come40591d2013-01-30 15:47:42 +0000812 } else { // inv is NULL, just check if we're invertible
813 if (!fMat[kMScaleX] || !fMat[kMScaleY]) {
814 invertible = false;
815 }
reed@google.com2fb96cc2013-01-04 17:02:33 +0000816 }
reed@google.come40591d2013-01-30 15:47:42 +0000817 return invertible;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000818 }
reed@google.com4a1362a2013-01-04 18:52:16 +0000819
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000820 int isPersp = mask & kPerspective_Mask;
821 double scale = sk_inv_determinant(fMat, isPersp);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822
823 if (scale == 0) { // underflow
824 return false;
825 }
826
827 if (inv) {
828 SkMatrix tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000829 if (inv == this) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 inv = &tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000831 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832
833 if (isPersp) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000834 inv->fMat[kMScaleX] = scross_dscale(fMat[kMScaleY], fMat[kMPersp2], fMat[kMTransY], fMat[kMPersp1], scale);
835 inv->fMat[kMSkewX] = scross_dscale(fMat[kMTransX], fMat[kMPersp1], fMat[kMSkewX], fMat[kMPersp2], scale);
836 inv->fMat[kMTransX] = scross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMTransX], fMat[kMScaleY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000838 inv->fMat[kMSkewY] = scross_dscale(fMat[kMTransY], fMat[kMPersp0], fMat[kMSkewY], fMat[kMPersp2], scale);
839 inv->fMat[kMScaleY] = scross_dscale(fMat[kMScaleX], fMat[kMPersp2], fMat[kMTransX], fMat[kMPersp0], scale);
840 inv->fMat[kMTransY] = scross_dscale(fMat[kMTransX], fMat[kMSkewY], fMat[kMScaleX], fMat[kMTransY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000842 inv->fMat[kMPersp0] = scross_dscale(fMat[kMSkewY], fMat[kMPersp1], fMat[kMScaleY], fMat[kMPersp0], scale);
843 inv->fMat[kMPersp1] = scross_dscale(fMat[kMSkewX], fMat[kMPersp0], fMat[kMScaleX], fMat[kMPersp1], scale);
844 inv->fMat[kMPersp2] = scross_dscale(fMat[kMScaleX], fMat[kMScaleY], fMat[kMSkewX], fMat[kMSkewY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 } else { // not perspective
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000846 inv->fMat[kMScaleX] = SkDoubleToScalar(fMat[kMScaleY] * scale);
847 inv->fMat[kMSkewX] = SkDoubleToScalar(-fMat[kMSkewX] * scale);
848 inv->fMat[kMTransX] = dcross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMScaleY], fMat[kMTransX], scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000849
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000850 inv->fMat[kMSkewY] = SkDoubleToScalar(-fMat[kMSkewY] * scale);
851 inv->fMat[kMScaleY] = SkDoubleToScalar(fMat[kMScaleX] * scale);
852 inv->fMat[kMTransY] = dcross_dscale(fMat[kMSkewY], fMat[kMTransX], fMat[kMScaleX], fMat[kMTransY], scale);
reed@google.com83c6a222013-12-18 05:19:22 +0000853
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 inv->fMat[kMPersp0] = 0;
855 inv->fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000856 inv->fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 }
858
junov@chromium.org6fc56992012-07-12 14:01:32 +0000859 inv->setTypeMask(fTypeMask);
860
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 if (inv == &tmp) {
862 *(SkMatrix*)this = tmp;
863 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 }
865 return true;
866}
867
868///////////////////////////////////////////////////////////////////////////////
869
870void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
871 const SkPoint src[], int count) {
872 SkASSERT(m.getType() == 0);
873
874 if (dst != src && count > 0)
875 memcpy(dst, src, count * sizeof(SkPoint));
876}
877
878void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
879 const SkPoint src[], int count) {
880 SkASSERT(m.getType() == kTranslate_Mask);
881
882 if (count > 0) {
883 SkScalar tx = m.fMat[kMTransX];
884 SkScalar ty = m.fMat[kMTransY];
885 do {
886 dst->fY = src->fY + ty;
887 dst->fX = src->fX + tx;
888 src += 1;
889 dst += 1;
890 } while (--count);
891 }
892}
893
894void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
895 const SkPoint src[], int count) {
896 SkASSERT(m.getType() == kScale_Mask);
897
898 if (count > 0) {
899 SkScalar mx = m.fMat[kMScaleX];
900 SkScalar my = m.fMat[kMScaleY];
901 do {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000902 dst->fY = src->fY * my;
903 dst->fX = src->fX * mx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 src += 1;
905 dst += 1;
906 } while (--count);
907 }
908}
909
910void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
911 const SkPoint src[], int count) {
912 SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
913
914 if (count > 0) {
915 SkScalar mx = m.fMat[kMScaleX];
916 SkScalar my = m.fMat[kMScaleY];
917 SkScalar tx = m.fMat[kMTransX];
918 SkScalar ty = m.fMat[kMTransY];
919 do {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000920 dst->fY = src->fY * my + ty;
921 dst->fX = src->fX * mx + tx;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 src += 1;
923 dst += 1;
924 } while (--count);
925 }
926}
927
928void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
929 const SkPoint src[], int count) {
930 SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
931
932 if (count > 0) {
933 SkScalar mx = m.fMat[kMScaleX];
934 SkScalar my = m.fMat[kMScaleY];
935 SkScalar kx = m.fMat[kMSkewX];
936 SkScalar ky = m.fMat[kMSkewY];
937 do {
938 SkScalar sy = src->fY;
939 SkScalar sx = src->fX;
940 src += 1;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000941 dst->fY = sdot(sx, ky, sy, my);
942 dst->fX = sdot(sx, mx, sy, kx);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 dst += 1;
944 } while (--count);
945 }
946}
947
948void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
949 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000950 SkASSERT(!m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951
952 if (count > 0) {
953 SkScalar mx = m.fMat[kMScaleX];
954 SkScalar my = m.fMat[kMScaleY];
955 SkScalar kx = m.fMat[kMSkewX];
956 SkScalar ky = m.fMat[kMSkewY];
957 SkScalar tx = m.fMat[kMTransX];
958 SkScalar ty = m.fMat[kMTransY];
959 do {
960 SkScalar sy = src->fY;
961 SkScalar sx = src->fX;
962 src += 1;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000963#ifdef SK_LEGACY_MATRIX_MATH_ORDER
964 dst->fY = sx * ky + (sy * my + ty);
965 dst->fX = sx * mx + (sy * kx + tx);
966#else
967 dst->fY = sdot(sx, ky, sy, my) + ty;
968 dst->fX = sdot(sx, mx, sy, kx) + tx;
969#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 dst += 1;
971 } while (--count);
972 }
973}
974
975void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
976 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000977 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 if (count > 0) {
980 do {
981 SkScalar sy = src->fY;
982 SkScalar sx = src->fX;
983 src += 1;
984
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000985 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
986 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
987#ifdef SK_LEGACY_MATRIX_MATH_ORDER
988 SkScalar z = sx * m.fMat[kMPersp0] + (sy * m.fMat[kMPersp1] + m.fMat[kMPersp2]);
989#else
990 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
991#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 if (z) {
993 z = SkScalarFastInvert(z);
994 }
995
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000996 dst->fY = y * z;
997 dst->fX = x * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 dst += 1;
999 } while (--count);
1000 }
1001}
1002
1003const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
1004 SkMatrix::Identity_pts, SkMatrix::Trans_pts,
1005 SkMatrix::Scale_pts, SkMatrix::ScaleTrans_pts,
1006 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
1007 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
1008 // repeat the persp proc 8 times
1009 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1010 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1011 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1012 SkMatrix::Persp_pts, SkMatrix::Persp_pts
1013};
1014
1015void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001016 SkASSERT((dst && src && count > 0) || 0 == count);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 // no partial overlap
bungeman@google.com3dc82c42013-10-11 19:11:10 +00001018 SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019
1020 this->getMapPtsProc()(*this, dst, src, count);
1021}
1022
reed80106322015-03-23 08:15:14 -07001023#include "Sk4x.h"
1024
reedd8b34c22015-03-27 14:00:41 -07001025void SkMatrix::Trans_vpts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
1026 SkASSERT(m.getType() <= kTranslate_Mask);
reed80106322015-03-23 08:15:14 -07001027
reedd8b34c22015-03-27 14:00:41 -07001028 if (count > 0) {
1029 SkScalar tx = m.getTranslateX();
1030 SkScalar ty = m.getTranslateY();
reed80106322015-03-23 08:15:14 -07001031 if (count & 1) {
1032 dst->fX = src->fX + tx;
1033 dst->fY = src->fY + ty;
1034 src += 1;
1035 dst += 1;
1036 }
1037 Sk4f trans4(tx, ty, tx, ty);
1038 count >>= 1;
reedd8b34c22015-03-27 14:00:41 -07001039 if (count & 1) {
reed80106322015-03-23 08:15:14 -07001040 (Sk4f::Load(&src->fX) + trans4).store(&dst->fX);
1041 src += 2;
1042 dst += 2;
1043 }
reedd8b34c22015-03-27 14:00:41 -07001044 count >>= 1;
1045 for (int i = 0; i < count; ++i) {
1046 (Sk4f::Load(&src[0].fX) + trans4).store(&dst[0].fX);
1047 (Sk4f::Load(&src[2].fX) + trans4).store(&dst[2].fX);
1048 src += 4;
1049 dst += 4;
1050 }
reed80106322015-03-23 08:15:14 -07001051 }
reedd8b34c22015-03-27 14:00:41 -07001052}
1053
1054void SkMatrix::Scale_vpts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
1055 SkASSERT(m.getType() <= (kScale_Mask | kTranslate_Mask));
1056
1057 if (count > 0) {
1058 SkScalar tx = m.getTranslateX();
1059 SkScalar ty = m.getTranslateY();
1060 SkScalar sx = m.getScaleX();
1061 SkScalar sy = m.getScaleY();
reed80106322015-03-23 08:15:14 -07001062 if (count & 1) {
1063 dst->fX = src->fX * sx + tx;
1064 dst->fY = src->fY * sy + ty;
1065 src += 1;
1066 dst += 1;
1067 }
1068 Sk4f trans4(tx, ty, tx, ty);
1069 Sk4f scale4(sx, sy, sx, sy);
1070 count >>= 1;
reedd8b34c22015-03-27 14:00:41 -07001071 if (count & 1) {
reed80106322015-03-23 08:15:14 -07001072 (Sk4f::Load(&src->fX) * scale4 + trans4).store(&dst->fX);
1073 src += 2;
1074 dst += 2;
1075 }
reedd8b34c22015-03-27 14:00:41 -07001076 count >>= 1;
1077 for (int i = 0; i < count; ++i) {
1078 (Sk4f::Load(&src[0].fX) * scale4 + trans4).store(&dst[0].fX);
1079 (Sk4f::Load(&src[2].fX) * scale4 + trans4).store(&dst[2].fX);
1080 src += 4;
1081 dst += 4;
1082 }
reed80106322015-03-23 08:15:14 -07001083 }
reedd8b34c22015-03-27 14:00:41 -07001084}
1085
1086void SkMatrix::Affine_vpts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
1087 SkASSERT(m.getType() != kPerspective_Mask);
1088
1089 if (count > 0) {
1090 SkScalar tx = m.getTranslateX();
1091 SkScalar ty = m.getTranslateY();
1092 SkScalar sx = m.getScaleX();
1093 SkScalar sy = m.getScaleY();
1094 SkScalar kx = m.getSkewX();
1095 SkScalar ky = m.getSkewY();
reed80106322015-03-23 08:15:14 -07001096 if (count & 1) {
1097 dst->set(src->fX * sx + src->fY * kx + tx,
1098 src->fX * ky + src->fY * sy + ty);
1099 src += 1;
1100 dst += 1;
1101 }
1102 Sk4f trans4(tx, ty, tx, ty);
1103 Sk4f scale4(sx, sy, sx, sy);
1104 Sk4f skew4(kx, ky, kx, ky); // applied to swizzle of src4
1105 count >>= 1;
1106 for (int i = 0; i < count; ++i) {
1107 Sk4f src4 = Sk4f::Load(&src->fX);
1108 Sk4f swz4(src[0].fY, src[0].fX, src[1].fY, src[1].fX); // need ABCD -> BADC
1109 (src4 * scale4 + swz4 * skew4 + trans4).store(&dst->fX);
1110 src += 2;
1111 dst += 2;
1112 }
reed80106322015-03-23 08:15:14 -07001113 }
reed80106322015-03-23 08:15:14 -07001114}
1115
reedd8b34c22015-03-27 14:00:41 -07001116const SkMatrix::MapPtsProc SkMatrix::gMapVPtsProcs[] = {
1117 SkMatrix::Identity_pts, SkMatrix::Trans_vpts,
1118 SkMatrix::Scale_vpts, SkMatrix::Scale_vpts,
1119 SkMatrix::Affine_vpts, SkMatrix::Affine_vpts,
1120 SkMatrix::Affine_vpts, SkMatrix::Affine_vpts,
1121 // repeat the persp proc 8 times
1122 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1123 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1124 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1125 SkMatrix::Persp_pts, SkMatrix::Persp_pts
1126};
1127
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128///////////////////////////////////////////////////////////////////////////////
1129
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001130void SkMatrix::mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const {
1131 SkASSERT((dst && src && count > 0) || 0 == count);
1132 // no partial overlap
qiankun.miao08996962015-01-12 07:50:25 -08001133 SkASSERT(src == dst || &dst[3*count] <= &src[0] || &src[3*count] <= &dst[0]);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001134
1135 if (count > 0) {
1136 if (this->isIdentity()) {
1137 memcpy(dst, src, 3*count*sizeof(SkScalar));
1138 return;
1139 }
1140 do {
1141 SkScalar sx = src[0];
1142 SkScalar sy = src[1];
1143 SkScalar sw = src[2];
1144 src += 3;
1145
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001146 SkScalar x = sdot(sx, fMat[kMScaleX], sy, fMat[kMSkewX], sw, fMat[kMTransX]);
1147 SkScalar y = sdot(sx, fMat[kMSkewY], sy, fMat[kMScaleY], sw, fMat[kMTransY]);
1148 SkScalar w = sdot(sx, fMat[kMPersp0], sy, fMat[kMPersp1], sw, fMat[kMPersp2]);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001149
1150 dst[0] = x;
1151 dst[1] = y;
1152 dst[2] = w;
1153 dst += 3;
1154 } while (--count);
1155 }
1156}
1157
1158///////////////////////////////////////////////////////////////////////////////
1159
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001161 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 SkPoint origin;
1163
1164 MapXYProc proc = this->getMapXYProc();
1165 proc(*this, 0, 0, &origin);
1166
1167 for (int i = count - 1; i >= 0; --i) {
1168 SkPoint tmp;
1169
1170 proc(*this, src[i].fX, src[i].fY, &tmp);
1171 dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
1172 }
1173 } else {
1174 SkMatrix tmp = *this;
1175
1176 tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
1177 tmp.clearTypeMask(kTranslate_Mask);
1178 tmp.mapPoints(dst, src, count);
1179 }
1180}
1181
1182bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
caryclarka10742c2014-09-18 11:00:40 -07001183 SkASSERT(dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184
1185 if (this->rectStaysRect()) {
1186 this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
1187 dst->sort();
1188 return true;
1189 } else {
1190 SkPoint quad[4];
1191
1192 src.toQuad(quad);
1193 this->mapPoints(quad, quad, 4);
1194 dst->set(quad, 4);
1195 return false;
1196 }
1197}
1198
1199SkScalar SkMatrix::mapRadius(SkScalar radius) const {
1200 SkVector vec[2];
1201
1202 vec[0].set(radius, 0);
1203 vec[1].set(0, radius);
1204 this->mapVectors(vec, 2);
1205
1206 SkScalar d0 = vec[0].length();
1207 SkScalar d1 = vec[1].length();
1208
reed@google.com6e252d42013-12-18 05:06:52 +00001209 // return geometric mean
1210 return SkScalarSqrt(d0 * d1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211}
1212
1213///////////////////////////////////////////////////////////////////////////////
1214
1215void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1216 SkPoint* pt) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001217 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001219 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1220 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1221 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222 if (z) {
1223 z = SkScalarFastInvert(z);
1224 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001225 pt->fX = x * z;
1226 pt->fY = y * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227}
1228
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1230 SkPoint* pt) {
1231 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001232
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001233#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1234 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1235 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1236#else
1237 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1238 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1239#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240}
1241
1242void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1243 SkPoint* pt) {
1244 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
1245 SkASSERT(0 == m.fMat[kMTransX]);
1246 SkASSERT(0 == m.fMat[kMTransY]);
1247
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001248#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1249 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1250 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1251#else
1252 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1253 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1254#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255}
1256
1257void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1258 SkPoint* pt) {
1259 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1260 == kScale_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001261
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001262 pt->fX = sx * m.fMat[kMScaleX] + m.fMat[kMTransX];
1263 pt->fY = sy * m.fMat[kMScaleY] + m.fMat[kMTransY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264}
1265
1266void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1267 SkPoint* pt) {
1268 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1269 == kScale_Mask);
1270 SkASSERT(0 == m.fMat[kMTransX]);
1271 SkASSERT(0 == m.fMat[kMTransY]);
1272
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001273 pt->fX = sx * m.fMat[kMScaleX];
1274 pt->fY = sy * m.fMat[kMScaleY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275}
1276
1277void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1278 SkPoint* pt) {
1279 SkASSERT(m.getType() == kTranslate_Mask);
1280
1281 pt->fX = sx + m.fMat[kMTransX];
1282 pt->fY = sy + m.fMat[kMTransY];
1283}
1284
1285void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1286 SkPoint* pt) {
1287 SkASSERT(0 == m.getType());
1288
1289 pt->fX = sx;
1290 pt->fY = sy;
1291}
1292
1293const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
1294 SkMatrix::Identity_xy, SkMatrix::Trans_xy,
1295 SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy,
1296 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1297 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1298 // repeat the persp proc 8 times
1299 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1300 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1301 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1302 SkMatrix::Persp_xy, SkMatrix::Persp_xy
1303};
1304
1305///////////////////////////////////////////////////////////////////////////////
1306
1307// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
reed@google.com83c6a222013-12-18 05:19:22 +00001308#define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26)))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309
1310bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
1311 if (PerspNearlyZero(fMat[kMPersp0])) {
1312 if (stepX || stepY) {
1313 if (PerspNearlyZero(fMat[kMPersp1]) &&
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001314 PerspNearlyZero(fMat[kMPersp2] - 1)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 if (stepX) {
1316 *stepX = SkScalarToFixed(fMat[kMScaleX]);
1317 }
1318 if (stepY) {
1319 *stepY = SkScalarToFixed(fMat[kMSkewY]);
1320 }
1321 } else {
reed@google.com83c6a222013-12-18 05:19:22 +00001322 SkScalar z = y * fMat[kMPersp1] + fMat[kMPersp2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 if (stepX) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001324 *stepX = SkScalarToFixed(fMat[kMScaleX] / z);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 }
1326 if (stepY) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001327 *stepY = SkScalarToFixed(fMat[kMSkewY] / z);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 }
1329 }
1330 }
1331 return true;
1332 }
1333 return false;
1334}
1335
1336///////////////////////////////////////////////////////////////////////////////
1337
1338#include "SkPerspIter.h"
1339
1340SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
1341 : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
1342 SkPoint pt;
1343
1344 SkMatrix::Persp_xy(m, x0, y0, &pt);
1345 fX = SkScalarToFixed(pt.fX);
1346 fY = SkScalarToFixed(pt.fY);
1347}
1348
1349int SkPerspIter::next() {
1350 int n = fCount;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001351
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 if (0 == n) {
1353 return 0;
1354 }
1355 SkPoint pt;
1356 SkFixed x = fX;
1357 SkFixed y = fY;
1358 SkFixed dx, dy;
1359
1360 if (n >= kCount) {
1361 n = kCount;
1362 fSX += SkIntToScalar(kCount);
1363 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1364 fX = SkScalarToFixed(pt.fX);
1365 fY = SkScalarToFixed(pt.fY);
1366 dx = (fX - x) >> kShift;
1367 dy = (fY - y) >> kShift;
1368 } else {
1369 fSX += SkIntToScalar(n);
1370 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1371 fX = SkScalarToFixed(pt.fX);
1372 fY = SkScalarToFixed(pt.fY);
1373 dx = (fX - x) / n;
1374 dy = (fY - y) / n;
1375 }
1376
1377 SkFixed* p = fStorage;
1378 for (int i = 0; i < n; i++) {
1379 *p++ = x; x += dx;
1380 *p++ = y; y += dy;
1381 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001382
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 fCount -= n;
1384 return n;
1385}
1386
1387///////////////////////////////////////////////////////////////////////////////
1388
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389static inline bool checkForZero(float x) {
1390 return x*x == 0;
1391}
1392
1393static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1394 float x = 1, y = 1;
1395 SkPoint pt1, pt2;
1396
1397 if (count > 1) {
1398 pt1.fX = poly[1].fX - poly[0].fX;
1399 pt1.fY = poly[1].fY - poly[0].fY;
1400 y = SkPoint::Length(pt1.fX, pt1.fY);
1401 if (checkForZero(y)) {
1402 return false;
1403 }
1404 switch (count) {
1405 case 2:
1406 break;
1407 case 3:
1408 pt2.fX = poly[0].fY - poly[2].fY;
1409 pt2.fY = poly[2].fX - poly[0].fX;
1410 goto CALC_X;
1411 default:
1412 pt2.fX = poly[0].fY - poly[3].fY;
1413 pt2.fY = poly[3].fX - poly[0].fX;
1414 CALC_X:
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001415 x = sdot(pt1.fX, pt2.fX, pt1.fY, pt2.fY) / y;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 break;
1417 }
1418 }
1419 pt->set(x, y);
1420 return true;
1421}
1422
1423bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1424 const SkPoint& scale) {
1425 float invScale = 1 / scale.fY;
1426
1427 dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1428 dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
1429 dst->fMat[kMPersp0] = 0;
1430 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1431 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1432 dst->fMat[kMPersp1] = 0;
1433 dst->fMat[kMTransX] = srcPt[0].fX;
1434 dst->fMat[kMTransY] = srcPt[0].fY;
1435 dst->fMat[kMPersp2] = 1;
1436 dst->setTypeMask(kUnknown_Mask);
1437 return true;
1438}
1439
1440bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1441 const SkPoint& scale) {
1442 float invScale = 1 / scale.fX;
1443 dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
1444 dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
1445 dst->fMat[kMPersp0] = 0;
1446
1447 invScale = 1 / scale.fY;
1448 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1449 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1450 dst->fMat[kMPersp1] = 0;
1451
1452 dst->fMat[kMTransX] = srcPt[0].fX;
1453 dst->fMat[kMTransY] = srcPt[0].fY;
1454 dst->fMat[kMPersp2] = 1;
1455 dst->setTypeMask(kUnknown_Mask);
1456 return true;
1457}
1458
1459bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1460 const SkPoint& scale) {
1461 float a1, a2;
1462 float x0, y0, x1, y1, x2, y2;
1463
1464 x0 = srcPt[2].fX - srcPt[0].fX;
1465 y0 = srcPt[2].fY - srcPt[0].fY;
1466 x1 = srcPt[2].fX - srcPt[1].fX;
1467 y1 = srcPt[2].fY - srcPt[1].fY;
1468 x2 = srcPt[2].fX - srcPt[3].fX;
1469 y2 = srcPt[2].fY - srcPt[3].fY;
1470
1471 /* check if abs(x2) > abs(y2) */
1472 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1473 float denom = SkScalarMulDiv(x1, y2, x2) - y1;
1474 if (checkForZero(denom)) {
1475 return false;
1476 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001477 a1 = (SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 } else {
1479 float denom = x1 - SkScalarMulDiv(y1, x2, y2);
1480 if (checkForZero(denom)) {
1481 return false;
1482 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001483 a1 = (x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 }
1485
1486 /* check if abs(x1) > abs(y1) */
1487 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1488 float denom = y2 - SkScalarMulDiv(x2, y1, x1);
1489 if (checkForZero(denom)) {
1490 return false;
1491 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001492 a2 = (y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 } else {
1494 float denom = SkScalarMulDiv(y2, x1, y1) - x2;
1495 if (checkForZero(denom)) {
1496 return false;
1497 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001498 a2 = (SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 }
1500
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001501 float invScale = SkScalarInvert(scale.fX);
1502 dst->fMat[kMScaleX] = (a2 * srcPt[3].fX + srcPt[3].fX - srcPt[0].fX) * invScale;
1503 dst->fMat[kMSkewY] = (a2 * srcPt[3].fY + srcPt[3].fY - srcPt[0].fY) * invScale;
1504 dst->fMat[kMPersp0] = a2 * invScale;
1505
1506 invScale = SkScalarInvert(scale.fY);
1507 dst->fMat[kMSkewX] = (a1 * srcPt[1].fX + srcPt[1].fX - srcPt[0].fX) * invScale;
1508 dst->fMat[kMScaleY] = (a1 * srcPt[1].fY + srcPt[1].fY - srcPt[0].fY) * invScale;
1509 dst->fMat[kMPersp1] = a1 * invScale;
1510
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 dst->fMat[kMTransX] = srcPt[0].fX;
1512 dst->fMat[kMTransY] = srcPt[0].fY;
1513 dst->fMat[kMPersp2] = 1;
1514 dst->setTypeMask(kUnknown_Mask);
1515 return true;
1516}
1517
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
1519
1520/* Taken from Rob Johnson's original sample code in QuickDraw GX
1521*/
1522bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
1523 int count) {
1524 if ((unsigned)count > 4) {
1525 SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
1526 return false;
1527 }
1528
1529 if (0 == count) {
1530 this->reset();
1531 return true;
1532 }
1533 if (1 == count) {
1534 this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
1535 return true;
1536 }
1537
1538 SkPoint scale;
1539 if (!poly_to_point(&scale, src, count) ||
1540 SkScalarNearlyZero(scale.fX) ||
1541 SkScalarNearlyZero(scale.fY)) {
1542 return false;
1543 }
1544
1545 static const PolyMapProc gPolyMapProcs[] = {
1546 SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
1547 };
1548 PolyMapProc proc = gPolyMapProcs[count - 2];
1549
1550 SkMatrix tempMap, result;
1551 tempMap.setTypeMask(kUnknown_Mask);
1552
1553 if (!proc(src, &tempMap, scale)) {
1554 return false;
1555 }
1556 if (!tempMap.invert(&result)) {
1557 return false;
1558 }
1559 if (!proc(dst, &tempMap, scale)) {
1560 return false;
1561 }
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001562 this->setConcat(tempMap, result);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563 return true;
1564}
1565
1566///////////////////////////////////////////////////////////////////////////////
1567
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001568enum MinMaxOrBoth {
1569 kMin_MinMaxOrBoth,
1570 kMax_MinMaxOrBoth,
1571 kBoth_MinMaxOrBoth
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001572};
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001573
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001574template <MinMaxOrBoth MIN_MAX_OR_BOTH> bool get_scale_factor(SkMatrix::TypeMask typeMask,
1575 const SkScalar m[9],
1576 SkScalar results[/*1 or 2*/]) {
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001577 if (typeMask & SkMatrix::kPerspective_Mask) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001578 return false;
bsalomon@google.com38396322011-09-09 19:32:04 +00001579 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001580 if (SkMatrix::kIdentity_Mask == typeMask) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001581 results[0] = SK_Scalar1;
1582 if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1583 results[1] = SK_Scalar1;
1584 }
1585 return true;
bsalomon@google.com38396322011-09-09 19:32:04 +00001586 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001587 if (!(typeMask & SkMatrix::kAffine_Mask)) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001588 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1589 results[0] = SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1590 SkScalarAbs(m[SkMatrix::kMScaleY]));
1591 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1592 results[0] = SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1593 SkScalarAbs(m[SkMatrix::kMScaleY]));
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001594 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001595 results[0] = SkScalarAbs(m[SkMatrix::kMScaleX]);
1596 results[1] = SkScalarAbs(m[SkMatrix::kMScaleY]);
1597 if (results[0] > results[1]) {
1598 SkTSwap(results[0], results[1]);
1599 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001600 }
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001601 return true;
bsalomon@google.com38396322011-09-09 19:32:04 +00001602 }
1603 // ignore the translation part of the matrix, just look at 2x2 portion.
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001604 // compute singular values, take largest or smallest abs value.
bsalomon@google.com38396322011-09-09 19:32:04 +00001605 // [a b; b c] = A^T*A
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001606 SkScalar a = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX],
1607 m[SkMatrix::kMSkewY], m[SkMatrix::kMSkewY]);
1608 SkScalar b = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX],
1609 m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]);
1610 SkScalar c = sdot(m[SkMatrix::kMSkewX], m[SkMatrix::kMSkewX],
1611 m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]);
bsalomon@google.com38396322011-09-09 19:32:04 +00001612 // eigenvalues of A^T*A are the squared singular values of A.
1613 // characteristic equation is det((A^T*A) - l*I) = 0
1614 // l^2 - (a + c)l + (ac-b^2)
1615 // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001616 // and roots are guaranteed to be pos and real).
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001617 SkScalar bSqd = b * b;
bsalomon@google.com38396322011-09-09 19:32:04 +00001618 // if upper left 2x2 is orthogonal save some math
jvanverth@google.comc490f802013-03-04 13:56:38 +00001619 if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001620 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1621 results[0] = SkMinScalar(a, c);
1622 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1623 results[0] = SkMaxScalar(a, c);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001624 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001625 results[0] = a;
1626 results[1] = c;
1627 if (results[0] > results[1]) {
1628 SkTSwap(results[0], results[1]);
1629 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001630 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001631 } else {
bsalomon@google.com38396322011-09-09 19:32:04 +00001632 SkScalar aminusc = a - c;
1633 SkScalar apluscdiv2 = SkScalarHalf(a + c);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001634 SkScalar x = SkScalarHalf(SkScalarSqrt(aminusc * aminusc + 4 * bSqd));
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001635 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1636 results[0] = apluscdiv2 - x;
1637 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1638 results[0] = apluscdiv2 + x;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001639 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001640 results[0] = apluscdiv2 - x;
1641 results[1] = apluscdiv2 + x;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001642 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001643 }
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001644 SkASSERT(results[0] >= 0);
1645 results[0] = SkScalarSqrt(results[0]);
1646 if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1647 SkASSERT(results[1] >= 0);
1648 results[1] = SkScalarSqrt(results[1]);
1649 }
1650 return true;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001651}
1652
commit-bot@chromium.org18786512014-05-20 14:53:45 +00001653SkScalar SkMatrix::getMinScale() const {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001654 SkScalar factor;
1655 if (get_scale_factor<kMin_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
1656 return factor;
1657 } else {
1658 return -1;
1659 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001660}
1661
commit-bot@chromium.org18786512014-05-20 14:53:45 +00001662SkScalar SkMatrix::getMaxScale() const {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001663 SkScalar factor;
1664 if (get_scale_factor<kMax_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
1665 return factor;
1666 } else {
1667 return -1;
1668 }
1669}
1670
1671bool SkMatrix::getMinMaxScales(SkScalar scaleFactors[2]) const {
1672 return get_scale_factor<kBoth_MinMaxOrBoth>(this->getType(), fMat, scaleFactors);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001673}
1674
mtklein78358bf2014-06-02 08:44:27 -07001675namespace {
1676
mtkleinada36352014-06-02 10:59:35 -07001677struct PODMatrix {
1678 SkScalar matrix[9];
1679 uint32_t typemask;
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +00001680
mtkleinada36352014-06-02 10:59:35 -07001681 const SkMatrix& asSkMatrix() const { return *reinterpret_cast<const SkMatrix*>(this); }
1682};
1683SK_COMPILE_ASSERT(sizeof(PODMatrix) == sizeof(SkMatrix), PODMatrixSizeMismatch);
mtklein78358bf2014-06-02 08:44:27 -07001684
1685} // namespace
1686
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001687const SkMatrix& SkMatrix::I() {
mtkleinada36352014-06-02 10:59:35 -07001688 SK_COMPILE_ASSERT(offsetof(SkMatrix, fMat) == offsetof(PODMatrix, matrix), BadfMat);
1689 SK_COMPILE_ASSERT(offsetof(SkMatrix, fTypeMask) == offsetof(PODMatrix, typemask), BadfTypeMask);
1690
1691 static const PODMatrix identity = { {SK_Scalar1, 0, 0,
1692 0, SK_Scalar1, 0,
1693 0, 0, SK_Scalar1 },
1694 kIdentity_Mask | kRectStaysRect_Mask};
1695 SkASSERT(identity.asSkMatrix().isIdentity());
1696 return identity.asSkMatrix();
tomhudson@google.com1f902872012-06-01 13:15:47 +00001697}
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001698
1699const SkMatrix& SkMatrix::InvalidMatrix() {
mtkleinada36352014-06-02 10:59:35 -07001700 SK_COMPILE_ASSERT(offsetof(SkMatrix, fMat) == offsetof(PODMatrix, matrix), BadfMat);
1701 SK_COMPILE_ASSERT(offsetof(SkMatrix, fTypeMask) == offsetof(PODMatrix, typemask), BadfTypeMask);
1702
1703 static const PODMatrix invalid =
1704 { {SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1705 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1706 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax },
1707 kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask };
1708 return invalid.asSkMatrix();
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001709}
1710
reedadf99902015-03-19 16:10:54 -07001711bool SkMatrix::decomposeScale(SkSize* scale, SkMatrix* remaining) const {
1712 if (this->hasPerspective()) {
1713 return false;
1714 }
1715
1716 const SkScalar sx = SkVector::Length(this->getScaleX(), this->getSkewY());
1717 const SkScalar sy = SkVector::Length(this->getSkewX(), this->getScaleY());
1718 if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) ||
1719 SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy)) {
1720 return false;
1721 }
1722
1723 if (scale) {
1724 scale->set(sx, sy);
1725 }
1726 if (remaining) {
1727 *remaining = *this;
1728 remaining->postScale(SkScalarInvert(sx), SkScalarInvert(sy));
1729 }
1730 return true;
1731}
1732
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001733///////////////////////////////////////////////////////////////////////////////
1734
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001735size_t SkMatrix::writeToMemory(void* buffer) const {
reed@android.com0ad336f2009-06-29 16:02:20 +00001736 // TODO write less for simple matrices
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001737 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
reed@android.com0ad336f2009-06-29 16:02:20 +00001738 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001739 memcpy(buffer, fMat, sizeInMemory);
reed@android.com0ad336f2009-06-29 16:02:20 +00001740 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001741 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001742}
1743
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001744size_t SkMatrix::readFromMemory(const void* buffer, size_t length) {
1745 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
1746 if (length < sizeInMemory) {
1747 return 0;
1748 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001749 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001750 memcpy(fMat, buffer, sizeInMemory);
reed@android.comf2b98d62010-12-20 18:26:13 +00001751 this->setTypeMask(kUnknown_Mask);
1752 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001753 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001754}
1755
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756void SkMatrix::dump() const {
1757 SkString str;
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001758 this->toString(&str);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759 SkDebugf("%s\n", str.c_str());
1760}
1761
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001762void SkMatrix::toString(SkString* str) const {
1763 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 +00001764 fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
1765 fMat[6], fMat[7], fMat[8]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766}
reed@google.comad514302013-01-02 20:19:45 +00001767
1768///////////////////////////////////////////////////////////////////////////////
1769
1770#include "SkMatrixUtils.h"
1771
reed@google.comae573582013-01-03 15:22:40 +00001772bool SkTreatAsSprite(const SkMatrix& mat, int width, int height,
reed@google.comad514302013-01-02 20:19:45 +00001773 unsigned subpixelBits) {
reed@google.comae573582013-01-03 15:22:40 +00001774 // quick reject on affine or perspective
reed@google.comad514302013-01-02 20:19:45 +00001775 if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
1776 return false;
1777 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001778
reed@google.comad514302013-01-02 20:19:45 +00001779 // quick success check
1780 if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
1781 return true;
1782 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001783
reed@google.comad514302013-01-02 20:19:45 +00001784 // mapRect supports negative scales, so we eliminate those first
1785 if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
1786 return false;
1787 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001788
reed@google.comad514302013-01-02 20:19:45 +00001789 SkRect dst;
reed@google.comae573582013-01-03 15:22:40 +00001790 SkIRect isrc = { 0, 0, width, height };
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00001791
reed@google.comad514302013-01-02 20:19:45 +00001792 {
reed@google.comae573582013-01-03 15:22:40 +00001793 SkRect src;
1794 src.set(isrc);
1795 mat.mapRect(&dst, src);
reed@google.comad514302013-01-02 20:19:45 +00001796 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001797
reed@google.comae573582013-01-03 15:22:40 +00001798 // just apply the translate to isrc
1799 isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
1800 SkScalarRoundToInt(mat.getTranslateY()));
1801
reed@google.comad514302013-01-02 20:19:45 +00001802 if (subpixelBits) {
1803 isrc.fLeft <<= subpixelBits;
1804 isrc.fTop <<= subpixelBits;
1805 isrc.fRight <<= subpixelBits;
1806 isrc.fBottom <<= subpixelBits;
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001807
reed@google.comad514302013-01-02 20:19:45 +00001808 const float scale = 1 << subpixelBits;
1809 dst.fLeft *= scale;
1810 dst.fTop *= scale;
1811 dst.fRight *= scale;
1812 dst.fBottom *= scale;
1813 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001814
reed@google.comae573582013-01-03 15:22:40 +00001815 SkIRect idst;
reed@google.comad514302013-01-02 20:19:45 +00001816 dst.round(&idst);
1817 return isrc == idst;
1818}
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001819
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001820// A square matrix M can be decomposed (via polar decomposition) into two matrices --
1821// an orthogonal matrix Q and a symmetric matrix S. In turn we can decompose S into U*W*U^T,
1822// where U is another orthogonal matrix and W is a scale matrix. These can be recombined
1823// to give M = (Q*U)*W*U^T, i.e., the product of two orthogonal matrices and a scale matrix.
1824//
1825// The one wrinkle is that traditionally Q may contain a reflection -- the
1826// calculation has been rejiggered to put that reflection into W.
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001827bool SkDecomposeUpper2x2(const SkMatrix& matrix,
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001828 SkPoint* rotation1,
1829 SkPoint* scale,
1830 SkPoint* rotation2) {
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001831
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001832 SkScalar A = matrix[SkMatrix::kMScaleX];
1833 SkScalar B = matrix[SkMatrix::kMSkewX];
1834 SkScalar C = matrix[SkMatrix::kMSkewY];
1835 SkScalar D = matrix[SkMatrix::kMScaleY];
1836
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +00001837 if (is_degenerate_2x2(A, B, C, D)) {
1838 return false;
1839 }
1840
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001841 double w1, w2;
1842 SkScalar cos1, sin1;
1843 SkScalar cos2, sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001844
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001845 // do polar decomposition (M = Q*S)
1846 SkScalar cosQ, sinQ;
1847 double Sa, Sb, Sd;
1848 // if M is already symmetric (i.e., M = I*S)
1849 if (SkScalarNearlyEqual(B, C)) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001850 cosQ = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001851 sinQ = 0;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001852
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001853 Sa = A;
1854 Sb = B;
1855 Sd = D;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001856 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001857 cosQ = A + D;
1858 sinQ = C - B;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001859 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cosQ*cosQ + sinQ*sinQ));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001860 cosQ *= reciplen;
1861 sinQ *= reciplen;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001862
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001863 // S = Q^-1*M
1864 // we don't calc Sc since it's symmetric
1865 Sa = A*cosQ + C*sinQ;
1866 Sb = B*cosQ + D*sinQ;
1867 Sd = -B*sinQ + D*cosQ;
1868 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +00001869
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001870 // Now we need to compute eigenvalues of S (our scale factors)
1871 // and eigenvectors (bases for our rotation)
1872 // From this, should be able to reconstruct S as U*W*U^T
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001873 if (SkScalarNearlyZero(SkDoubleToScalar(Sb))) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001874 // already diagonalized
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001875 cos1 = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001876 sin1 = 0;
1877 w1 = Sa;
1878 w2 = Sd;
1879 cos2 = cosQ;
1880 sin2 = sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001881 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001882 double diff = Sa - Sd;
1883 double discriminant = sqrt(diff*diff + 4.0*Sb*Sb);
1884 double trace = Sa + Sd;
1885 if (diff > 0) {
1886 w1 = 0.5*(trace + discriminant);
1887 w2 = 0.5*(trace - discriminant);
1888 } else {
1889 w1 = 0.5*(trace - discriminant);
1890 w2 = 0.5*(trace + discriminant);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001891 }
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001892
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001893 cos1 = SkDoubleToScalar(Sb); sin1 = SkDoubleToScalar(w1 - Sa);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001894 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cos1*cos1 + sin1*sin1));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001895 cos1 *= reciplen;
1896 sin1 *= reciplen;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001897
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001898 // rotation 2 is composition of Q and U
1899 cos2 = cos1*cosQ - sin1*sinQ;
1900 sin2 = sin1*cosQ + cos1*sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001901
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001902 // rotation 1 is U^T
1903 sin1 = -sin1;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001904 }
1905
bsalomon49f085d2014-09-05 13:34:00 -07001906 if (scale) {
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001907 scale->fX = SkDoubleToScalar(w1);
1908 scale->fY = SkDoubleToScalar(w2);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001909 }
bsalomon49f085d2014-09-05 13:34:00 -07001910 if (rotation1) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001911 rotation1->fX = cos1;
1912 rotation1->fY = sin1;
1913 }
bsalomon49f085d2014-09-05 13:34:00 -07001914 if (rotation2) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001915 rotation2->fX = cos2;
1916 rotation2->fY = sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001917 }
1918
1919 return true;
1920}