blob: 6cccc39ecea640d7b0e2fbf492457c4f513dbd8f [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkMatrix.h"
11#include "Sk64.h"
12#include "SkFloatBits.h"
reed@android.com31745582009-07-08 14:46:11 +000013#include "SkScalarCompare.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkString.h"
15
16#ifdef SK_SCALAR_IS_FLOAT
17 #define kMatrix22Elem SK_Scalar1
reed@android.comab7ac022009-09-18 13:38:43 +000018
19 static inline float SkDoubleToFloat(double x) {
20 return static_cast<float>(x);
21 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#else
23 #define kMatrix22Elem SK_Fract1
24#endif
25
26/* [scale-x skew-x trans-x] [X] [X']
27 [skew-y scale-y trans-y] * [Y] = [Y']
28 [persp-0 persp-1 persp-2] [1] [1 ]
29*/
30
31void SkMatrix::reset() {
32 fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +000033 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +000034 fMat[kMTransX] = fMat[kMTransY] =
35 fMat[kMPersp0] = fMat[kMPersp1] = 0;
36 fMat[kMPersp2] = kMatrix22Elem;
37
38 this->setTypeMask(kIdentity_Mask | kRectStaysRect_Mask);
39}
40
reed@android.com8a1c16f2008-12-17 15:59:43 +000041// this guy aligns with the masks, so we can compute a mask from a varaible 0/1
42enum {
43 kTranslate_Shift,
44 kScale_Shift,
45 kAffine_Shift,
46 kPerspective_Shift,
47 kRectStaysRect_Shift
48};
49
50#ifdef SK_SCALAR_IS_FLOAT
51 static const int32_t kScalar1Int = 0x3f800000;
52 static const int32_t kPersp1Int = 0x3f800000;
53#else
54 #define scalarAsInt(x) (x)
55 static const int32_t kScalar1Int = (1 << 16);
56 static const int32_t kPersp1Int = (1 << 30);
57#endif
58
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000059uint8_t SkMatrix::computePerspectiveTypeMask() const {
junov@chromium.org6fc56992012-07-12 14:01:32 +000060#ifdef SK_SCALAR_SLOW_COMPARES
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000061 if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
62 SkScalarAs2sCompliment(fMat[kMPersp1]) |
63 (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000064 return SkToU8(kORableMasks);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000065 }
junov@chromium.org6fc56992012-07-12 14:01:32 +000066#else
67 // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
68 // is a win, but replacing those below is not. We don't yet understand
69 // that result.
70 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 ||
71 fMat[kMPersp2] != kMatrix22Elem) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +000072 // If this is a perspective transform, we return true for all other
bsalomon@google.com19263b12012-07-13 13:36:38 +000073 // transform flags - this does not disable any optimizations, respects
rmistry@google.comfbfcd562012-08-23 18:09:54 +000074 // the rule that the type mask must be conservative, and speeds up
junov@chromium.org6fc56992012-07-12 14:01:32 +000075 // type mask computation.
76 return SkToU8(kORableMasks);
77 }
78#endif
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000079
junov@chromium.org6fc56992012-07-12 14:01:32 +000080 return SkToU8(kOnlyPerspectiveValid_Mask | kUnknown_Mask);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000081}
82
reed@android.com8a1c16f2008-12-17 15:59:43 +000083uint8_t SkMatrix::computeTypeMask() const {
84 unsigned mask = 0;
85
tomhudson@google.comac385252011-06-06 15:18:28 +000086#ifdef SK_SCALAR_SLOW_COMPARES
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
88 SkScalarAs2sCompliment(fMat[kMPersp1]) |
89 (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
junov@chromium.org6fc56992012-07-12 14:01:32 +000090 return SkToU8(kORableMasks);
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 }
tomhudson@google.comac385252011-06-06 15:18:28 +000092
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 if (SkScalarAs2sCompliment(fMat[kMTransX]) |
94 SkScalarAs2sCompliment(fMat[kMTransY])) {
95 mask |= kTranslate_Mask;
96 }
tomhudson@google.comac385252011-06-06 15:18:28 +000097#else
tomhudson@google.comac385252011-06-06 15:18:28 +000098 if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 ||
tomhudson@google.com521ed7c2011-06-06 17:21:44 +000099 fMat[kMPersp2] != kMatrix22Elem) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000100 // Once it is determined that that this is a perspective transform,
101 // all other flags are moot as far as optimizations are concerned.
102 return SkToU8(kORableMasks);
tomhudson@google.comac385252011-06-06 15:18:28 +0000103 }
104
105 if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
106 mask |= kTranslate_Mask;
107 }
108#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109
110 int m00 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleX]);
111 int m01 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewX]);
112 int m10 = SkScalarAs2sCompliment(fMat[SkMatrix::kMSkewY]);
113 int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
tomhudson@google.comac385252011-06-06 15:18:28 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 if (m01 | m10) {
junov@chromium.org6fc56992012-07-12 14:01:32 +0000116 // The skew components may be scale-inducing, unless we are dealing
117 // with a pure rotation. Testing for a pure rotation is expensive,
118 // so we opt for being conservative by always setting the scale bit.
119 // along with affine.
120 // By doing this, we are also ensuring that matrices have the same
121 // type masks as their inverses.
122 mask |= kAffine_Mask | kScale_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
junov@chromium.org6fc56992012-07-12 14:01:32 +0000124 // For rectStaysRect, in the affine case, we only need check that
125 // the primary diagonal is all zeros and that the secondary diagonal
126 // is all non-zero.
tomhudson@google.comac385252011-06-06 15:18:28 +0000127
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 // map non-zero to 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 m01 = m01 != 0;
130 m10 = m10 != 0;
tomhudson@google.comac385252011-06-06 15:18:28 +0000131
junov@chromium.org6fc56992012-07-12 14:01:32 +0000132 int dp0 = 0 == (m00 | m11) ; // true if both are 0
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 int ds1 = m01 & m10; // true if both are 1
tomhudson@google.comac385252011-06-06 15:18:28 +0000134
junov@chromium.org6fc56992012-07-12 14:01:32 +0000135 mask |= (dp0 & ds1) << kRectStaysRect_Shift;
136 } else {
137 // Only test for scale explicitly if not affine, since affine sets the
138 // scale bit.
139 if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
140 mask |= kScale_Mask;
141 }
142
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000143 // Not affine, therefore we already know secondary diagonal is
junov@chromium.org6fc56992012-07-12 14:01:32 +0000144 // all zeros, so we just need to check that primary diagonal is
145 // all non-zero.
146
147 // map non-zero to 1
148 m00 = m00 != 0;
149 m11 = m11 != 0;
150
151 // record if the (p)rimary diagonal is all non-zero
152 mask |= (m00 & m11) << kRectStaysRect_Shift;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 }
154
155 return SkToU8(mask);
156}
157
158///////////////////////////////////////////////////////////////////////////////
159
reed@google.com3fb51872011-06-01 15:11:22 +0000160#ifdef SK_SCALAR_IS_FLOAT
161
162bool operator==(const SkMatrix& a, const SkMatrix& b) {
163 const SkScalar* SK_RESTRICT ma = a.fMat;
164 const SkScalar* SK_RESTRICT mb = b.fMat;
165
166 return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
167 ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
168 ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
169}
170
171#endif
172
173///////////////////////////////////////////////////////////////////////////////
174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
reed@android.com31745582009-07-08 14:46:11 +0000176 if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 fMat[kMTransX] = dx;
178 fMat[kMTransY] = dy;
179
180 fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000181 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 fMat[kMPersp0] = fMat[kMPersp1] = 0;
183 fMat[kMPersp2] = kMatrix22Elem;
184
185 this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
186 } else {
187 this->reset();
188 }
189}
190
191bool SkMatrix::preTranslate(SkScalar dx, SkScalar dy) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000192 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 SkMatrix m;
194 m.setTranslate(dx, dy);
195 return this->preConcat(m);
196 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000197
reed@android.com31745582009-07-08 14:46:11 +0000198 if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) +
200 SkScalarMul(fMat[kMSkewX], dy);
201 fMat[kMTransY] += SkScalarMul(fMat[kMSkewY], dx) +
202 SkScalarMul(fMat[kMScaleY], dy);
203
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000204 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 }
206 return true;
207}
208
209bool SkMatrix::postTranslate(SkScalar dx, SkScalar dy) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000210 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkMatrix m;
212 m.setTranslate(dx, dy);
213 return this->postConcat(m);
214 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000215
reed@android.com31745582009-07-08 14:46:11 +0000216 if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 fMat[kMTransX] += dx;
218 fMat[kMTransY] += dy;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000219 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 }
221 return true;
222}
223
224///////////////////////////////////////////////////////////////////////////////
225
226void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@google.comf244f902011-09-06 21:02:36 +0000227 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
228 this->reset();
229 } else {
230 fMat[kMScaleX] = sx;
231 fMat[kMScaleY] = sy;
232 fMat[kMTransX] = px - SkScalarMul(sx, px);
233 fMat[kMTransY] = py - SkScalarMul(sy, py);
234 fMat[kMPersp2] = kMatrix22Elem;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000236 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000237 fMat[kMPersp0] = fMat[kMPersp1] = 0;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000238
reed@google.comf244f902011-09-06 21:02:36 +0000239 this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
240 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241}
242
243void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000244 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
245 this->reset();
246 } else {
247 fMat[kMScaleX] = sx;
248 fMat[kMScaleY] = sy;
249 fMat[kMPersp2] = kMatrix22Elem;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
reed@google.comf244f902011-09-06 21:02:36 +0000251 fMat[kMTransX] = fMat[kMTransY] =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000252 fMat[kMSkewX] = fMat[kMSkewY] =
reed@google.comf244f902011-09-06 21:02:36 +0000253 fMat[kMPersp0] = fMat[kMPersp1] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254
reed@google.comf244f902011-09-06 21:02:36 +0000255 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
256 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257}
258
bsalomon@google.com5c638652011-07-18 19:31:59 +0000259bool SkMatrix::setIDiv(int divx, int divy) {
260 if (!divx || !divy) {
261 return false;
262 }
263 this->setScale(SK_Scalar1 / divx, SK_Scalar1 / divy);
264 return true;
265}
266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
268 SkMatrix m;
269 m.setScale(sx, sy, px, py);
270 return this->preConcat(m);
271}
272
273bool SkMatrix::preScale(SkScalar sx, SkScalar sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000274 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
275 return true;
276 }
277
reed@google.com3fb51872011-06-01 15:11:22 +0000278#ifdef SK_SCALAR_IS_FIXED
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 SkMatrix m;
280 m.setScale(sx, sy);
281 return this->preConcat(m);
reed@google.com3fb51872011-06-01 15:11:22 +0000282#else
283 // the assumption is that these multiplies are very cheap, and that
284 // a full concat and/or just computing the matrix type is more expensive.
285 // Also, the fixed-point case checks for overflow, but the float doesn't,
286 // so we can get away with these blind multiplies.
287
288 fMat[kMScaleX] = SkScalarMul(fMat[kMScaleX], sx);
289 fMat[kMSkewY] = SkScalarMul(fMat[kMSkewY], sx);
290 fMat[kMPersp0] = SkScalarMul(fMat[kMPersp0], sx);
291
292 fMat[kMSkewX] = SkScalarMul(fMat[kMSkewX], sy);
293 fMat[kMScaleY] = SkScalarMul(fMat[kMScaleY], sy);
294 fMat[kMPersp1] = SkScalarMul(fMat[kMPersp1], sy);
295
296 this->orTypeMask(kScale_Mask);
297 return true;
298#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299}
300
301bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
reed@google.comf244f902011-09-06 21:02:36 +0000302 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
303 return true;
304 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 SkMatrix m;
306 m.setScale(sx, sy, px, py);
307 return this->postConcat(m);
308}
309
310bool SkMatrix::postScale(SkScalar sx, SkScalar sy) {
reed@google.comf244f902011-09-06 21:02:36 +0000311 if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
312 return true;
313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 SkMatrix m;
315 m.setScale(sx, sy);
316 return this->postConcat(m);
317}
318
319#ifdef SK_SCALAR_IS_FIXED
320 static inline SkFixed roundidiv(SkFixed numer, int denom) {
321 int ns = numer >> 31;
322 int ds = denom >> 31;
323 numer = (numer ^ ns) - ns;
324 denom = (denom ^ ds) - ds;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000325
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 SkFixed answer = (numer + (denom >> 1)) / denom;
327 int as = ns ^ ds;
328 return (answer ^ as) - as;
329 }
330#endif
331
332// this guy perhaps can go away, if we have a fract/high-precision way to
333// scale matrices
334bool SkMatrix::postIDiv(int divx, int divy) {
335 if (divx == 0 || divy == 0) {
336 return false;
337 }
338
339#ifdef SK_SCALAR_IS_FIXED
340 fMat[kMScaleX] = roundidiv(fMat[kMScaleX], divx);
341 fMat[kMSkewX] = roundidiv(fMat[kMSkewX], divx);
342 fMat[kMTransX] = roundidiv(fMat[kMTransX], divx);
343
344 fMat[kMScaleY] = roundidiv(fMat[kMScaleY], divy);
345 fMat[kMSkewY] = roundidiv(fMat[kMSkewY], divy);
346 fMat[kMTransY] = roundidiv(fMat[kMTransY], divy);
347#else
348 const float invX = 1.f / divx;
349 const float invY = 1.f / divy;
350
351 fMat[kMScaleX] *= invX;
352 fMat[kMSkewX] *= invX;
353 fMat[kMTransX] *= invX;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000354
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 fMat[kMScaleY] *= invY;
356 fMat[kMSkewY] *= invY;
357 fMat[kMTransY] *= invY;
358#endif
359
360 this->setTypeMask(kUnknown_Mask);
361 return true;
362}
363
364////////////////////////////////////////////////////////////////////////////////////
365
366void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV,
367 SkScalar px, SkScalar py) {
368 const SkScalar oneMinusCosV = SK_Scalar1 - cosV;
369
370 fMat[kMScaleX] = cosV;
371 fMat[kMSkewX] = -sinV;
372 fMat[kMTransX] = SkScalarMul(sinV, py) + SkScalarMul(oneMinusCosV, px);
373
374 fMat[kMSkewY] = sinV;
375 fMat[kMScaleY] = cosV;
376 fMat[kMTransY] = SkScalarMul(-sinV, px) + SkScalarMul(oneMinusCosV, py);
377
378 fMat[kMPersp0] = fMat[kMPersp1] = 0;
379 fMat[kMPersp2] = kMatrix22Elem;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000380
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000381 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382}
383
384void SkMatrix::setSinCos(SkScalar sinV, SkScalar cosV) {
385 fMat[kMScaleX] = cosV;
386 fMat[kMSkewX] = -sinV;
387 fMat[kMTransX] = 0;
388
389 fMat[kMSkewY] = sinV;
390 fMat[kMScaleY] = cosV;
391 fMat[kMTransY] = 0;
392
393 fMat[kMPersp0] = fMat[kMPersp1] = 0;
394 fMat[kMPersp2] = kMatrix22Elem;
395
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000396 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397}
398
399void SkMatrix::setRotate(SkScalar degrees, SkScalar px, SkScalar py) {
400 SkScalar sinV, cosV;
401 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
402 this->setSinCos(sinV, cosV, px, py);
403}
404
405void SkMatrix::setRotate(SkScalar degrees) {
406 SkScalar sinV, cosV;
407 sinV = SkScalarSinCos(SkDegreesToRadians(degrees), &cosV);
408 this->setSinCos(sinV, cosV);
409}
410
411bool SkMatrix::preRotate(SkScalar degrees, SkScalar px, SkScalar py) {
412 SkMatrix m;
413 m.setRotate(degrees, px, py);
414 return this->preConcat(m);
415}
416
417bool SkMatrix::preRotate(SkScalar degrees) {
418 SkMatrix m;
419 m.setRotate(degrees);
420 return this->preConcat(m);
421}
422
423bool SkMatrix::postRotate(SkScalar degrees, SkScalar px, SkScalar py) {
424 SkMatrix m;
425 m.setRotate(degrees, px, py);
426 return this->postConcat(m);
427}
428
429bool SkMatrix::postRotate(SkScalar degrees) {
430 SkMatrix m;
431 m.setRotate(degrees);
432 return this->postConcat(m);
433}
434
435////////////////////////////////////////////////////////////////////////////////////
436
437void SkMatrix::setSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
438 fMat[kMScaleX] = SK_Scalar1;
439 fMat[kMSkewX] = sx;
440 fMat[kMTransX] = SkScalarMul(-sx, py);
441
442 fMat[kMSkewY] = sy;
443 fMat[kMScaleY] = SK_Scalar1;
444 fMat[kMTransY] = SkScalarMul(-sy, px);
445
446 fMat[kMPersp0] = fMat[kMPersp1] = 0;
447 fMat[kMPersp2] = kMatrix22Elem;
448
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000449 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450}
451
452void SkMatrix::setSkew(SkScalar sx, SkScalar sy) {
453 fMat[kMScaleX] = SK_Scalar1;
454 fMat[kMSkewX] = sx;
455 fMat[kMTransX] = 0;
456
457 fMat[kMSkewY] = sy;
458 fMat[kMScaleY] = SK_Scalar1;
459 fMat[kMTransY] = 0;
460
461 fMat[kMPersp0] = fMat[kMPersp1] = 0;
462 fMat[kMPersp2] = kMatrix22Elem;
463
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000464 this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465}
466
467bool SkMatrix::preSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
468 SkMatrix m;
469 m.setSkew(sx, sy, px, py);
470 return this->preConcat(m);
471}
472
473bool SkMatrix::preSkew(SkScalar sx, SkScalar sy) {
474 SkMatrix m;
475 m.setSkew(sx, sy);
476 return this->preConcat(m);
477}
478
479bool SkMatrix::postSkew(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
480 SkMatrix m;
481 m.setSkew(sx, sy, px, py);
482 return this->postConcat(m);
483}
484
485bool SkMatrix::postSkew(SkScalar sx, SkScalar sy) {
486 SkMatrix m;
487 m.setSkew(sx, sy);
488 return this->postConcat(m);
489}
490
491///////////////////////////////////////////////////////////////////////////////
492
493bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst,
494 ScaleToFit align)
495{
496 if (src.isEmpty()) {
497 this->reset();
498 return false;
499 }
500
501 if (dst.isEmpty()) {
reed@android.com4516f472009-06-29 16:25:36 +0000502 sk_bzero(fMat, 8 * sizeof(SkScalar));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
504 } else {
505 SkScalar tx, sx = SkScalarDiv(dst.width(), src.width());
506 SkScalar ty, sy = SkScalarDiv(dst.height(), src.height());
507 bool xLarger = false;
508
509 if (align != kFill_ScaleToFit) {
510 if (sx > sy) {
511 xLarger = true;
512 sx = sy;
513 } else {
514 sy = sx;
515 }
516 }
517
518 tx = dst.fLeft - SkScalarMul(src.fLeft, sx);
519 ty = dst.fTop - SkScalarMul(src.fTop, sy);
520 if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) {
521 SkScalar diff;
522
523 if (xLarger) {
524 diff = dst.width() - SkScalarMul(src.width(), sy);
525 } else {
526 diff = dst.height() - SkScalarMul(src.height(), sy);
527 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 if (align == kCenter_ScaleToFit) {
530 diff = SkScalarHalf(diff);
531 }
532
533 if (xLarger) {
534 tx += diff;
535 } else {
536 ty += diff;
537 }
538 }
539
540 fMat[kMScaleX] = sx;
541 fMat[kMScaleY] = sy;
542 fMat[kMTransX] = tx;
543 fMat[kMTransY] = ty;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000544 fMat[kMSkewX] = fMat[kMSkewY] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 fMat[kMPersp0] = fMat[kMPersp1] = 0;
546
reed@google.com97cd69c2012-10-12 14:35:48 +0000547 unsigned mask = kRectStaysRect_Mask;
548 if (sx != SK_Scalar1 || sy != SK_Scalar1) {
549 mask |= kScale_Mask;
550 }
551 if (tx || ty) {
552 mask |= kTranslate_Mask;
553 }
554 this->setTypeMask(mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 }
556 // shared cleanup
557 fMat[kMPersp2] = kMatrix22Elem;
558 return true;
559}
560
561///////////////////////////////////////////////////////////////////////////////
562
563#ifdef SK_SCALAR_IS_FLOAT
564 static inline int fixmuladdmul(float a, float b, float c, float d,
565 float* result) {
reed@android.comab7ac022009-09-18 13:38:43 +0000566 *result = SkDoubleToFloat((double)a * b + (double)c * d);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 return true;
568 }
569
570 static inline bool rowcol3(const float row[], const float col[],
571 float* result) {
572 *result = row[0] * col[0] + row[1] * col[3] + row[2] * col[6];
573 return true;
574 }
575
576 static inline int negifaddoverflows(float& result, float a, float b) {
577 result = a + b;
578 return 0;
579 }
580#else
581 static inline bool fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d,
582 SkFixed* result) {
583 Sk64 tmp1, tmp2;
584 tmp1.setMul(a, b);
585 tmp2.setMul(c, d);
586 tmp1.add(tmp2);
587 if (tmp1.isFixed()) {
588 *result = tmp1.getFixed();
589 return true;
590 }
591 return false;
592 }
593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 static inline SkFixed fracmuladdmul(SkFixed a, SkFract b, SkFixed c,
595 SkFract d) {
596 Sk64 tmp1, tmp2;
597 tmp1.setMul(a, b);
598 tmp2.setMul(c, d);
599 tmp1.add(tmp2);
600 return tmp1.getFract();
601 }
602
603 static inline bool rowcol3(const SkFixed row[], const SkFixed col[],
604 SkFixed* result) {
605 Sk64 tmp1, tmp2;
606
607 tmp1.setMul(row[0], col[0]); // N * fixed
608 tmp2.setMul(row[1], col[3]); // N * fixed
609 tmp1.add(tmp2);
610
611 tmp2.setMul(row[2], col[6]); // N * fract
612 tmp2.roundRight(14); // make it fixed
613 tmp1.add(tmp2);
614
615 if (tmp1.isFixed()) {
616 *result = tmp1.getFixed();
617 return true;
618 }
619 return false;
620 }
621
622 static inline int negifaddoverflows(SkFixed& result, SkFixed a, SkFixed b) {
623 SkFixed c = a + b;
624 result = c;
625 return (c ^ a) & (c ^ b);
626 }
627#endif
628
629static void normalize_perspective(SkScalar mat[9]) {
630 if (SkScalarAbs(mat[SkMatrix::kMPersp2]) > kMatrix22Elem) {
631 for (int i = 0; i < 9; i++)
632 mat[i] = SkScalarHalf(mat[i]);
633 }
634}
635
636bool SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000637 TypeMask aType = a.getPerspectiveTypeMaskOnly();
638 TypeMask bType = b.getPerspectiveTypeMaskOnly();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000640 if (a.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 *this = b;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000642 } else if (b.isTriviallyIdentity()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 *this = a;
644 } else {
645 SkMatrix tmp;
646
647 if ((aType | bType) & kPerspective_Mask) {
648 if (!rowcol3(&a.fMat[0], &b.fMat[0], &tmp.fMat[kMScaleX])) {
649 return false;
650 }
651 if (!rowcol3(&a.fMat[0], &b.fMat[1], &tmp.fMat[kMSkewX])) {
652 return false;
653 }
654 if (!rowcol3(&a.fMat[0], &b.fMat[2], &tmp.fMat[kMTransX])) {
655 return false;
656 }
657
658 if (!rowcol3(&a.fMat[3], &b.fMat[0], &tmp.fMat[kMSkewY])) {
659 return false;
660 }
661 if (!rowcol3(&a.fMat[3], &b.fMat[1], &tmp.fMat[kMScaleY])) {
662 return false;
663 }
664 if (!rowcol3(&a.fMat[3], &b.fMat[2], &tmp.fMat[kMTransY])) {
665 return false;
666 }
667
668 if (!rowcol3(&a.fMat[6], &b.fMat[0], &tmp.fMat[kMPersp0])) {
669 return false;
670 }
671 if (!rowcol3(&a.fMat[6], &b.fMat[1], &tmp.fMat[kMPersp1])) {
672 return false;
673 }
674 if (!rowcol3(&a.fMat[6], &b.fMat[2], &tmp.fMat[kMPersp2])) {
675 return false;
676 }
677
678 normalize_perspective(tmp.fMat);
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000679 tmp.setTypeMask(kUnknown_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 } else { // not perspective
681 if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMScaleX],
682 a.fMat[kMSkewX], b.fMat[kMSkewY], &tmp.fMat[kMScaleX])) {
683 return false;
684 }
685 if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMSkewX],
686 a.fMat[kMSkewX], b.fMat[kMScaleY], &tmp.fMat[kMSkewX])) {
687 return false;
688 }
689 if (!fixmuladdmul(a.fMat[kMScaleX], b.fMat[kMTransX],
690 a.fMat[kMSkewX], b.fMat[kMTransY], &tmp.fMat[kMTransX])) {
691 return false;
692 }
693 if (negifaddoverflows(tmp.fMat[kMTransX], tmp.fMat[kMTransX],
694 a.fMat[kMTransX]) < 0) {
695 return false;
696 }
697
698 if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMScaleX],
699 a.fMat[kMScaleY], b.fMat[kMSkewY], &tmp.fMat[kMSkewY])) {
700 return false;
701 }
702 if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMSkewX],
703 a.fMat[kMScaleY], b.fMat[kMScaleY], &tmp.fMat[kMScaleY])) {
704 return false;
705 }
706 if (!fixmuladdmul(a.fMat[kMSkewY], b.fMat[kMTransX],
707 a.fMat[kMScaleY], b.fMat[kMTransY], &tmp.fMat[kMTransY])) {
708 return false;
709 }
710 if (negifaddoverflows(tmp.fMat[kMTransY], tmp.fMat[kMTransY],
711 a.fMat[kMTransY]) < 0) {
712 return false;
713 }
714
715 tmp.fMat[kMPersp0] = tmp.fMat[kMPersp1] = 0;
716 tmp.fMat[kMPersp2] = kMatrix22Elem;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000717 //SkDebugf("Concat mat non-persp type: %d\n", tmp.getType());
718 //SkASSERT(!(tmp.getType() & kPerspective_Mask));
719 tmp.setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 }
721 *this = tmp;
722 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 return true;
724}
725
726bool SkMatrix::preConcat(const SkMatrix& mat) {
727 // check for identity first, so we don't do a needless copy of ourselves
728 // to ourselves inside setConcat()
729 return mat.isIdentity() || this->setConcat(*this, mat);
730}
731
732bool SkMatrix::postConcat(const SkMatrix& mat) {
733 // check for identity first, so we don't do a needless copy of ourselves
734 // to ourselves inside setConcat()
735 return mat.isIdentity() || this->setConcat(mat, *this);
736}
737
738///////////////////////////////////////////////////////////////////////////////
739
reed@android.com0b9e2db2009-09-16 17:00:17 +0000740/* Matrix inversion is very expensive, but also the place where keeping
741 precision may be most important (here and matrix concat). Hence to avoid
742 bitmap blitting artifacts when walking the inverse, we use doubles for
743 the intermediate math, even though we know that is more expensive.
744 The fixed counter part is us using Sk64 for temp calculations.
745 */
746
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747#ifdef SK_SCALAR_IS_FLOAT
reed@android.com0b9e2db2009-09-16 17:00:17 +0000748 typedef double SkDetScalar;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 #define SkPerspMul(a, b) SkScalarMul(a, b)
reed@android.com0b9e2db2009-09-16 17:00:17 +0000750 #define SkScalarMulShift(a, b, s) SkDoubleToFloat((a) * (b))
751 static double sk_inv_determinant(const float mat[9], int isPerspective,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 int* /* (only used in Fixed case) */) {
753 double det;
754
755 if (isPerspective) {
756 det = mat[SkMatrix::kMScaleX] * ((double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp2] - (double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp1]) +
757 mat[SkMatrix::kMSkewX] * ((double)mat[SkMatrix::kMTransY] * mat[SkMatrix::kMPersp0] - (double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp2]) +
758 mat[SkMatrix::kMTransX] * ((double)mat[SkMatrix::kMSkewY] * mat[SkMatrix::kMPersp1] - (double)mat[SkMatrix::kMScaleY] * mat[SkMatrix::kMPersp0]);
759 } else {
760 det = (double)mat[SkMatrix::kMScaleX] * mat[SkMatrix::kMScaleY] - (double)mat[SkMatrix::kMSkewX] * mat[SkMatrix::kMSkewY];
761 }
762
senorblanco@chromium.org0e21ec02010-07-20 15:20:01 +0000763 // Since the determinant is on the order of the cube of the matrix members,
764 // compare to the cube of the default nearly-zero constant (although an
765 // estimate of the condition number would be better if it wasn't so expensive).
766 if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 return 0;
768 }
reed@android.com0b9e2db2009-09-16 17:00:17 +0000769 return 1.0 / det;
770 }
reed@android.com0b9e2db2009-09-16 17:00:17 +0000771 // we declar a,b,c,d to all be doubles, because we want to perform
772 // double-precision muls and subtract, even though the original values are
773 // from the matrix, which are floats.
774 static float inline mul_diff_scale(double a, double b, double c, double d,
775 double scale) {
776 return SkDoubleToFloat((a * b - c * d) * scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 }
778#else
reed@android.com0b9e2db2009-09-16 17:00:17 +0000779 typedef SkFixed SkDetScalar;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 #define SkPerspMul(a, b) SkFractMul(a, b)
781 #define SkScalarMulShift(a, b, s) SkMulShift(a, b, s)
782 static void set_muladdmul(Sk64* dst, int32_t a, int32_t b, int32_t c,
783 int32_t d) {
784 Sk64 tmp;
785 dst->setMul(a, b);
786 tmp.setMul(c, d);
787 dst->add(tmp);
788 }
789
790 static SkFixed sk_inv_determinant(const SkFixed mat[9], int isPerspective,
791 int* shift) {
792 Sk64 tmp1, tmp2;
793
794 if (isPerspective) {
795 tmp1.setMul(mat[SkMatrix::kMScaleX], fracmuladdmul(mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp2], -mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp1]));
796 tmp2.setMul(mat[SkMatrix::kMSkewX], fracmuladdmul(mat[SkMatrix::kMTransY], mat[SkMatrix::kMPersp0], -mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp2]));
797 tmp1.add(tmp2);
798 tmp2.setMul(mat[SkMatrix::kMTransX], fracmuladdmul(mat[SkMatrix::kMSkewY], mat[SkMatrix::kMPersp1], -mat[SkMatrix::kMScaleY], mat[SkMatrix::kMPersp0]));
799 tmp1.add(tmp2);
800 } else {
801 tmp1.setMul(mat[SkMatrix::kMScaleX], mat[SkMatrix::kMScaleY]);
802 tmp2.setMul(mat[SkMatrix::kMSkewX], mat[SkMatrix::kMSkewY]);
803 tmp1.sub(tmp2);
804 }
805
806 int s = tmp1.getClzAbs();
807 *shift = s;
808
809 SkFixed denom;
810 if (s <= 32) {
811 denom = tmp1.getShiftRight(33 - s);
812 } else {
813 denom = (int32_t)tmp1.fLo << (s - 33);
814 }
815
816 if (denom == 0) {
817 return 0;
818 }
819 /** This could perhaps be a special fractdiv function, since both of its
820 arguments are known to have bit 31 clear and bit 30 set (when they
821 are made positive), thus eliminating the need for calling clz()
822 */
823 return SkFractDiv(SK_Fract1, denom);
824 }
825#endif
826
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000827void SkMatrix::SetAffineIdentity(SkScalar affine[6]) {
828 affine[kAScaleX] = SK_Scalar1;
829 affine[kASkewY] = 0;
830 affine[kASkewX] = 0;
831 affine[kAScaleY] = SK_Scalar1;
832 affine[kATransX] = 0;
833 affine[kATransY] = 0;
834}
835
836bool SkMatrix::asAffine(SkScalar affine[6]) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000837 if (this->hasPerspective()) {
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000838 return false;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000839 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000840 if (affine) {
841 affine[kAScaleX] = this->fMat[kMScaleX];
842 affine[kASkewY] = this->fMat[kMSkewY];
843 affine[kASkewX] = this->fMat[kMSkewX];
844 affine[kAScaleY] = this->fMat[kMScaleY];
845 affine[kATransX] = this->fMat[kMTransX];
846 affine[kATransY] = this->fMat[kMTransY];
847 }
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000848 return true;
849}
850
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000851bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
852 SkASSERT(!this->isIdentity());
reed@google.com2fb96cc2013-01-04 17:02:33 +0000853
854 TypeMask mask = this->getType();
855
856 if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
857 if (inv) {
858 if (mask & kScale_Mask) {
859 SkScalar invX = fMat[kMScaleX];
860 SkScalar invY = fMat[kMScaleY];
861 if (0 == invX || 0 == invY) {
862 return false;
863 }
864 invX = SkScalarInvert(invX);
865 invY = SkScalarInvert(invY);
866
867 // Must be careful when writing to inv, since it may be the
868 // same memory as this.
869
870 inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
871 inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
872
873 inv->fMat[kMScaleX] = invX;
874 inv->fMat[kMScaleY] = invY;
875 inv->fMat[kMPersp2] = kMatrix22Elem;
876 inv->fMat[kMTransX] = -SkScalarMul(fMat[kMTransX], invX);
877 inv->fMat[kMTransY] = -SkScalarMul(fMat[kMTransY], invY);
878
879 inv->setTypeMask(mask | kRectStaysRect_Mask);
880 } else {
881 // translate only
882 inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
883 }
884 }
885 return true;
886 }
887
888 int isPersp = mask & kPerspective_Mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 int shift;
reed@android.com0b9e2db2009-09-16 17:00:17 +0000890 SkDetScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891
892 if (scale == 0) { // underflow
893 return false;
894 }
895
896 if (inv) {
897 SkMatrix tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000898 if (inv == this) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 inv = &tmp;
bsalomon@google.comcf9b7502011-08-01 13:26:01 +0000900 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901
902 if (isPersp) {
903 shift = 61 - shift;
904 inv->fMat[kMScaleX] = SkScalarMulShift(SkPerspMul(fMat[kMScaleY], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransY], fMat[kMPersp1]), scale, shift);
905 inv->fMat[kMSkewX] = SkScalarMulShift(SkPerspMul(fMat[kMTransX], fMat[kMPersp1]) - SkPerspMul(fMat[kMSkewX], fMat[kMPersp2]), scale, shift);
906 inv->fMat[kMTransX] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMTransY]) - SkScalarMul(fMat[kMTransX], fMat[kMScaleY]), scale, shift);
907
908 inv->fMat[kMSkewY] = SkScalarMulShift(SkPerspMul(fMat[kMTransY], fMat[kMPersp0]) - SkPerspMul(fMat[kMSkewY], fMat[kMPersp2]), scale, shift);
909 inv->fMat[kMScaleY] = SkScalarMulShift(SkPerspMul(fMat[kMScaleX], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransX], fMat[kMPersp0]), scale, shift);
910 inv->fMat[kMTransY] = SkScalarMulShift(SkScalarMul(fMat[kMTransX], fMat[kMSkewY]) - SkScalarMul(fMat[kMScaleX], fMat[kMTransY]), scale, shift);
911
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000912 inv->fMat[kMPersp0] = SkScalarMulShift(SkScalarMul(fMat[kMSkewY], fMat[kMPersp1]) - SkScalarMul(fMat[kMScaleY], fMat[kMPersp0]), scale, shift);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 inv->fMat[kMPersp1] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMPersp0]) - SkScalarMul(fMat[kMScaleX], fMat[kMPersp1]), scale, shift);
914 inv->fMat[kMPersp2] = SkScalarMulShift(SkScalarMul(fMat[kMScaleX], fMat[kMScaleY]) - SkScalarMul(fMat[kMSkewX], fMat[kMSkewY]), scale, shift);
915#ifdef SK_SCALAR_IS_FIXED
916 if (SkAbs32(inv->fMat[kMPersp2]) > SK_Fixed1) {
917 Sk64 tmp;
918
919 tmp.set(SK_Fract1);
920 tmp.shiftLeft(16);
921 tmp.div(inv->fMat[kMPersp2], Sk64::kRound_DivOption);
922
923 SkFract scale = tmp.get32();
924
925 for (int i = 0; i < 9; i++) {
926 inv->fMat[i] = SkFractMul(inv->fMat[i], scale);
927 }
928 }
929 inv->fMat[kMPersp2] = SkFixedToFract(inv->fMat[kMPersp2]);
930#endif
931 } else { // not perspective
932#ifdef SK_SCALAR_IS_FIXED
933 Sk64 tx, ty;
934 int clzNumer;
935
936 // check the 2x2 for overflow
937 {
938 int32_t value = SkAbs32(fMat[kMScaleY]);
939 value |= SkAbs32(fMat[kMSkewX]);
940 value |= SkAbs32(fMat[kMScaleX]);
941 value |= SkAbs32(fMat[kMSkewY]);
942 clzNumer = SkCLZ(value);
943 if (shift - clzNumer > 31)
944 return false; // overflow
945 }
946
947 set_muladdmul(&tx, fMat[kMSkewX], fMat[kMTransY], -fMat[kMScaleY], fMat[kMTransX]);
948 set_muladdmul(&ty, fMat[kMSkewY], fMat[kMTransX], -fMat[kMScaleX], fMat[kMTransY]);
949 // check tx,ty for overflow
950 clzNumer = SkCLZ(SkAbs32(tx.fHi) | SkAbs32(ty.fHi));
951 if (shift - clzNumer > 14) {
952 return false; // overflow
953 }
954
955 int fixedShift = 61 - shift;
956 int sk64shift = 44 - shift + clzNumer;
957
958 inv->fMat[kMScaleX] = SkMulShift(fMat[kMScaleY], scale, fixedShift);
959 inv->fMat[kMSkewX] = SkMulShift(-fMat[kMSkewX], scale, fixedShift);
960 inv->fMat[kMTransX] = SkMulShift(tx.getShiftRight(33 - clzNumer), scale, sk64shift);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000961
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 inv->fMat[kMSkewY] = SkMulShift(-fMat[kMSkewY], scale, fixedShift);
963 inv->fMat[kMScaleY] = SkMulShift(fMat[kMScaleX], scale, fixedShift);
964 inv->fMat[kMTransY] = SkMulShift(ty.getShiftRight(33 - clzNumer), scale, sk64shift);
965#else
reed@android.com0b9e2db2009-09-16 17:00:17 +0000966 inv->fMat[kMScaleX] = SkDoubleToFloat(fMat[kMScaleY] * scale);
967 inv->fMat[kMSkewX] = SkDoubleToFloat(-fMat[kMSkewX] * scale);
968 inv->fMat[kMTransX] = mul_diff_scale(fMat[kMSkewX], fMat[kMTransY],
969 fMat[kMScaleY], fMat[kMTransX], scale);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000970
reed@android.com0b9e2db2009-09-16 17:00:17 +0000971 inv->fMat[kMSkewY] = SkDoubleToFloat(-fMat[kMSkewY] * scale);
972 inv->fMat[kMScaleY] = SkDoubleToFloat(fMat[kMScaleX] * scale);
973 inv->fMat[kMTransY] = mul_diff_scale(fMat[kMSkewY], fMat[kMTransX],
974 fMat[kMScaleX], fMat[kMTransY], scale);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975#endif
976 inv->fMat[kMPersp0] = 0;
977 inv->fMat[kMPersp1] = 0;
978 inv->fMat[kMPersp2] = kMatrix22Elem;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 }
981
junov@chromium.org6fc56992012-07-12 14:01:32 +0000982 inv->setTypeMask(fTypeMask);
983
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 if (inv == &tmp) {
985 *(SkMatrix*)this = tmp;
986 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 }
988 return true;
989}
990
991///////////////////////////////////////////////////////////////////////////////
992
993void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
994 const SkPoint src[], int count) {
995 SkASSERT(m.getType() == 0);
996
997 if (dst != src && count > 0)
998 memcpy(dst, src, count * sizeof(SkPoint));
999}
1000
1001void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
1002 const SkPoint src[], int count) {
1003 SkASSERT(m.getType() == kTranslate_Mask);
1004
1005 if (count > 0) {
1006 SkScalar tx = m.fMat[kMTransX];
1007 SkScalar ty = m.fMat[kMTransY];
1008 do {
1009 dst->fY = src->fY + ty;
1010 dst->fX = src->fX + tx;
1011 src += 1;
1012 dst += 1;
1013 } while (--count);
1014 }
1015}
1016
1017void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
1018 const SkPoint src[], int count) {
1019 SkASSERT(m.getType() == kScale_Mask);
1020
1021 if (count > 0) {
1022 SkScalar mx = m.fMat[kMScaleX];
1023 SkScalar my = m.fMat[kMScaleY];
1024 do {
1025 dst->fY = SkScalarMul(src->fY, my);
1026 dst->fX = SkScalarMul(src->fX, mx);
1027 src += 1;
1028 dst += 1;
1029 } while (--count);
1030 }
1031}
1032
1033void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
1034 const SkPoint src[], int count) {
1035 SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
1036
1037 if (count > 0) {
1038 SkScalar mx = m.fMat[kMScaleX];
1039 SkScalar my = m.fMat[kMScaleY];
1040 SkScalar tx = m.fMat[kMTransX];
1041 SkScalar ty = m.fMat[kMTransY];
1042 do {
1043 dst->fY = SkScalarMulAdd(src->fY, my, ty);
1044 dst->fX = SkScalarMulAdd(src->fX, mx, tx);
1045 src += 1;
1046 dst += 1;
1047 } while (--count);
1048 }
1049}
1050
1051void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
1052 const SkPoint src[], int count) {
1053 SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
1054
1055 if (count > 0) {
1056 SkScalar mx = m.fMat[kMScaleX];
1057 SkScalar my = m.fMat[kMScaleY];
1058 SkScalar kx = m.fMat[kMSkewX];
1059 SkScalar ky = m.fMat[kMSkewY];
1060 do {
1061 SkScalar sy = src->fY;
1062 SkScalar sx = src->fX;
1063 src += 1;
1064 dst->fY = SkScalarMul(sx, ky) + SkScalarMul(sy, my);
1065 dst->fX = SkScalarMul(sx, mx) + SkScalarMul(sy, kx);
1066 dst += 1;
1067 } while (--count);
1068 }
1069}
1070
1071void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
1072 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001073 SkASSERT(!m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074
1075 if (count > 0) {
1076 SkScalar mx = m.fMat[kMScaleX];
1077 SkScalar my = m.fMat[kMScaleY];
1078 SkScalar kx = m.fMat[kMSkewX];
1079 SkScalar ky = m.fMat[kMSkewY];
1080 SkScalar tx = m.fMat[kMTransX];
1081 SkScalar ty = m.fMat[kMTransY];
1082 do {
1083 SkScalar sy = src->fY;
1084 SkScalar sx = src->fX;
1085 src += 1;
1086 dst->fY = SkScalarMul(sx, ky) + SkScalarMulAdd(sy, my, ty);
1087 dst->fX = SkScalarMul(sx, mx) + SkScalarMulAdd(sy, kx, tx);
1088 dst += 1;
1089 } while (--count);
1090 }
1091}
1092
1093void SkMatrix::Persp_pts(const SkMatrix& m, SkPoint dst[],
1094 const SkPoint src[], int count) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001095 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096
1097#ifdef SK_SCALAR_IS_FIXED
1098 SkFixed persp2 = SkFractToFixed(m.fMat[kMPersp2]);
1099#endif
1100
1101 if (count > 0) {
1102 do {
1103 SkScalar sy = src->fY;
1104 SkScalar sx = src->fX;
1105 src += 1;
1106
1107 SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
1108 SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1109 SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
1110 SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1111#ifdef SK_SCALAR_IS_FIXED
1112 SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
1113 SkFractMul(sy, m.fMat[kMPersp1]) + persp2;
1114#else
1115 float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
1116 SkScalarMulAdd(sy, m.fMat[kMPersp1], m.fMat[kMPersp2]);
1117#endif
1118 if (z) {
1119 z = SkScalarFastInvert(z);
1120 }
1121
1122 dst->fY = SkScalarMul(y, z);
1123 dst->fX = SkScalarMul(x, z);
1124 dst += 1;
1125 } while (--count);
1126 }
1127}
1128
1129const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
1130 SkMatrix::Identity_pts, SkMatrix::Trans_pts,
1131 SkMatrix::Scale_pts, SkMatrix::ScaleTrans_pts,
1132 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
1133 SkMatrix::Rot_pts, SkMatrix::RotTrans_pts,
1134 // repeat the persp proc 8 times
1135 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1136 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1137 SkMatrix::Persp_pts, SkMatrix::Persp_pts,
1138 SkMatrix::Persp_pts, SkMatrix::Persp_pts
1139};
1140
1141void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
1142 SkASSERT((dst && src && count > 0) || count == 0);
1143 // no partial overlap
1144 SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= count);
1145
1146 this->getMapPtsProc()(*this, dst, src, count);
1147}
1148
1149///////////////////////////////////////////////////////////////////////////////
1150
1151void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001152 if (this->hasPerspective()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 SkPoint origin;
1154
1155 MapXYProc proc = this->getMapXYProc();
1156 proc(*this, 0, 0, &origin);
1157
1158 for (int i = count - 1; i >= 0; --i) {
1159 SkPoint tmp;
1160
1161 proc(*this, src[i].fX, src[i].fY, &tmp);
1162 dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY);
1163 }
1164 } else {
1165 SkMatrix tmp = *this;
1166
1167 tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0;
1168 tmp.clearTypeMask(kTranslate_Mask);
1169 tmp.mapPoints(dst, src, count);
1170 }
1171}
1172
1173bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const {
1174 SkASSERT(dst && &src);
1175
1176 if (this->rectStaysRect()) {
1177 this->mapPoints((SkPoint*)dst, (const SkPoint*)&src, 2);
1178 dst->sort();
1179 return true;
1180 } else {
1181 SkPoint quad[4];
1182
1183 src.toQuad(quad);
1184 this->mapPoints(quad, quad, 4);
1185 dst->set(quad, 4);
1186 return false;
1187 }
1188}
1189
1190SkScalar SkMatrix::mapRadius(SkScalar radius) const {
1191 SkVector vec[2];
1192
1193 vec[0].set(radius, 0);
1194 vec[1].set(0, radius);
1195 this->mapVectors(vec, 2);
1196
1197 SkScalar d0 = vec[0].length();
1198 SkScalar d1 = vec[1].length();
1199
1200 return SkScalarMean(d0, d1);
1201}
1202
1203///////////////////////////////////////////////////////////////////////////////
1204
1205void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1206 SkPoint* pt) {
tomhudson@google.com8d430182011-06-06 19:11:19 +00001207 SkASSERT(m.hasPerspective());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208
1209 SkScalar x = SkScalarMul(sx, m.fMat[kMScaleX]) +
1210 SkScalarMul(sy, m.fMat[kMSkewX]) + m.fMat[kMTransX];
1211 SkScalar y = SkScalarMul(sx, m.fMat[kMSkewY]) +
1212 SkScalarMul(sy, m.fMat[kMScaleY]) + m.fMat[kMTransY];
1213#ifdef SK_SCALAR_IS_FIXED
1214 SkFixed z = SkFractMul(sx, m.fMat[kMPersp0]) +
1215 SkFractMul(sy, m.fMat[kMPersp1]) +
1216 SkFractToFixed(m.fMat[kMPersp2]);
1217#else
1218 float z = SkScalarMul(sx, m.fMat[kMPersp0]) +
1219 SkScalarMul(sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2];
1220#endif
1221 if (z) {
1222 z = SkScalarFastInvert(z);
1223 }
1224 pt->fX = SkScalarMul(x, z);
1225 pt->fY = SkScalarMul(y, z);
1226}
1227
1228#ifdef SK_SCALAR_IS_FIXED
1229static SkFixed fixmuladdmul(SkFixed a, SkFixed b, SkFixed c, SkFixed d) {
1230 Sk64 tmp, tmp1;
1231
1232 tmp.setMul(a, b);
1233 tmp1.setMul(c, d);
1234 return tmp.addGetFixed(tmp1);
1235// tmp.add(tmp1);
1236// return tmp.getFixed();
1237}
1238#endif
1239
1240void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1241 SkPoint* pt) {
1242 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001243
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244#ifdef SK_SCALAR_IS_FIXED
1245 pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) +
1246 m.fMat[kMTransX];
1247 pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) +
1248 m.fMat[kMTransY];
1249#else
1250 pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
1251 SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
1252 pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
1253 SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
1254#endif
1255}
1256
1257void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1258 SkPoint* pt) {
1259 SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask);
1260 SkASSERT(0 == m.fMat[kMTransX]);
1261 SkASSERT(0 == m.fMat[kMTransY]);
1262
1263#ifdef SK_SCALAR_IS_FIXED
1264 pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]);
1265 pt->fY = fixmuladdmul(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]);
1266#else
1267 pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]) +
1268 SkScalarMulAdd(sy, m.fMat[kMSkewX], m.fMat[kMTransX]);
1269 pt->fY = SkScalarMul(sx, m.fMat[kMSkewY]) +
1270 SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
1271#endif
1272}
1273
1274void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1275 SkPoint* pt) {
1276 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1277 == kScale_Mask);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001278
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 pt->fX = SkScalarMulAdd(sx, m.fMat[kMScaleX], m.fMat[kMTransX]);
1280 pt->fY = SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
1281}
1282
1283void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1284 SkPoint* pt) {
1285 SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
1286 == kScale_Mask);
1287 SkASSERT(0 == m.fMat[kMTransX]);
1288 SkASSERT(0 == m.fMat[kMTransY]);
1289
1290 pt->fX = SkScalarMul(sx, m.fMat[kMScaleX]);
1291 pt->fY = SkScalarMul(sy, m.fMat[kMScaleY]);
1292}
1293
1294void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1295 SkPoint* pt) {
1296 SkASSERT(m.getType() == kTranslate_Mask);
1297
1298 pt->fX = sx + m.fMat[kMTransX];
1299 pt->fY = sy + m.fMat[kMTransY];
1300}
1301
1302void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
1303 SkPoint* pt) {
1304 SkASSERT(0 == m.getType());
1305
1306 pt->fX = sx;
1307 pt->fY = sy;
1308}
1309
1310const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = {
1311 SkMatrix::Identity_xy, SkMatrix::Trans_xy,
1312 SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy,
1313 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1314 SkMatrix::Rot_xy, SkMatrix::RotTrans_xy,
1315 // repeat the persp proc 8 times
1316 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1317 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1318 SkMatrix::Persp_xy, SkMatrix::Persp_xy,
1319 SkMatrix::Persp_xy, SkMatrix::Persp_xy
1320};
1321
1322///////////////////////////////////////////////////////////////////////////////
1323
1324// if its nearly zero (just made up 26, perhaps it should be bigger or smaller)
1325#ifdef SK_SCALAR_IS_FIXED
1326 typedef SkFract SkPerspElemType;
1327 #define PerspNearlyZero(x) (SkAbs32(x) < (SK_Fract1 >> 26))
1328#else
1329 typedef float SkPerspElemType;
1330 #define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26)))
1331#endif
1332
1333bool SkMatrix::fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const {
1334 if (PerspNearlyZero(fMat[kMPersp0])) {
1335 if (stepX || stepY) {
1336 if (PerspNearlyZero(fMat[kMPersp1]) &&
1337 PerspNearlyZero(fMat[kMPersp2] - kMatrix22Elem)) {
1338 if (stepX) {
1339 *stepX = SkScalarToFixed(fMat[kMScaleX]);
1340 }
1341 if (stepY) {
1342 *stepY = SkScalarToFixed(fMat[kMSkewY]);
1343 }
1344 } else {
1345#ifdef SK_SCALAR_IS_FIXED
1346 SkFixed z = SkFractMul(y, fMat[kMPersp1]) +
1347 SkFractToFixed(fMat[kMPersp2]);
1348#else
1349 float z = y * fMat[kMPersp1] + fMat[kMPersp2];
1350#endif
1351 if (stepX) {
1352 *stepX = SkScalarToFixed(SkScalarDiv(fMat[kMScaleX], z));
1353 }
1354 if (stepY) {
1355 *stepY = SkScalarToFixed(SkScalarDiv(fMat[kMSkewY], z));
1356 }
1357 }
1358 }
1359 return true;
1360 }
1361 return false;
1362}
1363
1364///////////////////////////////////////////////////////////////////////////////
1365
1366#include "SkPerspIter.h"
1367
1368SkPerspIter::SkPerspIter(const SkMatrix& m, SkScalar x0, SkScalar y0, int count)
1369 : fMatrix(m), fSX(x0), fSY(y0), fCount(count) {
1370 SkPoint pt;
1371
1372 SkMatrix::Persp_xy(m, x0, y0, &pt);
1373 fX = SkScalarToFixed(pt.fX);
1374 fY = SkScalarToFixed(pt.fY);
1375}
1376
1377int SkPerspIter::next() {
1378 int n = fCount;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001379
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 if (0 == n) {
1381 return 0;
1382 }
1383 SkPoint pt;
1384 SkFixed x = fX;
1385 SkFixed y = fY;
1386 SkFixed dx, dy;
1387
1388 if (n >= kCount) {
1389 n = kCount;
1390 fSX += SkIntToScalar(kCount);
1391 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1392 fX = SkScalarToFixed(pt.fX);
1393 fY = SkScalarToFixed(pt.fY);
1394 dx = (fX - x) >> kShift;
1395 dy = (fY - y) >> kShift;
1396 } else {
1397 fSX += SkIntToScalar(n);
1398 SkMatrix::Persp_xy(fMatrix, fSX, fSY, &pt);
1399 fX = SkScalarToFixed(pt.fX);
1400 fY = SkScalarToFixed(pt.fY);
1401 dx = (fX - x) / n;
1402 dy = (fY - y) / n;
1403 }
1404
1405 SkFixed* p = fStorage;
1406 for (int i = 0; i < n; i++) {
1407 *p++ = x; x += dx;
1408 *p++ = y; y += dy;
1409 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 fCount -= n;
1412 return n;
1413}
1414
1415///////////////////////////////////////////////////////////////////////////////
1416
1417#ifdef SK_SCALAR_IS_FIXED
1418
1419static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1420 SkFixed x = SK_Fixed1, y = SK_Fixed1;
1421 SkPoint pt1, pt2;
1422 Sk64 w1, w2;
1423
1424 if (count > 1) {
1425 pt1.fX = poly[1].fX - poly[0].fX;
1426 pt1.fY = poly[1].fY - poly[0].fY;
1427 y = SkPoint::Length(pt1.fX, pt1.fY);
1428 if (y == 0) {
1429 return false;
1430 }
1431 switch (count) {
1432 case 2:
1433 break;
1434 case 3:
1435 pt2.fX = poly[0].fY - poly[2].fY;
1436 pt2.fY = poly[2].fX - poly[0].fX;
1437 goto CALC_X;
1438 default:
1439 pt2.fX = poly[0].fY - poly[3].fY;
1440 pt2.fY = poly[3].fX - poly[0].fX;
1441 CALC_X:
1442 w1.setMul(pt1.fX, pt2.fX);
1443 w2.setMul(pt1.fY, pt2.fY);
1444 w1.add(w2);
1445 w1.div(y, Sk64::kRound_DivOption);
1446 if (!w1.is32()) {
1447 return false;
1448 }
1449 x = w1.get32();
1450 break;
1451 }
1452 }
1453 pt->set(x, y);
1454 return true;
1455}
1456
1457bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1458 const SkPoint& scalePt) {
1459 // need to check if SkFixedDiv overflows...
1460
1461 const SkFixed scale = scalePt.fY;
1462 dst->fMat[kMScaleX] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
1463 dst->fMat[kMSkewY] = SkFixedDiv(srcPt[0].fX - srcPt[1].fX, scale);
1464 dst->fMat[kMPersp0] = 0;
1465 dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale);
1466 dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale);
1467 dst->fMat[kMPersp1] = 0;
1468 dst->fMat[kMTransX] = srcPt[0].fX;
1469 dst->fMat[kMTransY] = srcPt[0].fY;
1470 dst->fMat[kMPersp2] = SK_Fract1;
1471 dst->setTypeMask(kUnknown_Mask);
1472 return true;
1473}
1474
1475bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1476 const SkPoint& scale) {
1477 // really, need to check if SkFixedDiv overflow'd
1478
1479 dst->fMat[kMScaleX] = SkFixedDiv(srcPt[2].fX - srcPt[0].fX, scale.fX);
1480 dst->fMat[kMSkewY] = SkFixedDiv(srcPt[2].fY - srcPt[0].fY, scale.fX);
1481 dst->fMat[kMPersp0] = 0;
1482 dst->fMat[kMSkewX] = SkFixedDiv(srcPt[1].fX - srcPt[0].fX, scale.fY);
1483 dst->fMat[kMScaleY] = SkFixedDiv(srcPt[1].fY - srcPt[0].fY, scale.fY);
1484 dst->fMat[kMPersp1] = 0;
1485 dst->fMat[kMTransX] = srcPt[0].fX;
1486 dst->fMat[kMTransY] = srcPt[0].fY;
1487 dst->fMat[kMPersp2] = SK_Fract1;
1488 dst->setTypeMask(kUnknown_Mask);
1489 return true;
1490}
1491
1492bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1493 const SkPoint& scale) {
1494 SkFract a1, a2;
1495 SkFixed x0, y0, x1, y1, x2, y2;
1496
1497 x0 = srcPt[2].fX - srcPt[0].fX;
1498 y0 = srcPt[2].fY - srcPt[0].fY;
1499 x1 = srcPt[2].fX - srcPt[1].fX;
1500 y1 = srcPt[2].fY - srcPt[1].fY;
1501 x2 = srcPt[2].fX - srcPt[3].fX;
1502 y2 = srcPt[2].fY - srcPt[3].fY;
1503
1504 /* check if abs(x2) > abs(y2) */
1505 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1506 SkFixed denom = SkMulDiv(x1, y2, x2) - y1;
1507 if (0 == denom) {
1508 return false;
1509 }
1510 a1 = SkFractDiv(SkMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
1511 } else {
1512 SkFixed denom = x1 - SkMulDiv(y1, x2, y2);
1513 if (0 == denom) {
1514 return false;
1515 }
1516 a1 = SkFractDiv(x0 - x1 - SkMulDiv(y0 - y1, x2, y2), denom);
1517 }
1518
1519 /* check if abs(x1) > abs(y1) */
1520 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1521 SkFixed denom = y2 - SkMulDiv(x2, y1, x1);
1522 if (0 == denom) {
1523 return false;
1524 }
1525 a2 = SkFractDiv(y0 - y2 - SkMulDiv(x0 - x2, y1, x1), denom);
1526 } else {
1527 SkFixed denom = SkMulDiv(y2, x1, y1) - x2;
1528 if (0 == denom) {
1529 return false;
1530 }
1531 a2 = SkFractDiv(SkMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
1532 }
1533
1534 // need to check if SkFixedDiv overflows...
1535 dst->fMat[kMScaleX] = SkFixedDiv(SkFractMul(a2, srcPt[3].fX) +
1536 srcPt[3].fX - srcPt[0].fX, scale.fX);
1537 dst->fMat[kMSkewY] = SkFixedDiv(SkFractMul(a2, srcPt[3].fY) +
1538 srcPt[3].fY - srcPt[0].fY, scale.fX);
1539 dst->fMat[kMPersp0] = SkFixedDiv(a2, scale.fX);
1540 dst->fMat[kMSkewX] = SkFixedDiv(SkFractMul(a1, srcPt[1].fX) +
1541 srcPt[1].fX - srcPt[0].fX, scale.fY);
1542 dst->fMat[kMScaleY] = SkFixedDiv(SkFractMul(a1, srcPt[1].fY) +
1543 srcPt[1].fY - srcPt[0].fY, scale.fY);
1544 dst->fMat[kMPersp1] = SkFixedDiv(a1, scale.fY);
1545 dst->fMat[kMTransX] = srcPt[0].fX;
1546 dst->fMat[kMTransY] = srcPt[0].fY;
1547 dst->fMat[kMPersp2] = SK_Fract1;
1548 dst->setTypeMask(kUnknown_Mask);
1549 return true;
1550}
1551
1552#else /* Scalar is float */
1553
1554static inline bool checkForZero(float x) {
1555 return x*x == 0;
1556}
1557
1558static inline bool poly_to_point(SkPoint* pt, const SkPoint poly[], int count) {
1559 float x = 1, y = 1;
1560 SkPoint pt1, pt2;
1561
1562 if (count > 1) {
1563 pt1.fX = poly[1].fX - poly[0].fX;
1564 pt1.fY = poly[1].fY - poly[0].fY;
1565 y = SkPoint::Length(pt1.fX, pt1.fY);
1566 if (checkForZero(y)) {
1567 return false;
1568 }
1569 switch (count) {
1570 case 2:
1571 break;
1572 case 3:
1573 pt2.fX = poly[0].fY - poly[2].fY;
1574 pt2.fY = poly[2].fX - poly[0].fX;
1575 goto CALC_X;
1576 default:
1577 pt2.fX = poly[0].fY - poly[3].fY;
1578 pt2.fY = poly[3].fX - poly[0].fX;
1579 CALC_X:
1580 x = SkScalarDiv(SkScalarMul(pt1.fX, pt2.fX) +
1581 SkScalarMul(pt1.fY, pt2.fY), y);
1582 break;
1583 }
1584 }
1585 pt->set(x, y);
1586 return true;
1587}
1588
1589bool SkMatrix::Poly2Proc(const SkPoint srcPt[], SkMatrix* dst,
1590 const SkPoint& scale) {
1591 float invScale = 1 / scale.fY;
1592
1593 dst->fMat[kMScaleX] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1594 dst->fMat[kMSkewY] = (srcPt[0].fX - srcPt[1].fX) * invScale;
1595 dst->fMat[kMPersp0] = 0;
1596 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1597 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1598 dst->fMat[kMPersp1] = 0;
1599 dst->fMat[kMTransX] = srcPt[0].fX;
1600 dst->fMat[kMTransY] = srcPt[0].fY;
1601 dst->fMat[kMPersp2] = 1;
1602 dst->setTypeMask(kUnknown_Mask);
1603 return true;
1604}
1605
1606bool SkMatrix::Poly3Proc(const SkPoint srcPt[], SkMatrix* dst,
1607 const SkPoint& scale) {
1608 float invScale = 1 / scale.fX;
1609 dst->fMat[kMScaleX] = (srcPt[2].fX - srcPt[0].fX) * invScale;
1610 dst->fMat[kMSkewY] = (srcPt[2].fY - srcPt[0].fY) * invScale;
1611 dst->fMat[kMPersp0] = 0;
1612
1613 invScale = 1 / scale.fY;
1614 dst->fMat[kMSkewX] = (srcPt[1].fX - srcPt[0].fX) * invScale;
1615 dst->fMat[kMScaleY] = (srcPt[1].fY - srcPt[0].fY) * invScale;
1616 dst->fMat[kMPersp1] = 0;
1617
1618 dst->fMat[kMTransX] = srcPt[0].fX;
1619 dst->fMat[kMTransY] = srcPt[0].fY;
1620 dst->fMat[kMPersp2] = 1;
1621 dst->setTypeMask(kUnknown_Mask);
1622 return true;
1623}
1624
1625bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst,
1626 const SkPoint& scale) {
1627 float a1, a2;
1628 float x0, y0, x1, y1, x2, y2;
1629
1630 x0 = srcPt[2].fX - srcPt[0].fX;
1631 y0 = srcPt[2].fY - srcPt[0].fY;
1632 x1 = srcPt[2].fX - srcPt[1].fX;
1633 y1 = srcPt[2].fY - srcPt[1].fY;
1634 x2 = srcPt[2].fX - srcPt[3].fX;
1635 y2 = srcPt[2].fY - srcPt[3].fY;
1636
1637 /* check if abs(x2) > abs(y2) */
1638 if ( x2 > 0 ? y2 > 0 ? x2 > y2 : x2 > -y2 : y2 > 0 ? -x2 > y2 : x2 < y2) {
1639 float denom = SkScalarMulDiv(x1, y2, x2) - y1;
1640 if (checkForZero(denom)) {
1641 return false;
1642 }
1643 a1 = SkScalarDiv(SkScalarMulDiv(x0 - x1, y2, x2) - y0 + y1, denom);
1644 } else {
1645 float denom = x1 - SkScalarMulDiv(y1, x2, y2);
1646 if (checkForZero(denom)) {
1647 return false;
1648 }
1649 a1 = SkScalarDiv(x0 - x1 - SkScalarMulDiv(y0 - y1, x2, y2), denom);
1650 }
1651
1652 /* check if abs(x1) > abs(y1) */
1653 if ( x1 > 0 ? y1 > 0 ? x1 > y1 : x1 > -y1 : y1 > 0 ? -x1 > y1 : x1 < y1) {
1654 float denom = y2 - SkScalarMulDiv(x2, y1, x1);
1655 if (checkForZero(denom)) {
1656 return false;
1657 }
1658 a2 = SkScalarDiv(y0 - y2 - SkScalarMulDiv(x0 - x2, y1, x1), denom);
1659 } else {
1660 float denom = SkScalarMulDiv(y2, x1, y1) - x2;
1661 if (checkForZero(denom)) {
1662 return false;
1663 }
1664 a2 = SkScalarDiv(SkScalarMulDiv(y0 - y2, x1, y1) - x0 + x2, denom);
1665 }
1666
1667 float invScale = 1 / scale.fX;
1668 dst->fMat[kMScaleX] = SkScalarMul(SkScalarMul(a2, srcPt[3].fX) +
1669 srcPt[3].fX - srcPt[0].fX, invScale);
1670 dst->fMat[kMSkewY] = SkScalarMul(SkScalarMul(a2, srcPt[3].fY) +
1671 srcPt[3].fY - srcPt[0].fY, invScale);
1672 dst->fMat[kMPersp0] = SkScalarMul(a2, invScale);
1673 invScale = 1 / scale.fY;
1674 dst->fMat[kMSkewX] = SkScalarMul(SkScalarMul(a1, srcPt[1].fX) +
1675 srcPt[1].fX - srcPt[0].fX, invScale);
1676 dst->fMat[kMScaleY] = SkScalarMul(SkScalarMul(a1, srcPt[1].fY) +
1677 srcPt[1].fY - srcPt[0].fY, invScale);
1678 dst->fMat[kMPersp1] = SkScalarMul(a1, invScale);
1679 dst->fMat[kMTransX] = srcPt[0].fX;
1680 dst->fMat[kMTransY] = srcPt[0].fY;
1681 dst->fMat[kMPersp2] = 1;
1682 dst->setTypeMask(kUnknown_Mask);
1683 return true;
1684}
1685
1686#endif
1687
1688typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*, const SkPoint&);
1689
1690/* Taken from Rob Johnson's original sample code in QuickDraw GX
1691*/
1692bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
1693 int count) {
1694 if ((unsigned)count > 4) {
1695 SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count);
1696 return false;
1697 }
1698
1699 if (0 == count) {
1700 this->reset();
1701 return true;
1702 }
1703 if (1 == count) {
1704 this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY);
1705 return true;
1706 }
1707
1708 SkPoint scale;
1709 if (!poly_to_point(&scale, src, count) ||
1710 SkScalarNearlyZero(scale.fX) ||
1711 SkScalarNearlyZero(scale.fY)) {
1712 return false;
1713 }
1714
1715 static const PolyMapProc gPolyMapProcs[] = {
1716 SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc
1717 };
1718 PolyMapProc proc = gPolyMapProcs[count - 2];
1719
1720 SkMatrix tempMap, result;
1721 tempMap.setTypeMask(kUnknown_Mask);
1722
1723 if (!proc(src, &tempMap, scale)) {
1724 return false;
1725 }
1726 if (!tempMap.invert(&result)) {
1727 return false;
1728 }
1729 if (!proc(dst, &tempMap, scale)) {
1730 return false;
1731 }
1732 if (!result.setConcat(tempMap, result)) {
1733 return false;
1734 }
1735 *this = result;
1736 return true;
1737}
1738
1739///////////////////////////////////////////////////////////////////////////////
1740
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001741SkScalar SkMatrix::getMaxStretch() const {
1742 TypeMask mask = this->getType();
1743
bsalomon@google.com38396322011-09-09 19:32:04 +00001744 if (this->hasPerspective()) {
1745 return -SK_Scalar1;
1746 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001747 if (this->isIdentity()) {
bsalomon@google.com38396322011-09-09 19:32:04 +00001748 return SK_Scalar1;
1749 }
1750 if (!(mask & kAffine_Mask)) {
1751 return SkMaxScalar(SkScalarAbs(fMat[kMScaleX]),
1752 SkScalarAbs(fMat[kMScaleY]));
1753 }
1754 // ignore the translation part of the matrix, just look at 2x2 portion.
1755 // compute singular values, take largest abs value.
1756 // [a b; b c] = A^T*A
1757 SkScalar a = SkScalarMul(fMat[kMScaleX], fMat[kMScaleX]) +
1758 SkScalarMul(fMat[kMSkewY], fMat[kMSkewY]);
1759 SkScalar b = SkScalarMul(fMat[kMScaleX], fMat[kMSkewX]) +
1760 SkScalarMul(fMat[kMScaleY], fMat[kMSkewY]);
1761 SkScalar c = SkScalarMul(fMat[kMSkewX], fMat[kMSkewX]) +
1762 SkScalarMul(fMat[kMScaleY], fMat[kMScaleY]);
1763 // eigenvalues of A^T*A are the squared singular values of A.
1764 // characteristic equation is det((A^T*A) - l*I) = 0
1765 // l^2 - (a + c)l + (ac-b^2)
1766 // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
1767 // and roots are guaraunteed to be pos and real).
1768 SkScalar largerRoot;
1769 SkScalar bSqd = SkScalarMul(b,b);
1770 // if upper left 2x2 is orthogonal save some math
1771 if (bSqd <= SK_ScalarNearlyZero) {
1772 largerRoot = SkMaxScalar(a, c);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001773 } else {
bsalomon@google.com38396322011-09-09 19:32:04 +00001774 SkScalar aminusc = a - c;
1775 SkScalar apluscdiv2 = SkScalarHalf(a + c);
1776 SkScalar x = SkScalarHalf(SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd));
1777 largerRoot = apluscdiv2 + x;
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001778 }
bsalomon@google.com38396322011-09-09 19:32:04 +00001779 return SkScalarSqrt(largerRoot);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001780}
1781
1782const SkMatrix& SkMatrix::I() {
1783 static SkMatrix gIdentity;
1784 static bool gOnce;
1785 if (!gOnce) {
1786 gIdentity.reset();
1787 gOnce = true;
1788 }
1789 return gIdentity;
tomhudson@google.com1f902872012-06-01 13:15:47 +00001790}
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001791
1792const SkMatrix& SkMatrix::InvalidMatrix() {
1793 static SkMatrix gInvalid;
1794 static bool gOnce;
1795 if (!gOnce) {
1796 gInvalid.setAll(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1797 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax,
1798 SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1799 gInvalid.getType(); // force the type to be computed
1800 gOnce = true;
1801 }
1802 return gInvalid;
1803}
1804
1805///////////////////////////////////////////////////////////////////////////////
1806
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001807uint32_t SkMatrix::writeToMemory(void* buffer) const {
reed@android.com0ad336f2009-06-29 16:02:20 +00001808 // TODO write less for simple matrices
1809 if (buffer) {
1810 memcpy(buffer, fMat, 9 * sizeof(SkScalar));
1811 }
1812 return 9 * sizeof(SkScalar);
1813}
1814
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001815uint32_t SkMatrix::readFromMemory(const void* buffer) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001816 if (buffer) {
1817 memcpy(fMat, buffer, 9 * sizeof(SkScalar));
1818 this->setTypeMask(kUnknown_Mask);
1819 }
reed@android.com0ad336f2009-06-29 16:02:20 +00001820 return 9 * sizeof(SkScalar);
1821}
1822
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823void SkMatrix::dump() const {
1824 SkString str;
1825 this->toDumpString(&str);
1826 SkDebugf("%s\n", str.c_str());
1827}
1828
1829void SkMatrix::toDumpString(SkString* str) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830 str->printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
1831#ifdef SK_SCALAR_IS_FLOAT
1832 fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
1833 fMat[6], fMat[7], fMat[8]);
1834#else
1835 SkFixedToFloat(fMat[0]), SkFixedToFloat(fMat[1]), SkFixedToFloat(fMat[2]),
1836 SkFixedToFloat(fMat[3]), SkFixedToFloat(fMat[4]), SkFixedToFloat(fMat[5]),
1837 SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8]));
1838#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839}
reed@google.comad514302013-01-02 20:19:45 +00001840
1841///////////////////////////////////////////////////////////////////////////////
1842
1843#include "SkMatrixUtils.h"
1844
reed@google.comae573582013-01-03 15:22:40 +00001845bool SkTreatAsSprite(const SkMatrix& mat, int width, int height,
reed@google.comad514302013-01-02 20:19:45 +00001846 unsigned subpixelBits) {
reed@google.comae573582013-01-03 15:22:40 +00001847 // quick reject on affine or perspective
reed@google.comad514302013-01-02 20:19:45 +00001848 if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
1849 return false;
1850 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001851
reed@google.comad514302013-01-02 20:19:45 +00001852 // quick success check
1853 if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
1854 return true;
1855 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001856
reed@google.comad514302013-01-02 20:19:45 +00001857 // mapRect supports negative scales, so we eliminate those first
1858 if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
1859 return false;
1860 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001861
reed@google.comad514302013-01-02 20:19:45 +00001862 SkRect dst;
reed@google.comae573582013-01-03 15:22:40 +00001863 SkIRect isrc = { 0, 0, width, height };
skia.committer@gmail.comd9f65e32013-01-04 12:07:46 +00001864
reed@google.comad514302013-01-02 20:19:45 +00001865 {
reed@google.comae573582013-01-03 15:22:40 +00001866 SkRect src;
1867 src.set(isrc);
1868 mat.mapRect(&dst, src);
reed@google.comad514302013-01-02 20:19:45 +00001869 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001870
reed@google.comae573582013-01-03 15:22:40 +00001871 // just apply the translate to isrc
1872 isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
1873 SkScalarRoundToInt(mat.getTranslateY()));
1874
reed@google.comad514302013-01-02 20:19:45 +00001875 if (subpixelBits) {
1876 isrc.fLeft <<= subpixelBits;
1877 isrc.fTop <<= subpixelBits;
1878 isrc.fRight <<= subpixelBits;
1879 isrc.fBottom <<= subpixelBits;
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001880
reed@google.comad514302013-01-02 20:19:45 +00001881 const float scale = 1 << subpixelBits;
1882 dst.fLeft *= scale;
1883 dst.fTop *= scale;
1884 dst.fRight *= scale;
1885 dst.fBottom *= scale;
1886 }
skia.committer@gmail.com422188f2013-01-03 02:01:32 +00001887
reed@google.comae573582013-01-03 15:22:40 +00001888 SkIRect idst;
reed@google.comad514302013-01-02 20:19:45 +00001889 dst.round(&idst);
1890 return isrc == idst;
1891}
1892