blob: a05ae0a177670a298b4945e870a534d7c584183f [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 "SkFloatBits.h"
mtklein567118f2016-04-14 14:07:02 -07009#include "SkMatrix.h"
10#include "SkNx.h"
11#include "SkPaint.h"
reed71c3c762015-06-24 10:29:17 -070012#include "SkRSXform.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkString.h"
mtkleinada36352014-06-02 10:59:35 -070014#include <stddef.h>
15
reed451e8222014-12-13 08:46:48 -080016static void normalize_perspective(SkScalar mat[9]) {
17 // If it was interesting to never store the last element, we could divide all 8 other
18 // elements here by the 9th, making it 1.0...
19 //
20 // When SkScalar was SkFixed, we would sometimes rescale the entire matrix to keep its
21 // component values from getting too large. This is not a concern when using floats/doubles,
22 // so we do nothing now.
23
24 // Disable this for now, but it could be enabled.
25#if 0
26 if (0 == mat[SkMatrix::kMPersp0] && 0 == mat[SkMatrix::kMPersp1]) {
27 SkScalar p2 = mat[SkMatrix::kMPersp2];
28 if (p2 != 0 && p2 != 1) {
29 double inv = 1.0 / p2;
30 for (int i = 0; i < 6; ++i) {
31 mat[i] = SkDoubleToScalar(mat[i] * inv);
32 }
33 mat[SkMatrix::kMPersp2] = 1;
34 }
35 }
36#endif
37}
38
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000039// In a few places, we performed the following
40// a * b + c * d + e
41// as
42// a * b + (c * d + e)
43//
44// sdot and scross are indended to capture these compound operations into a
45// function, with an eye toward considering upscaling the intermediates to
46// doubles for more precision (as we do in concat and invert).
47//
48// However, these few lines that performed the last add before the "dot", cause
49// tiny image differences, so we guard that change until we see the impact on
50// chrome's layouttests.
51//
52#define SK_LEGACY_MATRIX_MATH_ORDER
reed@android.comab7ac022009-09-18 13:38:43 +000053
reed@google.com8f4d2302013-12-17 16:44:46 +000054static inline float SkDoubleToFloat(double x) {
55 return static_cast<float>(x);
56}
reed@android.com8a1c16f2008-12-17 15:59:43 +000057
58/* [scale-x skew-x trans-x] [X] [X']
59 [skew-y scale-y trans-y] * [Y] = [Y']
60 [persp-0 persp-1 persp-2] [1] [1 ]
61*/
62
63void SkMatrix::reset() {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +000064 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000065 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 fMat[kMTransX] = fMat[kMTransY] =
67 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
69}
70
reed451e8222014-12-13 08:46:48 -080071void SkMatrix::set9(const SkScalar buffer[]) {
72 memcpy(fMat, buffer, 9 * sizeof(SkScalar));
73 normalize_perspective(fMat);
74 this->setTypeMask(kUnknown_Mask);
75}
76
77void SkMatrix::setAffine(const SkScalar buffer[]) {
78 fMat[kMScaleX] = buffer[kAScaleX];
79 fMat[kMSkewX] = buffer[kASkewX];
80 fMat[kMTransX] = buffer[kATransX];
81 fMat[kMSkewY] = buffer[kASkewY];
82 fMat[kMScaleY] = buffer[kAScaleY];
83 fMat[kMTransY] = buffer[kATransY];
84 fMat[kMPersp0] = 0;
85 fMat[kMPersp1] = 0;
86 fMat[kMPersp2] = 1;
87 this->setTypeMask(kUnknown_Mask);
88}
89
reed@android.com8a1c16f2008-12-17 15:59:43 +000090// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
91enum {
92 kTranslate_Shift,
93 kScale_Shift,
94 kAffine_Shift,
95 kPerspective_Shift,
96 kRectStaysRect_Shift
97};
98
reed@google.com8f4d2302013-12-17 16:44:46 +000099static const int32_t kScalar1Int = 0x3f800000;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000101uint8_t SkMatrix::computePerspectiveTypeMask() const {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000102 // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
103 // is a win, but replacing those below is not. We don't yet understand
104 // that result.
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000105 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000106 // If this is a perspective transform, we return true for all other
bsalomon@google.com19263b12012-07-13 13:36:38 +0000107 // transform flags - this does not disable any optimizations, respects
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000108 // the rule that the type mask must be conservative, and speeds up
junov@chromium.org6fc56992012-07-12 14:01:32 +0000109 // type mask computation.
110 return SkToU8(kORableMasks);
111 }
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000112
junov@chromium.org6fc56992012-07-12 14:01:32 +0000113 return SkToU8(kOnlyPerspectiveValid_Mask | kUnknown_Mask);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000114}
115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116uint8_t SkMatrix::computeTypeMask() const {
117 unsigned mask = 0;
118
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000119 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 || fMat[kMPersp2] != 1) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000120 // Once it is determined that that this is a perspective transform,
121 // all other flags are moot as far as optimizations are concerned.
122 return SkToU8(kORableMasks);
tomhudson@google.comac385252011-06-06 15:18:28 +0000123 }
124
125 if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
126 mask |= kTranslate_Mask;
127 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128
129 int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
130 int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
131 int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
132 int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
tomhudson@google.comac385252011-06-06 15:18:28 +0000133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 if (m01 | m10) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000135 // The skew components may be scale-inducing, unless we are dealing
136 // with a pure rotation. Testing for a pure rotation is expensive,
137 // so we opt for being conservative by always setting the scale bit.
138 // along with affine.
139 // By doing this, we are also ensuring that matrices have the same
140 // type masks as their inverses.
141 mask |= kAffine_Mask | kScale_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142
junov@chromium.org6fc56992012-07-12 14:01:32 +0000143 // For rectStaysRect, in the affine case, we only need check that
144 // the primary diagonal is all zeros and that the secondary diagonal
145 // is all non-zero.
tomhudson@google.comac385252011-06-06 15:18:28 +0000146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 // map non-zero to 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 m01 = m01 != 0;
149 m10 = m10 != 0;
tomhudson@google.comac385252011-06-06 15:18:28 +0000150
junov@chromium.org6fc56992012-07-12 14:01:32 +0000151 int dp0 = 0 == (m00 | m11) ; // true if both are 0
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 int ds1 = m01 & m10; // true if both are 1
tomhudson@google.comac385252011-06-06 15:18:28 +0000153
junov@chromium.org6fc56992012-07-12 14:01:32 +0000154 mask |= (dp0 & ds1) << kRectStaysRect_Shift;
155 } else {
156 // Only test for scale explicitly if not affine, since affine sets the
157 // scale bit.
tomhudsond5c42652015-12-07 10:38:05 -0800158 if ((m00 ^ kScalar1Int) | (m11 ^ kScalar1Int)) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000159 mask |= kScale_Mask;
160 }
161
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000162 // Not affine, therefore we already know secondary diagonal is
junov@chromium.org6fc56992012-07-12 14:01:32 +0000163 // all zeros, so we just need to check that primary diagonal is
164 // all non-zero.
165
166 // map non-zero to 1
167 m00 = m00 != 0;
168 m11 = m11 != 0;
169
170 // record if the (p)rimary diagonal is all non-zero
171 mask |= (m00 & m11) << kRectStaysRect_Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 }
173
174 return SkToU8(mask);
175}
176
177///////////////////////////////////////////////////////////////////////////////
178
reed@google.com3fb51872011-06-01 15:11:22 +0000179bool operator==(const SkMatrix& a, const SkMatrix& b) {
180 const SkScalar* SK_RESTRICT ma = a.fMat;
181 const SkScalar* SK_RESTRICT mb = b.fMat;
182
183 return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
184 ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
185 ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
186}
187
reed@google.com3fb51872011-06-01 15:11:22 +0000188///////////////////////////////////////////////////////////////////////////////
189
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000190// helper function to determine if upper-left 2x2 of matrix is degenerate
skia.committer@gmail.com16d53aa2013-07-30 07:01:00 +0000191static inline bool is_degenerate_2x2(SkScalar scaleX, SkScalar skewX,
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000192 SkScalar skewY, SkScalar scaleY) {
193 SkScalar perp_dot = scaleX*scaleY - skewX*skewY;
194 return SkScalarNearlyZero(perp_dot, SK_ScalarNearlyZero*SK_ScalarNearlyZero);
195}
196
197///////////////////////////////////////////////////////////////////////////////
198
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000199bool SkMatrix::isSimilarity(SkScalar tol) const {
200 // if identity or translate matrix
201 TypeMask mask = this->getType();
202 if (mask <= kTranslate_Mask) {
203 return true;
204 }
205 if (mask & kPerspective_Mask) {
206 return false;
207 }
208
209 SkScalar mx = fMat[kMScaleX];
210 SkScalar my = fMat[kMScaleY];
211 // if no skew, can just compare scale factors
212 if (!(mask & kAffine_Mask)) {
213 return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
214 }
215 SkScalar sx = fMat[kMSkewX];
216 SkScalar sy = fMat[kMSkewY];
217
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000218 if (is_degenerate_2x2(mx, sx, sy, my)) {
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000219 return false;
220 }
221
jvanverth17a845f2014-09-02 13:15:40 -0700222 // upper 2x2 is rotation/reflection + uniform scale if basis vectors
223 // are 90 degree rotations of each other
224 return (SkScalarNearlyEqual(mx, my, tol) && SkScalarNearlyEqual(sx, -sy, tol))
225 || (SkScalarNearlyEqual(mx, -my, tol) && SkScalarNearlyEqual(sx, sy, tol));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000226}
227
228bool SkMatrix::preservesRightAngles(SkScalar tol) const {
229 TypeMask mask = this->getType();
skia.committer@gmail.com07d3a652013-04-10 07:01:15 +0000230
jvanverth17a845f2014-09-02 13:15:40 -0700231 if (mask <= kTranslate_Mask) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000232 // identity, translate and/or scale
233 return true;
234 }
235 if (mask & kPerspective_Mask) {
236 return false;
237 }
238
jvanverth17a845f2014-09-02 13:15:40 -0700239 SkASSERT(mask & (kAffine_Mask | kScale_Mask));
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000240
241 SkScalar mx = fMat[kMScaleX];
242 SkScalar my = fMat[kMScaleY];
243 SkScalar sx = fMat[kMSkewX];
244 SkScalar sy = fMat[kMSkewY];
245
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +0000246 if (is_degenerate_2x2(mx, sx, sy, my)) {
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000247 return false;
248 }
249
jvanverth17a845f2014-09-02 13:15:40 -0700250 // upper 2x2 is scale + rotation/reflection if basis vectors are orthogonal
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000251 SkVector vec[2];
jvanverth17a845f2014-09-02 13:15:40 -0700252 vec[0].set(mx, sy);
253 vec[1].set(sx, my);
robertphillips@google.comdf3695e2013-04-09 14:01:44 +0000254
jvanverth17a845f2014-09-02 13:15:40 -0700255 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000256}
257
258///////////////////////////////////////////////////////////////////////////////
259
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000260static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
261 return a * b + c * d;
262}
263
264static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
265 SkScalar e, SkScalar f) {
266 return a * b + c * d + e * f;
267}
268
269static inline SkScalar scross(SkScalar a, SkScalar b, SkScalar c, SkScalar d) {
270 return a * b - c * d;
271}
272
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
reed@google.comc0784db2013-12-13 21:16:12 +0000274 if (dx || dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 fMat[kMTransX] = dx;
276 fMat[kMTransY] = dy;
277
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000278 fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000279 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281
282 this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
283 } else {
284 this->reset();
285 }
286}
287
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000288void SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
289 if (!dx && !dy) {
290 return;
291 }
292
mtkleincbdf0072016-08-19 09:05:27 -0700293 if (fTypeMask <= kTranslate_Mask) {
294 fMat[kMTransX] += dx;
295 fMat[kMTransY] += dy;
296 this->setTypeMask((fMat[kMTransX] != 0 || fMat[kMTransY] != 0) ? kTranslate_Mask
297 : kIdentity_Mask);
298 } else if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 SkMatrix m;
300 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000301 this->preConcat(m);
302 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000303 fMat[kMTransX] += sdot(fMat[kMScaleX], dx, fMat[kMSkewX], dy);
304 fMat[kMTransY] += sdot(fMat[kMSkewY], dx, fMat[kMScaleY], dy);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000305 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307}
308
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000309void SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
310 if (!dx && !dy) {
311 return;
312 }
313
tomhudson@google.com8d430182011-06-06 19:11:19 +0000314 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 SkMatrix m;
316 m.setTranslate(dx, dy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000317 this->postConcat(m);
318 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 fMat[kMTransX] += dx;
320 fMat[kMTransY] += dy;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000321 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323}
324
325///////////////////////////////////////////////////////////////////////////////
326
327void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000328 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000329 this->reset();
330 } else {
reedd1f0ebd2014-10-02 12:58:43 -0700331 this->setScaleTranslate(sx, sy, px - sx * px, py - sy * py);
reed@google.comf244f902011-09-06 21:02:36 +0000332 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333}
334
335void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000336 if (1 == sx && 1 == sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000337 this->reset();
338 } else {
339 fMat[kMScaleX] = sx;
340 fMat[kMScaleY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000341 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342
reed@google.comf244f902011-09-06 21:02:36 +0000343 fMat[kMTransX] = fMat[kMTransY] =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000344 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000345 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
reed@google.comf244f902011-09-06 21:02:36 +0000347 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349}
350
bsalomon@google.com5c638652011-07-18 19:31:59 +0000351bool SkMatrix::setIDiv(int divx, int divy) {
352 if (!divx || !divy) {
353 return false;
354 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000355 this->setScale(SkScalarInvert(divx), SkScalarInvert(divy));
bsalomon@google.com5c638652011-07-18 19:31:59 +0000356 return true;
357}
358
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000359void SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
360 if (1 == sx && 1 == sy) {
361 return;
362 }
363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 SkMatrix m;
365 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000366 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367}
368
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000369void SkMatrix::preScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000370 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000371 return;
reed@google.comf244f902011-09-06 21:02:36 +0000372 }
373
reed@google.com3fb51872011-06-01 15:11:22 +0000374 // the assumption is that these multiplies are very cheap, and that
375 // a full concat and/or just computing the matrix type is more expensive.
376 // Also, the fixed-point case checks for overflow, but the float doesn't,
377 // so we can get away with these blind multiplies.
378
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000379 fMat[kMScaleX] *= sx;
380 fMat[kMSkewY] *= sx;
381 fMat[kMPersp0] *= sx;
reed@google.com3fb51872011-06-01 15:11:22 +0000382
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000383 fMat[kMSkewX] *= sy;
384 fMat[kMScaleY] *= sy;
385 fMat[kMPersp1] *= sy;
reed@google.com3fb51872011-06-01 15:11:22 +0000386
fmalitaa379da42015-12-17 14:12:30 -0800387 // Attempt to simplify our type when applying an inverse scale.
388 // TODO: The persp/affine preconditions are in place to keep the mask consistent with
389 // what computeTypeMask() would produce (persp/skew always implies kScale).
390 // We should investigate whether these flag dependencies are truly needed.
391 if (fMat[kMScaleX] == 1 && fMat[kMScaleY] == 1
392 && !(fTypeMask & (kPerspective_Mask | kAffine_Mask))) {
393 this->clearTypeMask(kScale_Mask);
394 } else {
395 this->orTypeMask(kScale_Mask);
396 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397}
398
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000399void SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000400 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000401 return;
reed@google.comf244f902011-09-06 21:02:36 +0000402 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 SkMatrix m;
404 m.setScale(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000405 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406}
407
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000408void SkMatrix::postScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000409 if (1 == sx && 1 == sy) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000410 return;
reed@google.comf244f902011-09-06 21:02:36 +0000411 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 SkMatrix m;
413 m.setScale(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000414 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415}
416
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417// this guy perhaps can go away, if we have a fract/high-precision way to
418// scale matrices
419bool SkMatrix::postIDiv(int divx, int divy) {
420 if (divx == 0 || divy == 0) {
421 return false;
422 }
423
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 const float invX = 1.f / divx;
425 const float invY = 1.f / divy;
426
427 fMat[kMScaleX] *= invX;
428 fMat[kMSkewX] *= invX;
429 fMat[kMTransX] *= invX;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000430
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 fMat[kMScaleY] *= invY;
432 fMat[kMSkewY] *= invY;
433 fMat[kMTransY] *= invY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434
435 this->setTypeMask(kUnknown_Mask);
436 return true;
437}
438
439////////////////////////////////////////////////////////////////////////////////////
440
reedd1f0ebd2014-10-02 12:58:43 -0700441void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000442 const SkScalar oneMinusCosV = 1 - cosV;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443
444 fMat[kMScaleX] = cosV;
445 fMat[kMSkewX] = -sinV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000446 fMat[kMTransX] = sdot(sinV, py, oneMinusCosV, px);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447
448 fMat[kMSkewY] = sinV;
449 fMat[kMScaleY] = cosV;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000450 fMat[kMTransY] = sdot(-sinV, px, oneMinusCosV, py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451
452 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000453 fMat[kMPersp2] = 1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000454
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000455 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456}
457
reed71c3c762015-06-24 10:29:17 -0700458SkMatrix& SkMatrix::setRSXform(const SkRSXform& xform) {
459 fMat[kMScaleX] = xform.fSCos;
460 fMat[kMSkewX] = -xform.fSSin;
461 fMat[kMTransX] = xform.fTx;
mtklein4e8a09d2015-09-10 11:18:31 -0700462
reed71c3c762015-06-24 10:29:17 -0700463 fMat[kMSkewY] = xform.fSSin;
464 fMat[kMScaleY] = xform.fSCos;
465 fMat[kMTransY] = xform.fTy;
mtklein4e8a09d2015-09-10 11:18:31 -0700466
reed71c3c762015-06-24 10:29:17 -0700467 fMat[kMPersp0] = fMat[kMPersp1] = 0;
468 fMat[kMPersp2] = 1;
mtklein4e8a09d2015-09-10 11:18:31 -0700469
reed71c3c762015-06-24 10:29:17 -0700470 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
471 return *this;
472}
473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
475 fMat[kMScaleX] = cosV;
476 fMat[kMSkewX] = -sinV;
477 fMat[kMTransX] = 0;
478
479 fMat[kMSkewY] = sinV;
480 fMat[kMScaleY] = cosV;
481 fMat[kMTransY] = 0;
482
483 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000484 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000486 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487}
488
489void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
490 SkScalar sinV, cosV;
491 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
492 this->setSinCos(sinV, cosV, px, py);
493}
494
495void SkMatrix::setRotate(SkScalar degrees) {
496 SkScalar sinV, cosV;
497 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
498 this->setSinCos(sinV, cosV);
499}
500
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000501void SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502 SkMatrix m;
503 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000504 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505}
506
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000507void SkMatrix::preRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 SkMatrix m;
509 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000510 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511}
512
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000513void SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 SkMatrix m;
515 m.setRotate(degrees, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000516 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517}
518
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000519void SkMatrix::postRotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 SkMatrix m;
521 m.setRotate(degrees);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000522 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
525////////////////////////////////////////////////////////////////////////////////////
526
527void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000528 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 fMat[kMSkewX] = sx;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000530 fMat[kMTransX] = -sx * py;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531
532 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000533 fMat[kMScaleY] = 1;
534 fMat[kMTransY] = -sy * px;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535
536 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000537 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000539 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540}
541
542void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000543 fMat[kMScaleX] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 fMat[kMSkewX] = sx;
545 fMat[kMTransX] = 0;
546
547 fMat[kMSkewY] = sy;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000548 fMat[kMScaleY] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 fMat[kMTransY] = 0;
550
551 fMat[kMPersp0] = fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000552 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000554 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555}
556
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000557void SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 SkMatrix m;
559 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000560 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561}
562
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000563void SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 SkMatrix m;
565 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000566 this->preConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567}
568
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000569void SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 SkMatrix m;
571 m.setSkew(sx, sy, px, py);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000572 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573}
574
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000575void SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 SkMatrix m;
577 m.setSkew(sx, sy);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000578 this->postConcat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579}
580
581///////////////////////////////////////////////////////////////////////////////
582
reedd1f0ebd2014-10-02 12:58:43 -0700583bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit align) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 if (src.isEmpty()) {
585 this->reset();
586 return false;
587 }
588
589 if (dst.isEmpty()) {
reed@android.com4516f472009-06-29 16:25:36 +0000590 sk_bzero(fMat, 8 * sizeof(SkScalar));
reedd1f0ebd2014-10-02 12:58:43 -0700591 fMat[kMPersp2] = 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
593 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000594 SkScalar tx, sx = dst.width() / src.width();
595 SkScalar ty, sy = dst.height() / src.height();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 bool xLarger = false;
597
598 if (align != kFill_ScaleToFit) {
599 if (sx > sy) {
600 xLarger = true;
601 sx = sy;
602 } else {
603 sy = sx;
604 }
605 }
606
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000607 tx = dst.fLeft - src.fLeft * sx;
608 ty = dst.fTop - src.fTop * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
610 SkScalar diff;
611
612 if (xLarger) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000613 diff = dst.width() - src.width() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000615 diff = dst.height() - src.height() * sy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000617
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 if (align == kCenter_ScaleToFit) {
619 diff = SkScalarHalf(diff);
620 }
621
622 if (xLarger) {
623 tx += diff;
624 } else {
625 ty += diff;
626 }
627 }
628
reedd1f0ebd2014-10-02 12:58:43 -0700629 this->setScaleTranslate(sx, sy, tx, ty);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 return true;
632}
633
634///////////////////////////////////////////////////////////////////////////////
635
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000636static inline float muladdmul(float a, float b, float c, float d) {
637 return SkDoubleToFloat((double)a * b + (double)c * d);
reed@google.com8f4d2302013-12-17 16:44:46 +0000638}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000640static inline float rowcol3(const float row[], const float col[]) {
641 return row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
reed@google.com8f4d2302013-12-17 16:44:46 +0000642}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643
reedd1f0ebd2014-10-02 12:58:43 -0700644static bool only_scale_and_translate(unsigned mask) {
645 return 0 == (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask));
646}
647
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000648void SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
reedd1f0ebd2014-10-02 12:58:43 -0700649 TypeMask aType = a.getType();
650 TypeMask bType = b.getType();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000652 if (a.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 *this = b;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000654 } else if (b.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 *this = a;
reedd1f0ebd2014-10-02 12:58:43 -0700656 } else if (only_scale_and_translate(aType | bType)) {
657 this->setScaleTranslate(a.fMat[kMScaleX] * b.fMat[kMScaleX],
658 a.fMat[kMScaleY] * b.fMat[kMScaleY],
659 a.fMat[kMScaleX] * b.fMat[kMTransX] + a.fMat[kMTransX],
660 a.fMat[kMScaleY] * b.fMat[kMTransY] + a.fMat[kMTransY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 } else {
662 SkMatrix tmp;
663
664 if ((aType | bType) & kPerspective_Mask) {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000665 tmp.fMat[kMScaleX] = rowcol3(&a.fMat[0], &b.fMat[0]);
666 tmp.fMat[kMSkewX] = rowcol3(&a.fMat[0], &b.fMat[1]);
667 tmp.fMat[kMTransX] = rowcol3(&a.fMat[0], &b.fMat[2]);
668 tmp.fMat[kMSkewY] = rowcol3(&a.fMat[3], &b.fMat[0]);
669 tmp.fMat[kMScaleY] = rowcol3(&a.fMat[3], &b.fMat[1]);
670 tmp.fMat[kMTransY] = rowcol3(&a.fMat[3], &b.fMat[2]);
671 tmp.fMat[kMPersp0] = rowcol3(&a.fMat[6], &b.fMat[0]);
672 tmp.fMat[kMPersp1] = rowcol3(&a.fMat[6], &b.fMat[1]);
673 tmp.fMat[kMPersp2] = rowcol3(&a.fMat[6], &b.fMat[2]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674
675 normalize_perspective(tmp.fMat);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000676 tmp.setTypeMask(kUnknown_Mask);
reedd1f0ebd2014-10-02 12:58:43 -0700677 } else {
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000678 tmp.fMat[kMScaleX] = muladdmul(a.fMat[kMScaleX],
679 b.fMat[kMScaleX],
680 a.fMat[kMSkewX],
681 b.fMat[kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000683 tmp.fMat[kMSkewX] = muladdmul(a.fMat[kMScaleX],
684 b.fMat[kMSkewX],
685 a.fMat[kMSkewX],
686 b.fMat[kMScaleY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000688 tmp.fMat[kMTransX] = muladdmul(a.fMat[kMScaleX],
689 b.fMat[kMTransX],
690 a.fMat[kMSkewX],
reedd1f0ebd2014-10-02 12:58:43 -0700691 b.fMat[kMTransY]) + a.fMat[kMTransX];
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000692
693 tmp.fMat[kMSkewY] = muladdmul(a.fMat[kMSkewY],
694 b.fMat[kMScaleX],
695 a.fMat[kMScaleY],
696 b.fMat[kMSkewY]);
697
698 tmp.fMat[kMScaleY] = muladdmul(a.fMat[kMSkewY],
699 b.fMat[kMSkewX],
700 a.fMat[kMScaleY],
701 b.fMat[kMScaleY]);
702
703 tmp.fMat[kMTransY] = muladdmul(a.fMat[kMSkewY],
704 b.fMat[kMTransX],
705 a.fMat[kMScaleY],
reedd1f0ebd2014-10-02 12:58:43 -0700706 b.fMat[kMTransY]) + a.fMat[kMTransY];
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000707
reedd1f0ebd2014-10-02 12:58:43 -0700708 tmp.fMat[kMPersp0] = 0;
709 tmp.fMat[kMPersp1] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000710 tmp.fMat[kMPersp2] = 1;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000711 //SkDebugf("Concat mat non-persp type: %d\n", tmp.getType());
712 //SkASSERT(!(tmp.getType() & kPerspective_Mask));
713 tmp.setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714 }
715 *this = tmp;
716 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717}
718
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000719void SkMatrix::preConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 // check for identity first, so we don't do a needless copy of ourselves
721 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000722 if(!mat.isIdentity()) {
723 this->setConcat(*this, mat);
724 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725}
726
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000727void SkMatrix::postConcat(const SkMatrix& mat) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 // check for identity first, so we don't do a needless copy of ourselves
729 // to ourselves inside setConcat()
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000730 if (!mat.isIdentity()) {
731 this->setConcat(mat, *this);
732 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733}
734
735///////////////////////////////////////////////////////////////////////////////
736
reed@android.com0b9e2db2009-09-16 17:00:17 +0000737/* Matrix inversion is very expensive, but also the place where keeping
738 precision may be most important (here and matrix concat). Hence to avoid
739 bitmap blitting artifacts when walking the inverse, we use doubles for
740 the intermediate math, even though we know that is more expensive.
reed@android.com0b9e2db2009-09-16 17:00:17 +0000741 */
742
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000743static inline SkScalar scross_dscale(SkScalar a, SkScalar b,
744 SkScalar c, SkScalar d, double scale) {
745 return SkDoubleToScalar(scross(a, b, c, d) * scale);
746}
747
748static inline double dcross(double a, double b, double c, double d) {
749 return a * b - c * d;
750}
751
752static inline SkScalar dcross_dscale(double a, double b,
753 double c, double d, double scale) {
754 return SkDoubleToScalar(dcross(a, b, c, d) * scale);
755}
756
757static double sk_inv_determinant(const float mat[9], int isPerspective) {
reed@google.com8f4d2302013-12-17 16:44:46 +0000758 double det;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759
reed@google.com8f4d2302013-12-17 16:44:46 +0000760 if (isPerspective) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000761 det = mat[SkMatrix::kMScaleX] *
762 dcross(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2],
763 mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1])
764 +
765 mat[SkMatrix::kMSkewX] *
766 dcross(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0],
767 mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2])
768 +
769 mat[SkMatrix::kMTransX] *
770 dcross(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1],
771 mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]);
reed@google.com8f4d2302013-12-17 16:44:46 +0000772 } else {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000773 det = dcross(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY],
774 mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 }
776
reed@google.com8f4d2302013-12-17 16:44:46 +0000777 // Since the determinant is on the order of the cube of the matrix members,
778 // compare to the cube of the default nearly-zero constant (although an
779 // estimate of the condition number would be better if it wasn't so expensive).
780 if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
781 return 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 }
reed@google.com8f4d2302013-12-17 16:44:46 +0000783 return 1.0 / det;
784}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000786void SkMatrix::SetAffineIdentity(SkScalar affine[6]) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000787 affine[kAScaleX] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000788 affine[kASkewY] = 0;
789 affine[kASkewX] = 0;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000790 affine[kAScaleY] = 1;
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000791 affine[kATransX] = 0;
792 affine[kATransY] = 0;
793}
794
795bool SkMatrix::asAffine(SkScalar affine[6]) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000796 if (this->hasPerspective()) {
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000797 return false;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000798 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000799 if (affine) {
800 affine[kAScaleX] = this->fMat[kMScaleX];
801 affine[kASkewY] = this->fMat[kMSkewY];
802 affine[kASkewX] = this->fMat[kMSkewX];
803 affine[kAScaleY] = this->fMat[kMScaleY];
804 affine[kATransX] = this->fMat[kMTransX];
805 affine[kATransY] = this->fMat[kMTransY];
806 }
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000807 return true;
808}
809
robertphillips8b1fa6d2015-06-19 06:40:02 -0700810void SkMatrix::ComputeInv(SkScalar dst[9], const SkScalar src[9], double invDet, bool isPersp) {
robertphillips20eee3f2015-06-19 05:14:26 -0700811 SkASSERT(src != dst);
812 SkASSERT(src && dst);
813
814 if (isPersp) {
815 dst[kMScaleX] = scross_dscale(src[kMScaleY], src[kMPersp2], src[kMTransY], src[kMPersp1], invDet);
816 dst[kMSkewX] = scross_dscale(src[kMTransX], src[kMPersp1], src[kMSkewX], src[kMPersp2], invDet);
817 dst[kMTransX] = scross_dscale(src[kMSkewX], src[kMTransY], src[kMTransX], src[kMScaleY], invDet);
818
819 dst[kMSkewY] = scross_dscale(src[kMTransY], src[kMPersp0], src[kMSkewY], src[kMPersp2], invDet);
820 dst[kMScaleY] = scross_dscale(src[kMScaleX], src[kMPersp2], src[kMTransX], src[kMPersp0], invDet);
821 dst[kMTransY] = scross_dscale(src[kMTransX], src[kMSkewY], src[kMScaleX], src[kMTransY], invDet);
822
823 dst[kMPersp0] = scross_dscale(src[kMSkewY], src[kMPersp1], src[kMScaleY], src[kMPersp0], invDet);
824 dst[kMPersp1] = scross_dscale(src[kMSkewX], src[kMPersp0], src[kMScaleX], src[kMPersp1], invDet);
825 dst[kMPersp2] = scross_dscale(src[kMScaleX], src[kMScaleY], src[kMSkewX], src[kMSkewY], invDet);
826 } else { // not perspective
827 dst[kMScaleX] = SkDoubleToScalar(src[kMScaleY] * invDet);
828 dst[kMSkewX] = SkDoubleToScalar(-src[kMSkewX] * invDet);
829 dst[kMTransX] = dcross_dscale(src[kMSkewX], src[kMTransY], src[kMScaleY], src[kMTransX], invDet);
830
831 dst[kMSkewY] = SkDoubleToScalar(-src[kMSkewY] * invDet);
832 dst[kMScaleY] = SkDoubleToScalar(src[kMScaleX] * invDet);
833 dst[kMTransY] = dcross_dscale(src[kMSkewY], src[kMTransX], src[kMScaleX], src[kMTransY], invDet);
834
835 dst[kMPersp0] = 0;
836 dst[kMPersp1] = 0;
837 dst[kMPersp2] = 1;
838 }
839}
840
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000841bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
842 SkASSERT(!this->isIdentity());
reed@google.com2fb96cc2013-01-04 17:02:33 +0000843
844 TypeMask mask = this->getType();
845
846 if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
reed@google.come40591d2013-01-30 15:47:42 +0000847 bool invertible = true;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000848 if (inv) {
849 if (mask & kScale_Mask) {
850 SkScalar invX = fMat[kMScaleX];
851 SkScalar invY = fMat[kMScaleY];
852 if (0 == invX || 0 == invY) {
853 return false;
854 }
855 invX = SkScalarInvert(invX);
856 invY = SkScalarInvert(invY);
857
858 // Must be careful when writing to inv, since it may be the
859 // same memory as this.
860
861 inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
862 inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
863
864 inv->fMat[kMScaleX] = invX;
865 inv->fMat[kMScaleY] = invY;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000866 inv->fMat[kMPersp2] = 1;
867 inv->fMat[kMTransX] = -fMat[kMTransX] * invX;
868 inv->fMat[kMTransY] = -fMat[kMTransY] * invY;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000869
870 inv->setTypeMask(mask | kRectStaysRect_Mask);
871 } else {
872 // translate only
873 inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
874 }
halcanary96fcdcc2015-08-27 07:41:13 -0700875 } else { // inv is nullptr, just check if we're invertible
reed@google.come40591d2013-01-30 15:47:42 +0000876 if (!fMat[kMScaleX] || !fMat[kMScaleY]) {
877 invertible = false;
878 }
reed@google.com2fb96cc2013-01-04 17:02:33 +0000879 }
reed@google.come40591d2013-01-30 15:47:42 +0000880 return invertible;
reed@google.com2fb96cc2013-01-04 17:02:33 +0000881 }
reed@google.com4a1362a2013-01-04 18:52:16 +0000882
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000883 int isPersp = mask & kPerspective_Mask;
robertphillips20eee3f2015-06-19 05:14:26 -0700884 double invDet = sk_inv_determinant(fMat, isPersp);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885
robertphillips20eee3f2015-06-19 05:14:26 -0700886 if (invDet == 0) { // underflow
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 return false;
888 }
889
robertphillips20eee3f2015-06-19 05:14:26 -0700890 bool applyingInPlace = (inv == this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891
robertphillips20eee3f2015-06-19 05:14:26 -0700892 SkMatrix* tmp = inv;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893
robertphillips20eee3f2015-06-19 05:14:26 -0700894 SkMatrix storage;
halcanary96fcdcc2015-08-27 07:41:13 -0700895 if (applyingInPlace || nullptr == tmp) {
robertphillips20eee3f2015-06-19 05:14:26 -0700896 tmp = &storage; // we either need to avoid trampling memory or have no memory
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 }
robertphillips20eee3f2015-06-19 05:14:26 -0700898
899 ComputeInv(tmp->fMat, fMat, invDet, isPersp);
900 if (!tmp->isFinite()) {
901 return false;
902 }
903
904 tmp->setTypeMask(fTypeMask);
905
906 if (applyingInPlace) {
907 *inv = storage; // need to copy answer back
908 }
909
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 return true;
911}
912
913///////////////////////////////////////////////////////////////////////////////
914
reed7da19012015-03-29 11:58:48 -0700915void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 SkASSERT(m.getType() == 0);
917
reed7da19012015-03-29 11:58:48 -0700918 if (dst != src && count > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 memcpy(dst, src, count * sizeof(SkPoint));
reed7da19012015-03-29 11:58:48 -0700920 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921}
922
reed7da19012015-03-29 11:58:48 -0700923void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
mtklein567118f2016-04-14 14:07:02 -0700924 SkASSERT(m.getType() <= SkMatrix::kTranslate_Mask);
925 if (count > 0) {
926 SkScalar tx = m.getTranslateX();
927 SkScalar ty = m.getTranslateY();
928 if (count & 1) {
929 dst->fX = src->fX + tx;
930 dst->fY = src->fY + ty;
931 src += 1;
932 dst += 1;
933 }
934 Sk4s trans4(tx, ty, tx, ty);
935 count >>= 1;
936 if (count & 1) {
937 (Sk4s::Load(src) + trans4).store(dst);
938 src += 2;
939 dst += 2;
940 }
941 count >>= 1;
942 for (int i = 0; i < count; ++i) {
943 (Sk4s::Load(src+0) + trans4).store(dst+0);
944 (Sk4s::Load(src+2) + trans4).store(dst+2);
945 src += 4;
946 dst += 4;
947 }
948 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949}
950
reed7da19012015-03-29 11:58:48 -0700951void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
mtklein567118f2016-04-14 14:07:02 -0700952 SkASSERT(m.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
953 if (count > 0) {
954 SkScalar tx = m.getTranslateX();
955 SkScalar ty = m.getTranslateY();
956 SkScalar sx = m.getScaleX();
957 SkScalar sy = m.getScaleY();
958 if (count & 1) {
959 dst->fX = src->fX * sx + tx;
960 dst->fY = src->fY * sy + ty;
961 src += 1;
962 dst += 1;
963 }
964 Sk4s trans4(tx, ty, tx, ty);
965 Sk4s scale4(sx, sy, sx, sy);
966 count >>= 1;
967 if (count & 1) {
968 (Sk4s::Load(src) * scale4 + trans4).store(dst);
969 src += 2;
970 dst += 2;
971 }
972 count >>= 1;
973 for (int i = 0; i < count; ++i) {
974 (Sk4s::Load(src+0) * scale4 + trans4).store(dst+0);
975 (Sk4s::Load(src+2) * scale4 + trans4).store(dst+2);
976 src += 4;
977 dst += 4;
978 }
979 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980}
981
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
983 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000984 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 if (count > 0) {
987 do {
988 SkScalar sy = src->fY;
989 SkScalar sx = src->fX;
990 src += 1;
991
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +0000992 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
993 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
994#ifdef SK_LEGACY_MATRIX_MATH_ORDER
995 SkScalar z = sx * m.fMat[kMPersp0] + (sy * m.fMat[kMPersp1] + m.fMat[kMPersp2]);
996#else
997 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
998#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 if (z) {
1000 z = SkScalarFastInvert(z);
1001 }
1002
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001003 dst->fY = y * z;
1004 dst->fX = x * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 dst += 1;
1006 } while (--count);
1007 }
1008}
1009
reedd8b34c22015-03-27 14:00:41 -07001010void SkMatrix::Affine_vpts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
mtklein567118f2016-04-14 14:07:02 -07001011 SkASSERT(m.getType() != SkMatrix::kPerspective_Mask);
1012 if (count > 0) {
1013 SkScalar tx = m.getTranslateX();
1014 SkScalar ty = m.getTranslateY();
1015 SkScalar sx = m.getScaleX();
1016 SkScalar sy = m.getScaleY();
1017 SkScalar kx = m.getSkewX();
1018 SkScalar ky = m.getSkewY();
1019 if (count & 1) {
1020 dst->set(src->fX * sx + src->fY * kx + tx,
1021 src->fX * ky + src->fY * sy + ty);
1022 src += 1;
1023 dst += 1;
1024 }
1025 Sk4s trans4(tx, ty, tx, ty);
1026 Sk4s scale4(sx, sy, sx, sy);
1027 Sk4s skew4(kx, ky, kx, ky); // applied to swizzle of src4
1028 count >>= 1;
1029 for (int i = 0; i < count; ++i) {
1030 Sk4s src4 = Sk4s::Load(src);
1031 Sk4s swz4 = SkNx_shuffle<1,0,3,2>(src4); // y0 x0, y1 x1
1032 (src4 * scale4 + swz4 * skew4 + trans4).store(dst);
1033 src += 2;
1034 dst += 2;
1035 }
1036 }
reed80106322015-03-23 08:15:14 -07001037}
1038
reed7da19012015-03-29 11:58:48 -07001039const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
1040 SkMatrix::Identity_pts, SkMatrix::Trans_pts,
1041 SkMatrix::Scale_pts, SkMatrix::Scale_pts,
reedd8b34c22015-03-27 14:00:41 -07001042 SkMatrix::Affine_vpts, SkMatrix::Affine_vpts,
1043 SkMatrix::Affine_vpts, SkMatrix::Affine_vpts,
1044 // repeat the persp proc 8 times
1045 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1046 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1047 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1048 SkMatrix::Persp_pts, SkMatrix::Persp_pts
1049};
1050
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051///////////////////////////////////////////////////////////////////////////////
1052
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001053void SkMatrix::mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const {
1054 SkASSERT((dst && src && count > 0) || 0 == count);
1055 // no partial overlap
qiankun.miao08996962015-01-12 07:50:25 -08001056 SkASSERT(src == dst || &dst[3*count] <= &src[0] || &src[3*count] <= &dst[0]);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001057
1058 if (count > 0) {
1059 if (this->isIdentity()) {
1060 memcpy(dst, src, 3*count*sizeof(SkScalar));
1061 return;
1062 }
1063 do {
1064 SkScalar sx = src[0];
1065 SkScalar sy = src[1];
1066 SkScalar sw = src[2];
1067 src += 3;
1068
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001069 SkScalar x = sdot(sx, fMat[kMScaleX], sy, fMat[kMSkewX], sw, fMat[kMTransX]);
1070 SkScalar y = sdot(sx, fMat[kMSkewY], sy, fMat[kMScaleY], sw, fMat[kMTransY]);
1071 SkScalar w = sdot(sx, fMat[kMPersp0], sy, fMat[kMPersp1], sw, fMat[kMPersp2]);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +00001072
1073 dst[0] = x;
1074 dst[1] = y;
1075 dst[2] = w;
1076 dst += 3;
1077 } while (--count);
1078 }
1079}
1080
1081///////////////////////////////////////////////////////////////////////////////
1082
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001084 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 SkPoint origin;
1086
1087 MapXYProc proc = this->getMapXYProc();
1088 proc(*this, 0, 0, &origin);
1089
1090 for (int i = count - 1; i >= 0; --i) {
1091 SkPoint tmp;
1092
1093 proc(*this, src[i].fX, src[i].fY, &tmp);
1094 dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
1095 }
1096 } else {
1097 SkMatrix tmp = *this;
1098
1099 tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
1100 tmp.clearTypeMask(kTranslate_Mask);
1101 tmp.mapPoints(dst, src, count);
1102 }
1103}
1104
reeda39667c2016-08-22 06:39:49 -07001105static Sk4f sort_as_rect(const Sk4f& ltrb) {
1106 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1107 Sk4f min = Sk4f::Min(ltrb, rblt);
1108 Sk4f max = Sk4f::Max(ltrb, rblt);
1109 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1110 // ARM this sequence generates the fastest (a single instruction).
1111 return Sk4f(min[2], min[3], max[0], max[1]);
1112}
1113
halcanaryc5769b22016-08-10 07:13:21 -07001114void SkMatrix::mapRectScaleTranslate(SkRect* dst, const SkRect& src) const {
1115 SkASSERT(dst);
reed47df89e2016-06-30 06:38:54 -07001116 SkASSERT(this->isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001117
reed47df89e2016-06-30 06:38:54 -07001118 SkScalar sx = fMat[kMScaleX];
1119 SkScalar sy = fMat[kMScaleY];
1120 SkScalar tx = fMat[kMTransX];
1121 SkScalar ty = fMat[kMTransY];
1122 Sk4f scale(sx, sy, sx, sy);
1123 Sk4f trans(tx, ty, tx, ty);
reeda39667c2016-08-22 06:39:49 -07001124 sort_as_rect(Sk4f::Load(&src.fLeft) * scale + trans).store(&dst->fLeft);
reed47df89e2016-06-30 06:38:54 -07001125}
1126
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
caryclarka10742c2014-09-18 11:00:40 -07001128 SkASSERT(dst);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129
reeda39667c2016-08-22 06:39:49 -07001130 if (this->getType() <= kTranslate_Mask) {
1131 SkScalar tx = fMat[kMTransX];
1132 SkScalar ty = fMat[kMTransY];
1133 Sk4f trans(tx, ty, tx, ty);
1134 sort_as_rect(Sk4f::Load(&src.fLeft) + trans).store(&dst->fLeft);
1135 return true;
1136 }
reed47df89e2016-06-30 06:38:54 -07001137 if (this->isScaleTranslate()) {
halcanaryc5769b22016-08-10 07:13:21 -07001138 this->mapRectScaleTranslate(dst, src);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 return true;
1140 } else {
1141 SkPoint quad[4];
1142
1143 src.toQuad(quad);
1144 this->mapPoints(quad, quad, 4);
1145 dst->set(quad, 4);
1146 return false;
1147 }
1148}
1149
1150SkScalar SkMatrix::mapRadius(SkScalar radius) const {
1151 SkVector vec[2];
1152
1153 vec[0].set(radius, 0);
1154 vec[1].set(0, radius);
1155 this->mapVectors(vec, 2);
1156
1157 SkScalar d0 = vec[0].length();
1158 SkScalar d1 = vec[1].length();
1159
reed@google.com6e252d42013-12-18 05:06:52 +00001160 // return geometric mean
1161 return SkScalarSqrt(d0 * d1);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162}
1163
1164///////////////////////////////////////////////////////////////////////////////
1165
1166void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1167 SkPoint* pt) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001168 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001170 SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1171 SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1172 SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 if (z) {
1174 z = SkScalarFastInvert(z);
1175 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001176 pt->fX = x * z;
1177 pt->fY = y * z;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178}
1179
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1181 SkPoint* pt) {
1182 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001183
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001184#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1185 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1186 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1187#else
1188 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1189 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1190#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
1193void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1194 SkPoint* pt) {
1195 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
1196 SkASSERT(0 == m.fMat[kMTransX]);
1197 SkASSERT(0 == m.fMat[kMTransY]);
1198
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001199#ifdef SK_LEGACY_MATRIX_MATH_ORDER
1200 pt->fX = sx * m.fMat[kMScaleX] + (sy * m.fMat[kMSkewX] + m.fMat[kMTransX]);
1201 pt->fY = sx * m.fMat[kMSkewY] + (sy * m.fMat[kMScaleY] + m.fMat[kMTransY]);
1202#else
1203 pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1204 pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1205#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206}
1207
1208void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1209 SkPoint* pt) {
1210 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1211 == kScale_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001212
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001213 pt->fX = sx * m.fMat[kMScaleX] + m.fMat[kMTransX];
1214 pt->fY = sy * m.fMat[kMScaleY] + m.fMat[kMTransY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215}
1216
1217void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1218 SkPoint* pt) {
1219 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1220 == kScale_Mask);
1221 SkASSERT(0 == m.fMat[kMTransX]);
1222 SkASSERT(0 == m.fMat[kMTransY]);
1223
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001224 pt->fX = sx * m.fMat[kMScaleX];
1225 pt->fY = sy * m.fMat[kMScaleY];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226}
1227
1228void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1229 SkPoint* pt) {
1230 SkASSERT(m.getType() == kTranslate_Mask);
1231
1232 pt->fX = sx + m.fMat[kMTransX];
1233 pt->fY = sy + m.fMat[kMTransY];
1234}
1235
1236void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1237 SkPoint* pt) {
1238 SkASSERT(0 == m.getType());
1239
1240 pt->fX = sx;
1241 pt->fY = sy;
1242}
1243
1244const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
1245 SkMatrix::Identity_xy, SkMatrix::Trans_xy,
1246 SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy,
1247 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1248 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1249 // repeat the persp proc 8 times
1250 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1251 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1252 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1253 SkMatrix::Persp_xy, SkMatrix::Persp_xy
1254};
1255
1256///////////////////////////////////////////////////////////////////////////////
1257
1258// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
reed@google.com83c6a222013-12-18 05:19:22 +00001259#define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26)))
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260
benjaminwagner8e175562016-02-16 10:09:40 -08001261bool SkMatrix::isFixedStepInX() const {
1262 return PerspNearlyZero(fMat[kMPersp0]);
1263}
1264
1265SkVector SkMatrix::fixedStepInX(SkScalar y) const {
1266 SkASSERT(PerspNearlyZero(fMat[kMPersp0]));
1267 if (PerspNearlyZero(fMat[kMPersp1]) &&
1268 PerspNearlyZero(fMat[kMPersp2] - 1)) {
1269 return SkVector::Make(fMat[kMScaleX], fMat[kMSkewY]);
1270 } else {
1271 SkScalar z = y * fMat[kMPersp1] + fMat[kMPersp2];
1272 return SkVector::Make(fMat[kMScaleX] / z, fMat[kMSkewY] / z);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274}
1275
1276///////////////////////////////////////////////////////////////////////////////
1277
1278#include "SkPerspIter.h"
1279
1280SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
1281 : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
1282 SkPoint pt;
1283
1284 SkMatrix::Persp_xy(m, x0, y0, &pt);
1285 fX = SkScalarToFixed(pt.fX);
1286 fY = SkScalarToFixed(pt.fY);
1287}
1288
1289int SkPerspIter::next() {
1290 int n = fCount;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001291
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 if (0 == n) {
1293 return 0;
1294 }
1295 SkPoint pt;
1296 SkFixed x = fX;
1297 SkFixed y = fY;
1298 SkFixed dx, dy;
1299
1300 if (n >= kCount) {
1301 n = kCount;
1302 fSX += SkIntToScalar(kCount);
1303 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1304 fX = SkScalarToFixed(pt.fX);
1305 fY = SkScalarToFixed(pt.fY);
1306 dx = (fX - x) >> kShift;
1307 dy = (fY - y) >> kShift;
1308 } else {
1309 fSX += SkIntToScalar(n);
1310 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1311 fX = SkScalarToFixed(pt.fX);
1312 fY = SkScalarToFixed(pt.fY);
1313 dx = (fX - x) / n;
1314 dy = (fY - y) / n;
1315 }
1316
1317 SkFixed* p = fStorage;
1318 for (int i = 0; i < n; i++) {
1319 *p++ = x; x += dx;
1320 *p++ = y; y += dy;
1321 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001322
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 fCount -= n;
1324 return n;
1325}
1326
1327///////////////////////////////////////////////////////////////////////////////
1328
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329static inline bool checkForZero(float x) {
1330 return x*x == 0;
1331}
1332
1333static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1334 float x = 1, y = 1;
1335 SkPoint pt1, pt2;
1336
1337 if (count > 1) {
1338 pt1.fX = poly[1].fX - poly[0].fX;
1339 pt1.fY = poly[1].fY - poly[0].fY;
1340 y = SkPoint::Length(pt1.fX, pt1.fY);
1341 if (checkForZero(y)) {
1342 return false;
1343 }
1344 switch (count) {
1345 case 2:
1346 break;
1347 case 3:
1348 pt2.fX = poly[0].fY - poly[2].fY;
1349 pt2.fY = poly[2].fX - poly[0].fX;
1350 goto CALC_X;
1351 default:
1352 pt2.fX = poly[0].fY - poly[3].fY;
1353 pt2.fY = poly[3].fX - poly[0].fX;
1354 CALC_X:
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001355 x = sdot(pt1.fX, pt2.fX, pt1.fY, pt2.fY) / y;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 break;
1357 }
1358 }
1359 pt->set(x, y);
1360 return true;
1361}
1362
1363bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1364 const SkPoint& scale) {
1365 float invScale = 1 / scale.fY;
1366
1367 dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1368 dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
1369 dst->fMat[kMPersp0] = 0;
1370 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1371 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1372 dst->fMat[kMPersp1] = 0;
1373 dst->fMat[kMTransX] = srcPt[0].fX;
1374 dst->fMat[kMTransY] = srcPt[0].fY;
1375 dst->fMat[kMPersp2] = 1;
1376 dst->setTypeMask(kUnknown_Mask);
1377 return true;
1378}
1379
1380bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1381 const SkPoint& scale) {
1382 float invScale = 1 / scale.fX;
1383 dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
1384 dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
1385 dst->fMat[kMPersp0] = 0;
1386
1387 invScale = 1 / scale.fY;
1388 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1389 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1390 dst->fMat[kMPersp1] = 0;
1391
1392 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
1399bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1400 const SkPoint& scale) {
1401 float a1, a2;
1402 float x0, y0, x1, y1, x2, y2;
1403
1404 x0 = srcPt[2].fX - srcPt[0].fX;
1405 y0 = srcPt[2].fY - srcPt[0].fY;
1406 x1 = srcPt[2].fX - srcPt[1].fX;
1407 y1 = srcPt[2].fY - srcPt[1].fY;
1408 x2 = srcPt[2].fX - srcPt[3].fX;
1409 y2 = srcPt[2].fY - srcPt[3].fY;
1410
1411 /* check if abs(x2) > abs(y2) */
1412 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1413 float denom = SkScalarMulDiv(x1, y2, x2) - y1;
1414 if (checkForZero(denom)) {
1415 return false;
1416 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001417 a1 = (SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 } else {
1419 float denom = x1 - SkScalarMulDiv(y1, x2, y2);
1420 if (checkForZero(denom)) {
1421 return false;
1422 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001423 a1 = (x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 }
1425
1426 /* check if abs(x1) > abs(y1) */
1427 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1428 float denom = y2 - SkScalarMulDiv(x2, y1, x1);
1429 if (checkForZero(denom)) {
1430 return false;
1431 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001432 a2 = (y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1)) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 } else {
1434 float denom = SkScalarMulDiv(y2, x1, y1) - x2;
1435 if (checkForZero(denom)) {
1436 return false;
1437 }
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001438 a2 = (SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2) / denom;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 }
1440
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001441 float invScale = SkScalarInvert(scale.fX);
1442 dst->fMat[kMScaleX] = (a2 * srcPt[3].fX + srcPt[3].fX - srcPt[0].fX) * invScale;
1443 dst->fMat[kMSkewY] = (a2 * srcPt[3].fY + srcPt[3].fY - srcPt[0].fY) * invScale;
1444 dst->fMat[kMPersp0] = a2 * invScale;
1445
1446 invScale = SkScalarInvert(scale.fY);
1447 dst->fMat[kMSkewX] = (a1 * srcPt[1].fX + srcPt[1].fX - srcPt[0].fX) * invScale;
1448 dst->fMat[kMScaleY] = (a1 * srcPt[1].fY + srcPt[1].fY - srcPt[0].fY) * invScale;
1449 dst->fMat[kMPersp1] = a1 * invScale;
1450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 dst->fMat[kMTransX] = srcPt[0].fX;
1452 dst->fMat[kMTransY] = srcPt[0].fY;
1453 dst->fMat[kMPersp2] = 1;
1454 dst->setTypeMask(kUnknown_Mask);
1455 return true;
1456}
1457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
1459
1460/* Taken from Rob Johnson's original sample code in QuickDraw GX
1461*/
1462bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
1463 int count) {
1464 if ((unsigned)count > 4) {
1465 SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
1466 return false;
1467 }
1468
1469 if (0 == count) {
1470 this->reset();
1471 return true;
1472 }
1473 if (1 == count) {
1474 this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
1475 return true;
1476 }
1477
1478 SkPoint scale;
1479 if (!poly_to_point(&scale, src, count) ||
1480 SkScalarNearlyZero(scale.fX) ||
1481 SkScalarNearlyZero(scale.fY)) {
1482 return false;
1483 }
1484
1485 static const PolyMapProc gPolyMapProcs[] = {
1486 SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
1487 };
1488 PolyMapProc proc = gPolyMapProcs[count - 2];
1489
1490 SkMatrix tempMap, result;
1491 tempMap.setTypeMask(kUnknown_Mask);
1492
1493 if (!proc(src, &tempMap, scale)) {
1494 return false;
1495 }
1496 if (!tempMap.invert(&result)) {
1497 return false;
1498 }
1499 if (!proc(dst, &tempMap, scale)) {
1500 return false;
1501 }
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001502 this->setConcat(tempMap, result);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503 return true;
1504}
1505
1506///////////////////////////////////////////////////////////////////////////////
1507
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001508enum MinMaxOrBoth {
1509 kMin_MinMaxOrBoth,
1510 kMax_MinMaxOrBoth,
1511 kBoth_MinMaxOrBoth
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001512};
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001513
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001514template <MinMaxOrBoth MIN_MAX_OR_BOTH> bool get_scale_factor(SkMatrix::TypeMask typeMask,
1515 const SkScalar m[9],
1516 SkScalar results[/*1 or 2*/]) {
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001517 if (typeMask & SkMatrix::kPerspective_Mask) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001518 return false;
bsalomon@google.com38396322011-09-09 19:32:04 +00001519 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001520 if (SkMatrix::kIdentity_Mask == typeMask) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001521 results[0] = SK_Scalar1;
1522 if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1523 results[1] = SK_Scalar1;
1524 }
1525 return true;
bsalomon@google.com38396322011-09-09 19:32:04 +00001526 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001527 if (!(typeMask & SkMatrix::kAffine_Mask)) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001528 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1529 results[0] = SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1530 SkScalarAbs(m[SkMatrix::kMScaleY]));
1531 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1532 results[0] = SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
1533 SkScalarAbs(m[SkMatrix::kMScaleY]));
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001534 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001535 results[0] = SkScalarAbs(m[SkMatrix::kMScaleX]);
1536 results[1] = SkScalarAbs(m[SkMatrix::kMScaleY]);
1537 if (results[0] > results[1]) {
1538 SkTSwap(results[0], results[1]);
1539 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001540 }
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001541 return true;
bsalomon@google.com38396322011-09-09 19:32:04 +00001542 }
1543 // ignore the translation part of the matrix, just look at 2x2 portion.
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001544 // compute singular values, take largest or smallest abs value.
bsalomon@google.com38396322011-09-09 19:32:04 +00001545 // [a b; b c] = A^T*A
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001546 SkScalar a = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMScaleX],
1547 m[SkMatrix::kMSkewY], m[SkMatrix::kMSkewY]);
1548 SkScalar b = sdot(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewX],
1549 m[SkMatrix::kMScaleY], m[SkMatrix::kMSkewY]);
1550 SkScalar c = sdot(m[SkMatrix::kMSkewX], m[SkMatrix::kMSkewX],
1551 m[SkMatrix::kMScaleY], m[SkMatrix::kMScaleY]);
bsalomon@google.com38396322011-09-09 19:32:04 +00001552 // eigenvalues of A^T*A are the squared singular values of A.
1553 // characteristic equation is det((A^T*A) - l*I) = 0
1554 // l^2 - (a + c)l + (ac-b^2)
1555 // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001556 // and roots are guaranteed to be pos and real).
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001557 SkScalar bSqd = b * b;
bsalomon@google.com38396322011-09-09 19:32:04 +00001558 // if upper left 2x2 is orthogonal save some math
jvanverth@google.comc490f802013-03-04 13:56:38 +00001559 if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001560 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1561 results[0] = SkMinScalar(a, c);
1562 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1563 results[0] = SkMaxScalar(a, c);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001564 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001565 results[0] = a;
1566 results[1] = c;
1567 if (results[0] > results[1]) {
1568 SkTSwap(results[0], results[1]);
1569 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001570 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001571 } else {
bsalomon@google.com38396322011-09-09 19:32:04 +00001572 SkScalar aminusc = a - c;
1573 SkScalar apluscdiv2 = SkScalarHalf(a + c);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001574 SkScalar x = SkScalarHalf(SkScalarSqrt(aminusc * aminusc + 4 * bSqd));
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001575 if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1576 results[0] = apluscdiv2 - x;
1577 } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
1578 results[0] = apluscdiv2 + x;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001579 } else {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001580 results[0] = apluscdiv2 - x;
1581 results[1] = apluscdiv2 + x;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001582 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001583 }
kolczykcea22ae2016-07-16 11:52:37 -07001584 if (!SkScalarIsFinite(results[0])) {
bsalomonef2c7c72015-12-17 15:33:13 -08001585 return false;
1586 }
kolczyk718aab12016-07-19 03:49:14 -07001587 // Due to the floating point inaccuracy, there might be an error in a, b, c
1588 // calculated by sdot, further deepened by subsequent arithmetic operations
1589 // on them. Therefore, we allow and cap the nearly-zero negative values.
1590 SkASSERT(results[0] >= -SK_ScalarNearlyZero);
1591 if (results[0] < 0) {
kolczykcea22ae2016-07-16 11:52:37 -07001592 results[0] = 0;
1593 }
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001594 results[0] = SkScalarSqrt(results[0]);
1595 if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
kolczykcea22ae2016-07-16 11:52:37 -07001596 if (!SkScalarIsFinite(results[1])) {
bsalomonef2c7c72015-12-17 15:33:13 -08001597 return false;
1598 }
kolczyk718aab12016-07-19 03:49:14 -07001599 SkASSERT(results[1] >= -SK_ScalarNearlyZero);
1600 if (results[1] < 0) {
kolczykcea22ae2016-07-16 11:52:37 -07001601 results[1] = 0;
1602 }
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001603 results[1] = SkScalarSqrt(results[1]);
1604 }
1605 return true;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001606}
1607
commit-bot@chromium.org18786512014-05-20 14:53:45 +00001608SkScalar SkMatrix::getMinScale() const {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001609 SkScalar factor;
1610 if (get_scale_factor<kMin_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
1611 return factor;
1612 } else {
1613 return -1;
1614 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +00001615}
1616
commit-bot@chromium.org18786512014-05-20 14:53:45 +00001617SkScalar SkMatrix::getMaxScale() const {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +00001618 SkScalar factor;
1619 if (get_scale_factor<kMax_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
1620 return factor;
1621 } else {
1622 return -1;
1623 }
1624}
1625
1626bool SkMatrix::getMinMaxScales(SkScalar scaleFactors[2]) const {
1627 return get_scale_factor<kBoth_MinMaxOrBoth>(this->getType(), fMat, scaleFactors);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001628}
1629
mtklein78358bf2014-06-02 08:44:27 -07001630namespace {
1631
bungemanafd7c742015-08-25 12:05:55 -07001632// SkMatrix is C++11 POD (trivial and standard-layout), but not aggregate (it has private fields).
1633struct AggregateMatrix {
mtkleinada36352014-06-02 10:59:35 -07001634 SkScalar matrix[9];
1635 uint32_t typemask;
commit-bot@chromium.org21a705d2013-10-10 21:58:31 +00001636
mtkleinada36352014-06-02 10:59:35 -07001637 const SkMatrix& asSkMatrix() const { return *reinterpret_cast<const SkMatrix*>(this); }
1638};
bungemanafd7c742015-08-25 12:05:55 -07001639static_assert(sizeof(AggregateMatrix) == sizeof(SkMatrix), "AggregateMatrix size mismatch.");
mtklein78358bf2014-06-02 08:44:27 -07001640
1641} // namespace
1642
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001643const SkMatrix& SkMatrix::I() {
bungemanafd7c742015-08-25 12:05:55 -07001644 static_assert(offsetof(SkMatrix,fMat) == offsetof(AggregateMatrix,matrix), "fMat");
1645 static_assert(offsetof(SkMatrix,fTypeMask) == offsetof(AggregateMatrix,typemask), "fTypeMask");
mtkleinada36352014-06-02 10:59:35 -07001646
bungemanafd7c742015-08-25 12:05:55 -07001647 static const AggregateMatrix identity = { {SK_Scalar1, 0, 0,
1648 0, SK_Scalar1, 0,
1649 0, 0, SK_Scalar1 },
1650 kIdentity_Mask | kRectStaysRect_Mask};
mtkleinada36352014-06-02 10:59:35 -07001651 SkASSERT(identity.asSkMatrix().isIdentity());
1652 return identity.asSkMatrix();
tomhudson@google.com1f902872012-06-01 13:15:47 +00001653}
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001654
1655const SkMatrix& SkMatrix::InvalidMatrix() {
bungemanafd7c742015-08-25 12:05:55 -07001656 static_assert(offsetof(SkMatrix,fMat) == offsetof(AggregateMatrix,matrix), "fMat");
1657 static_assert(offsetof(SkMatrix,fTypeMask) == offsetof(AggregateMatrix,typemask), "fTypeMask");
mtkleinada36352014-06-02 10:59:35 -07001658
bungemanafd7c742015-08-25 12:05:55 -07001659 static const AggregateMatrix invalid =
mtkleinada36352014-06-02 10:59:35 -07001660 { {SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1661 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1662 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax },
1663 kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask };
1664 return invalid.asSkMatrix();
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001665}
1666
reedadf99902015-03-19 16:10:54 -07001667bool SkMatrix::decomposeScale(SkSize* scale, SkMatrix* remaining) const {
1668 if (this->hasPerspective()) {
1669 return false;
1670 }
1671
1672 const SkScalar sx = SkVector::Length(this->getScaleX(), this->getSkewY());
1673 const SkScalar sy = SkVector::Length(this->getSkewX(), this->getScaleY());
1674 if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) ||
1675 SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy)) {
1676 return false;
1677 }
1678
1679 if (scale) {
1680 scale->set(sx, sy);
1681 }
1682 if (remaining) {
1683 *remaining = *this;
1684 remaining->postScale(SkScalarInvert(sx), SkScalarInvert(sy));
1685 }
1686 return true;
1687}
1688
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001689///////////////////////////////////////////////////////////////////////////////
1690
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001691size_t SkMatrix::writeToMemory(void* buffer) const {
reed@android.com0ad336f2009-06-29 16:02:20 +00001692 // TODO write less for simple matrices
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001693 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
reed@android.com0ad336f2009-06-29 16:02:20 +00001694 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001695 memcpy(buffer, fMat, sizeInMemory);
reed@android.com0ad336f2009-06-29 16:02:20 +00001696 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001697 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001698}
1699
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001700size_t SkMatrix::readFromMemory(const void* buffer, size_t length) {
1701 static const size_t sizeInMemory = 9 * sizeof(SkScalar);
1702 if (length < sizeInMemory) {
1703 return 0;
1704 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001705 if (buffer) {
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001706 memcpy(fMat, buffer, sizeInMemory);
reed@android.comf2b98d62010-12-20 18:26:13 +00001707 this->setTypeMask(kUnknown_Mask);
1708 }
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001709 return sizeInMemory;
reed@android.com0ad336f2009-06-29 16:02:20 +00001710}
1711
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712void SkMatrix::dump() const {
1713 SkString str;
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001714 this->toString(&str);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715 SkDebugf("%s\n", str.c_str());
1716}
1717
robertphillips@google.com76f9e932013-01-15 20:17:47 +00001718void SkMatrix::toString(SkString* str) const {
1719 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 +00001720 fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
1721 fMat[6], fMat[7], fMat[8]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722}
reed@google.comad514302013-01-02 20:19:45 +00001723
1724///////////////////////////////////////////////////////////////////////////////
1725
1726#include "SkMatrixUtils.h"
1727
fmalitac7e211a2016-01-07 10:34:46 -08001728bool SkTreatAsSprite(const SkMatrix& mat, const SkISize& size, const SkPaint& paint) {
1729 // Our path aa is 2-bits, and our rect aa is 8, so we could use 8,
1730 // but in practice 4 seems enough (still looks smooth) and allows
1731 // more slightly fractional cases to fall into the fast (sprite) case.
1732 static const unsigned kAntiAliasSubpixelBits = 4;
1733
1734 const unsigned subpixelBits = paint.isAntiAlias() ? kAntiAliasSubpixelBits : 0;
1735
reed@google.comae573582013-01-03 15:22:40 +00001736 // quick reject on affine or perspective
reed@google.comad514302013-01-02 20:19:45 +00001737 if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
1738 return false;
1739 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001740
reed@google.comad514302013-01-02 20:19:45 +00001741 // quick success check
1742 if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
1743 return true;
1744 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001745
reed@google.comad514302013-01-02 20:19:45 +00001746 // mapRect supports negative scales, so we eliminate those first
1747 if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
1748 return false;
1749 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001750
reed@google.comad514302013-01-02 20:19:45 +00001751 SkRect dst;
fmalitac7e211a2016-01-07 10:34:46 -08001752 SkIRect isrc = SkIRect::MakeSize(size);
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00001753
reed@google.comad514302013-01-02 20:19:45 +00001754 {
reed@google.comae573582013-01-03 15:22:40 +00001755 SkRect src;
1756 src.set(isrc);
1757 mat.mapRect(&dst, src);
reed@google.comad514302013-01-02 20:19:45 +00001758 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001759
reed@google.comae573582013-01-03 15:22:40 +00001760 // just apply the translate to isrc
1761 isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
1762 SkScalarRoundToInt(mat.getTranslateY()));
1763
reed@google.comad514302013-01-02 20:19:45 +00001764 if (subpixelBits) {
fmalitac7e211a2016-01-07 10:34:46 -08001765 isrc.fLeft = SkLeftShift(isrc.fLeft, subpixelBits);
1766 isrc.fTop = SkLeftShift(isrc.fTop, subpixelBits);
1767 isrc.fRight = SkLeftShift(isrc.fRight, subpixelBits);
1768 isrc.fBottom = SkLeftShift(isrc.fBottom, subpixelBits);
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001769
reed@google.comad514302013-01-02 20:19:45 +00001770 const float scale = 1 << subpixelBits;
1771 dst.fLeft *= scale;
1772 dst.fTop *= scale;
1773 dst.fRight *= scale;
1774 dst.fBottom *= scale;
1775 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001776
reed@google.comae573582013-01-03 15:22:40 +00001777 SkIRect idst;
reed@google.comad514302013-01-02 20:19:45 +00001778 dst.round(&idst);
1779 return isrc == idst;
1780}
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001781
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001782// A square matrix M can be decomposed (via polar decomposition) into two matrices --
1783// an orthogonal matrix Q and a symmetric matrix S. In turn we can decompose S into U*W*U^T,
1784// where U is another orthogonal matrix and W is a scale matrix. These can be recombined
1785// to give M = (Q*U)*W*U^T, i.e., the product of two orthogonal matrices and a scale matrix.
1786//
1787// The one wrinkle is that traditionally Q may contain a reflection -- the
1788// calculation has been rejiggered to put that reflection into W.
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001789bool SkDecomposeUpper2x2(const SkMatrix& matrix,
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001790 SkPoint* rotation1,
1791 SkPoint* scale,
1792 SkPoint* rotation2) {
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001793
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001794 SkScalar A = matrix[SkMatrix::kMScaleX];
1795 SkScalar B = matrix[SkMatrix::kMSkewX];
1796 SkScalar C = matrix[SkMatrix::kMSkewY];
1797 SkScalar D = matrix[SkMatrix::kMScaleY];
1798
commit-bot@chromium.org4dcd0622013-07-29 14:43:31 +00001799 if (is_degenerate_2x2(A, B, C, D)) {
1800 return false;
1801 }
1802
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001803 double w1, w2;
1804 SkScalar cos1, sin1;
1805 SkScalar cos2, sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001806
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001807 // do polar decomposition (M = Q*S)
1808 SkScalar cosQ, sinQ;
1809 double Sa, Sb, Sd;
1810 // if M is already symmetric (i.e., M = I*S)
1811 if (SkScalarNearlyEqual(B, C)) {
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001812 cosQ = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001813 sinQ = 0;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001814
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001815 Sa = A;
1816 Sb = B;
1817 Sd = D;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001818 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001819 cosQ = A + D;
1820 sinQ = C - B;
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001821 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cosQ*cosQ + sinQ*sinQ));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001822 cosQ *= reciplen;
1823 sinQ *= reciplen;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001824
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001825 // S = Q^-1*M
1826 // we don't calc Sc since it's symmetric
1827 Sa = A*cosQ + C*sinQ;
1828 Sb = B*cosQ + D*sinQ;
1829 Sd = -B*sinQ + D*cosQ;
1830 }
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +00001831
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001832 // Now we need to compute eigenvalues of S (our scale factors)
1833 // and eigenvectors (bases for our rotation)
1834 // From this, should be able to reconstruct S as U*W*U^T
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001835 if (SkScalarNearlyZero(SkDoubleToScalar(Sb))) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001836 // already diagonalized
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001837 cos1 = 1;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001838 sin1 = 0;
1839 w1 = Sa;
1840 w2 = Sd;
1841 cos2 = cosQ;
1842 sin2 = sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001843 } else {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001844 double diff = Sa - Sd;
1845 double discriminant = sqrt(diff*diff + 4.0*Sb*Sb);
1846 double trace = Sa + Sd;
1847 if (diff > 0) {
1848 w1 = 0.5*(trace + discriminant);
1849 w2 = 0.5*(trace - discriminant);
1850 } else {
1851 w1 = 0.5*(trace - discriminant);
1852 w2 = 0.5*(trace + discriminant);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001853 }
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001854
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001855 cos1 = SkDoubleToScalar(Sb); sin1 = SkDoubleToScalar(w1 - Sa);
commit-bot@chromium.org0b9ada12014-01-30 22:13:12 +00001856 SkScalar reciplen = SkScalarInvert(SkScalarSqrt(cos1*cos1 + sin1*sin1));
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001857 cos1 *= reciplen;
1858 sin1 *= reciplen;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001859
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001860 // rotation 2 is composition of Q and U
1861 cos2 = cos1*cosQ - sin1*sinQ;
1862 sin2 = sin1*cosQ + cos1*sinQ;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +00001863
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001864 // rotation 1 is U^T
1865 sin1 = -sin1;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001866 }
1867
bsalomon49f085d2014-09-05 13:34:00 -07001868 if (scale) {
jvanverth@google.com25f72ed2013-09-03 19:46:16 +00001869 scale->fX = SkDoubleToScalar(w1);
1870 scale->fY = SkDoubleToScalar(w2);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001871 }
bsalomon49f085d2014-09-05 13:34:00 -07001872 if (rotation1) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001873 rotation1->fX = cos1;
1874 rotation1->fY = sin1;
1875 }
bsalomon49f085d2014-09-05 13:34:00 -07001876 if (rotation2) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +00001877 rotation2->fX = cos2;
1878 rotation2->fY = sin2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +00001879 }
1880
1881 return true;
1882}
reed71c3c762015-06-24 10:29:17 -07001883
1884//////////////////////////////////////////////////////////////////////////////////////////////////
1885
1886void SkRSXform::toQuad(SkScalar width, SkScalar height, SkPoint quad[4]) const {
reedca109532015-06-25 16:25:25 -07001887#if 0
1888 // This is the slow way, but it documents what we're doing
reed71c3c762015-06-24 10:29:17 -07001889 quad[0].set(0, 0);
1890 quad[1].set(width, 0);
1891 quad[2].set(width, height);
1892 quad[3].set(0, height);
1893 SkMatrix m;
1894 m.setRSXform(*this).mapPoints(quad, quad, 4);
reedca109532015-06-25 16:25:25 -07001895#else
1896 const SkScalar m00 = fSCos;
1897 const SkScalar m01 = -fSSin;
1898 const SkScalar m02 = fTx;
1899 const SkScalar m10 = -m01;
1900 const SkScalar m11 = m00;
1901 const SkScalar m12 = fTy;
1902
1903 quad[0].set(m02, m12);
1904 quad[1].set(m00 * width + m02, m10 * width + m12);
1905 quad[2].set(m00 * width + m01 * height + m02, m10 * width + m11 * height + m12);
1906 quad[3].set(m01 * height + m02, m11 * height + m12);
1907#endif
reed71c3c762015-06-24 10:29:17 -07001908}