blob: 71d9525962ef392ecae88303948c156a79c4dd2e [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#ifndef SkMatrix_DEFINED
11#define SkMatrix_DEFINED
12
mtklein0b544ae2014-07-08 19:37:47 -070013#include "SkDynamicAnnotations.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkRect.h"
15
16class SkString;
17
reed@google.com8f4d2302013-12-17 16:44:46 +000018// TODO: can we remove these 3 (need to check chrome/android)
19typedef SkScalar SkPersp;
20#define SkScalarToPersp(x) (x)
21#define SkPerspToScalar(x) (x)
bungeman@google.com07faed12011-10-07 21:55:56 +000022
reed@android.com8a1c16f2008-12-17 15:59:43 +000023/** \class SkMatrix
24
25 The SkMatrix class holds a 3x3 matrix for transforming coordinates.
26 SkMatrix does not have a constructor, so it must be explicitly initialized
27 using either reset() - to construct an identity matrix, or one of the set
28 functions (e.g. setTranslate, setRotate, etc.).
29*/
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +000030class SK_API SkMatrix {
reed@android.com8a1c16f2008-12-17 15:59:43 +000031public:
32 /** Enum of bit fields for the mask return by getType().
33 Use this to identify the complexity of the matrix.
34 */
35 enum TypeMask {
36 kIdentity_Mask = 0,
37 kTranslate_Mask = 0x01, //!< set if the matrix has translation
38 kScale_Mask = 0x02, //!< set if the matrix has X or Y scale
39 kAffine_Mask = 0x04, //!< set if the matrix skews or rotates
40 kPerspective_Mask = 0x08 //!< set if the matrix is in perspective
41 };
42
rmistry@google.comfbfcd562012-08-23 18:09:54 +000043 /** Returns a bitfield describing the transformations the matrix may
bsalomon@google.comfd4c00e2012-07-13 14:39:51 +000044 perform. The bitfield is computed conservatively, so it may include
rmistry@google.comfbfcd562012-08-23 18:09:54 +000045 false positives. For example, when kPerspective_Mask is true, all
bsalomon@google.comfd4c00e2012-07-13 14:39:51 +000046 other bits may be set to true even in the case of a pure perspective
47 transform.
junov@chromium.org6fc56992012-07-12 14:01:32 +000048 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 TypeMask getType() const {
50 if (fTypeMask & kUnknown_Mask) {
51 fTypeMask = this->computeTypeMask();
52 }
53 // only return the public masks
54 return (TypeMask)(fTypeMask & 0xF);
55 }
56
57 /** Returns true if the matrix is identity.
58 */
59 bool isIdentity() const {
60 return this->getType() == 0;
61 }
62
robertphillips9f2251c2014-11-04 13:33:50 -080063 bool isScaleTranslate() const {
64 return !(this->getType() & ~(kScale_Mask | kTranslate_Mask));
65 }
66
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 /** Returns true if will map a rectangle to another rectangle. This can be
68 true if the matrix is identity, scale-only, or rotates a multiple of
69 90 degrees.
70 */
71 bool rectStaysRect() const {
72 if (fTypeMask & kUnknown_Mask) {
73 fTypeMask = this->computeTypeMask();
74 }
75 return (fTypeMask & kRectStaysRect_Mask) != 0;
76 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +000077 // alias for rectStaysRect()
78 bool preservesAxisAlignment() const { return this->rectStaysRect(); }
79
80 /**
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000081 * Returns true if the matrix contains perspective elements.
bsalomon@google.comcc4dac32011-05-10 13:52:42 +000082 */
83 bool hasPerspective() const {
tomhudson@google.comdd5f7442011-08-30 15:13:55 +000084 return SkToBool(this->getPerspectiveTypeMaskOnly() &
85 kPerspective_Mask);
bsalomon@google.comcc4dac32011-05-10 13:52:42 +000086 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000087
jvanverth17a845f2014-09-02 13:15:40 -070088 /** Returns true if the matrix contains only translation, rotation/reflection or uniform scale
jvanverth@google.com46d3d392013-01-22 13:34:01 +000089 Returns false if other transformation types are included or is degenerate
90 */
91 bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const;
92
jvanverth17a845f2014-09-02 13:15:40 -070093 /** Returns true if the matrix contains only translation, rotation/reflection or scale
robertphillips@google.comdf3695e2013-04-09 14:01:44 +000094 (non-uniform scale is allowed).
95 Returns false if other transformation types are included or is degenerate
96 */
97 bool preservesRightAngles(SkScalar tol = SK_ScalarNearlyZero) const;
98
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 enum {
100 kMScaleX,
101 kMSkewX,
102 kMTransX,
103 kMSkewY,
104 kMScaleY,
105 kMTransY,
106 kMPersp0,
107 kMPersp1,
108 kMPersp2
109 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000110
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000111 /** Affine arrays are in column major order
112 because that's how PDF and XPS like it.
113 */
114 enum {
115 kAScaleX,
116 kASkewY,
117 kASkewX,
118 kAScaleY,
119 kATransX,
120 kATransY
121 };
122
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 SkScalar operator[](int index) const {
124 SkASSERT((unsigned)index < 9);
125 return fMat[index];
126 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000127
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 SkScalar get(int index) const {
129 SkASSERT((unsigned)index < 9);
130 return fMat[index];
131 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000132
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 SkScalar getScaleX() const { return fMat[kMScaleX]; }
134 SkScalar getScaleY() const { return fMat[kMScaleY]; }
135 SkScalar getSkewY() const { return fMat[kMSkewY]; }
136 SkScalar getSkewX() const { return fMat[kMSkewX]; }
137 SkScalar getTranslateX() const { return fMat[kMTransX]; }
138 SkScalar getTranslateY() const { return fMat[kMTransY]; }
bungeman@google.com07faed12011-10-07 21:55:56 +0000139 SkPersp getPerspX() const { return fMat[kMPersp0]; }
140 SkPersp getPerspY() const { return fMat[kMPersp1]; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141
reed@android.comd055c1f2010-03-01 14:54:05 +0000142 SkScalar& operator[](int index) {
143 SkASSERT((unsigned)index < 9);
144 this->setTypeMask(kUnknown_Mask);
145 return fMat[index];
146 }
147
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 void set(int index, SkScalar value) {
149 SkASSERT((unsigned)index < 9);
150 fMat[index] = value;
151 this->setTypeMask(kUnknown_Mask);
152 }
153
154 void setScaleX(SkScalar v) { this->set(kMScaleX, v); }
155 void setScaleY(SkScalar v) { this->set(kMScaleY, v); }
156 void setSkewY(SkScalar v) { this->set(kMSkewY, v); }
157 void setSkewX(SkScalar v) { this->set(kMSkewX, v); }
158 void setTranslateX(SkScalar v) { this->set(kMTransX, v); }
159 void setTranslateY(SkScalar v) { this->set(kMTransY, v); }
bungeman@google.com07faed12011-10-07 21:55:56 +0000160 void setPerspX(SkPersp v) { this->set(kMPersp0, v); }
161 void setPerspY(SkPersp v) { this->set(kMPersp1, v); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000163 void setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
164 SkScalar skewY, SkScalar scaleY, SkScalar transY,
bungeman@google.com07faed12011-10-07 21:55:56 +0000165 SkPersp persp0, SkPersp persp1, SkPersp persp2) {
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000166 fMat[kMScaleX] = scaleX;
167 fMat[kMSkewX] = skewX;
168 fMat[kMTransX] = transX;
169 fMat[kMSkewY] = skewY;
170 fMat[kMScaleY] = scaleY;
171 fMat[kMTransY] = transY;
172 fMat[kMPersp0] = persp0;
173 fMat[kMPersp1] = persp1;
174 fMat[kMPersp2] = persp2;
175 this->setTypeMask(kUnknown_Mask);
176 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 /** Set the matrix to identity
179 */
180 void reset();
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000181 // alias for reset()
182 void setIdentity() { this->reset(); }
183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 /** Set the matrix to translate by (dx, dy).
185 */
186 void setTranslate(SkScalar dx, SkScalar dy);
bsalomon@google.com7b7cdd12012-11-07 16:17:24 +0000187 void setTranslate(const SkVector& v) { this->setTranslate(v.fX, v.fY); }
188
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 /** Set the matrix to scale by sx and sy, with a pivot point at (px, py).
190 The pivot point is the coordinate that should remain unchanged by the
191 specified transformation.
192 */
193 void setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
194 /** Set the matrix to scale by sx and sy.
195 */
196 void setScale(SkScalar sx, SkScalar sy);
bsalomon@google.com5c638652011-07-18 19:31:59 +0000197 /** Set the matrix to scale by 1/divx and 1/divy. Returns false and doesn't
198 touch the matrix if either divx or divy is zero.
199 */
200 bool setIDiv(int divx, int divy);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 /** Set the matrix to rotate by the specified number of degrees, with a
202 pivot point at (px, py). The pivot point is the coordinate that should
203 remain unchanged by the specified transformation.
204 */
205 void setRotate(SkScalar degrees, SkScalar px, SkScalar py);
206 /** Set the matrix to rotate about (0,0) by the specified number of degrees.
207 */
208 void setRotate(SkScalar degrees);
209 /** Set the matrix to rotate by the specified sine and cosine values, with
210 a pivot point at (px, py). The pivot point is the coordinate that
211 should remain unchanged by the specified transformation.
212 */
213 void setSinCos(SkScalar sinValue, SkScalar cosValue,
214 SkScalar px, SkScalar py);
215 /** Set the matrix to rotate by the specified sine and cosine values.
216 */
217 void setSinCos(SkScalar sinValue, SkScalar cosValue);
218 /** Set the matrix to skew by sx and sy, with a pivot point at (px, py).
219 The pivot point is the coordinate that should remain unchanged by the
220 specified transformation.
221 */
222 void setSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
223 /** Set the matrix to skew by sx and sy.
224 */
225 void setSkew(SkScalar kx, SkScalar ky);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000226 /** Set the matrix to the concatenation of the two specified matrices.
227 Either of the two matrices may also be the target matrix.
228 *this = a * b;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000230 void setConcat(const SkMatrix& a, const SkMatrix& b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231
232 /** Preconcats the matrix with the specified translation.
233 M' = M * T(dx, dy)
234 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000235 void preTranslate(SkScalar dx, SkScalar dy);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 /** Preconcats the matrix with the specified scale.
237 M' = M * S(sx, sy, px, py)
238 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000239 void preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 /** Preconcats the matrix with the specified scale.
241 M' = M * S(sx, sy)
242 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000243 void preScale(SkScalar sx, SkScalar sy);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 /** Preconcats the matrix with the specified rotation.
245 M' = M * R(degrees, px, py)
246 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000247 void preRotate(SkScalar degrees, SkScalar px, SkScalar py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 /** Preconcats the matrix with the specified rotation.
249 M' = M * R(degrees)
250 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000251 void preRotate(SkScalar degrees);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 /** Preconcats the matrix with the specified skew.
253 M' = M * K(kx, ky, px, py)
254 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000255 void preSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 /** Preconcats the matrix with the specified skew.
257 M' = M * K(kx, ky)
258 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000259 void preSkew(SkScalar kx, SkScalar ky);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 /** Preconcats the matrix with the specified matrix.
261 M' = M * other
262 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000263 void preConcat(const SkMatrix& other);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264
265 /** Postconcats the matrix with the specified translation.
266 M' = T(dx, dy) * M
267 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000268 void postTranslate(SkScalar dx, SkScalar dy);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 /** Postconcats the matrix with the specified scale.
270 M' = S(sx, sy, px, py) * M
271 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000272 void postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 /** Postconcats the matrix with the specified scale.
274 M' = S(sx, sy) * M
275 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000276 void postScale(SkScalar sx, SkScalar sy);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 /** Postconcats the matrix by dividing it by the specified integers.
278 M' = S(1/divx, 1/divy, 0, 0) * M
279 */
280 bool postIDiv(int divx, int divy);
281 /** Postconcats the matrix with the specified rotation.
282 M' = R(degrees, px, py) * M
283 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000284 void postRotate(SkScalar degrees, SkScalar px, SkScalar py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 /** Postconcats the matrix with the specified rotation.
286 M' = R(degrees) * M
287 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000288 void postRotate(SkScalar degrees);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 /** Postconcats the matrix with the specified skew.
290 M' = K(kx, ky, px, py) * M
291 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000292 void postSkew(SkScalar kx, SkScalar ky, SkScalar px, SkScalar py);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 /** Postconcats the matrix with the specified skew.
294 M' = K(kx, ky) * M
295 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000296 void postSkew(SkScalar kx, SkScalar ky);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 /** Postconcats the matrix with the specified matrix.
298 M' = other * M
299 */
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000300 void postConcat(const SkMatrix& other);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301
302 enum ScaleToFit {
303 /**
304 * Scale in X and Y independently, so that src matches dst exactly.
305 * This may change the aspect ratio of the src.
306 */
307 kFill_ScaleToFit,
308 /**
309 * Compute a scale that will maintain the original src aspect ratio,
310 * but will also ensure that src fits entirely inside dst. At least one
311 * axis (X or Y) will fit exactly. kStart aligns the result to the
312 * left and top edges of dst.
313 */
314 kStart_ScaleToFit,
315 /**
316 * Compute a scale that will maintain the original src aspect ratio,
317 * but will also ensure that src fits entirely inside dst. At least one
318 * axis (X or Y) will fit exactly. The result is centered inside dst.
319 */
320 kCenter_ScaleToFit,
321 /**
322 * Compute a scale that will maintain the original src aspect ratio,
323 * but will also ensure that src fits entirely inside dst. At least one
324 * axis (X or Y) will fit exactly. kEnd aligns the result to the
325 * right and bottom edges of dst.
326 */
327 kEnd_ScaleToFit
328 };
329
330 /** Set the matrix to the scale and translate values that map the source
331 rectangle to the destination rectangle, returning true if the the result
332 can be represented.
333 @param src the source rectangle to map from.
334 @param dst the destination rectangle to map to.
335 @param stf the ScaleToFit option
336 @return true if the matrix can be represented by the rectangle mapping.
337 */
338 bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 /** Set the matrix such that the specified src points would map to the
341 specified dst points. count must be within [0..4].
342 @param src The array of src points
343 @param dst The array of dst points
344 @param count The number of points to use for the transformation
345 @return true if the matrix was set to the specified transformation
346 */
347 bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count);
348
349 /** If this matrix can be inverted, return true and if inverse is not null,
350 set inverse to be the inverse of this matrix. If this matrix cannot be
351 inverted, ignore inverse and return false
352 */
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000353 bool SK_WARN_UNUSED_RESULT invert(SkMatrix* inverse) const {
354 // Allow the trivial case to be inlined.
355 if (this->isIdentity()) {
bsalomon49f085d2014-09-05 13:34:00 -0700356 if (inverse) {
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000357 inverse->reset();
358 }
359 return true;
360 }
361 return this->invertNonIdentity(inverse);
362 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000364 /** Fills the passed array with affine identity values
365 in column major order.
366 @param affine The array to fill with affine identity values.
367 Must not be NULL.
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000368 */
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000369 static void SetAffineIdentity(SkScalar affine[6]);
370
371 /** Fills the passed array with the affine values in column major order.
372 If the matrix is a perspective transform, returns false
373 and does not change the passed array.
374 @param affine The array to fill with affine values. Ignored if NULL.
375 */
376 bool asAffine(SkScalar affine[6]) const;
vandebo@chromium.orgddbbd802010-10-26 19:45:06 +0000377
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 /** Apply this matrix to the array of points specified by src, and write
379 the transformed points into the array of points specified by dst.
380 dst[] = M * src[]
381 @param dst Where the transformed coordinates are written. It must
382 contain at least count entries
383 @param src The original coordinates that are to be transformed. It
384 must contain at least count entries
385 @param count The number of points in src to read, and then transform
386 into dst.
387 */
388 void mapPoints(SkPoint dst[], const SkPoint src[], int count) const;
389
390 /** Apply this matrix to the array of points, overwriting it with the
391 transformed values.
392 dst[] = M * pts[]
393 @param pts The points to be transformed. It must contain at least
394 count entries
395 @param count The number of points in pts.
396 */
397 void mapPoints(SkPoint pts[], int count) const {
398 this->mapPoints(pts, pts, count);
399 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000400
bsalomon@google.com647a8042011-08-23 14:39:01 +0000401 /** Like mapPoints but with custom byte stride between the points. Stride
402 * should be a multiple of sizeof(SkScalar).
403 */
404 void mapPointsWithStride(SkPoint pts[], size_t stride, int count) const {
405 SkASSERT(stride >= sizeof(SkPoint));
406 SkASSERT(0 == stride % sizeof(SkScalar));
407 for (int i = 0; i < count; ++i) {
408 this->mapPoints(pts, pts, 1);
409 pts = (SkPoint*)((intptr_t)pts + stride);
410 }
411 }
412
413 /** Like mapPoints but with custom byte stride between the points.
414 */
415 void mapPointsWithStride(SkPoint dst[], SkPoint src[],
416 size_t stride, int count) const {
417 SkASSERT(stride >= sizeof(SkPoint));
418 SkASSERT(0 == stride % sizeof(SkScalar));
419 for (int i = 0; i < count; ++i) {
420 this->mapPoints(dst, src, 1);
421 src = (SkPoint*)((intptr_t)src + stride);
422 dst = (SkPoint*)((intptr_t)dst + stride);
423 }
424 }
425
egdaniel@google.com259fbaf2013-08-15 21:12:11 +0000426 /** Apply this matrix to the array of homogeneous points, specified by src,
427 where a homogeneous point is defined by 3 contiguous scalar values,
428 and write the transformed points into the array of scalars specified by dst.
429 dst[] = M * src[]
430 @param dst Where the transformed coordinates are written. It must
431 contain at least 3 * count entries
432 @param src The original coordinates that are to be transformed. It
433 must contain at least 3 * count entries
434 @param count The number of triples (homogeneous points) in src to read,
435 and then transform into dst.
436 */
437 void mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const;
438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 void mapXY(SkScalar x, SkScalar y, SkPoint* result) const {
440 SkASSERT(result);
441 this->getMapXYProc()(*this, x, y, result);
442 }
443
444 /** Apply this matrix to the array of vectors specified by src, and write
445 the transformed vectors into the array of vectors specified by dst.
446 This is similar to mapPoints, but ignores any translation in the matrix.
447 @param dst Where the transformed coordinates are written. It must
448 contain at least count entries
449 @param src The original coordinates that are to be transformed. It
450 must contain at least count entries
451 @param count The number of vectors in src to read, and then transform
452 into dst.
453 */
454 void mapVectors(SkVector dst[], const SkVector src[], int count) const;
455
456 /** Apply this matrix to the array of vectors specified by src, and write
457 the transformed vectors into the array of vectors specified by dst.
458 This is similar to mapPoints, but ignores any translation in the matrix.
459 @param vecs The vectors to be transformed. It must contain at least
460 count entries
461 @param count The number of vectors in vecs.
462 */
463 void mapVectors(SkVector vecs[], int count) const {
464 this->mapVectors(vecs, vecs, count);
465 }
466
467 /** Apply this matrix to the src rectangle, and write the transformed
468 rectangle into dst. This is accomplished by transforming the 4 corners
469 of src, and then setting dst to the bounds of those points.
470 @param dst Where the transformed rectangle is written.
471 @param src The original rectangle to be transformed.
472 @return the result of calling rectStaysRect()
473 */
474 bool mapRect(SkRect* dst, const SkRect& src) const;
475
476 /** Apply this matrix to the rectangle, and write the transformed rectangle
477 back into it. This is accomplished by transforming the 4 corners of
478 rect, and then setting it to the bounds of those points
479 @param rect The rectangle to transform.
480 @return the result of calling rectStaysRect()
481 */
482 bool mapRect(SkRect* rect) const {
483 return this->mapRect(rect, *rect);
484 }
485
commit-bot@chromium.org24ab3b02013-08-14 21:56:37 +0000486 /** Apply this matrix to the src rectangle, and write the four transformed
487 points into dst. The points written to dst will be the original top-left, top-right,
488 bottom-right, and bottom-left points transformed by the matrix.
489 @param dst Where the transformed quad is written.
490 @param rect The original rectangle to be transformed.
491 */
492 void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const {
493 // This could potentially be faster if we only transformed each x and y of the rect once.
494 rect.toQuad(dst);
495 this->mapPoints(dst, 4);
496 }
497
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 /** Return the mean radius of a circle after it has been mapped by
499 this matrix. NOTE: in perspective this value assumes the circle
500 has its center at the origin.
501 */
502 SkScalar mapRadius(SkScalar radius) const;
503
504 typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y,
505 SkPoint* result);
506
507 static MapXYProc GetMapXYProc(TypeMask mask) {
508 SkASSERT((mask & ~kAllMasks) == 0);
509 return gMapXYProcs[mask & kAllMasks];
510 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 MapXYProc getMapXYProc() const {
513 return GetMapXYProc(this->getType());
514 }
515
516 typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[],
517 const SkPoint src[], int count);
518
519 static MapPtsProc GetMapPtsProc(TypeMask mask) {
520 SkASSERT((mask & ~kAllMasks) == 0);
521 return gMapPtsProcs[mask & kAllMasks];
522 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 MapPtsProc getMapPtsProc() const {
525 return GetMapPtsProc(this->getType());
526 }
527
528 /** If the matrix can be stepped in X (not complex perspective)
529 then return true and if step[XY] is not null, return the step[XY] value.
530 If it cannot, return false and ignore step.
531 */
532 bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const;
533
bsalomon@google.com8fe84b52012-03-26 15:24:27 +0000534 /** Efficient comparison of two matrices. It distinguishes between zero and
535 * negative zero. It will return false when the sign of zero values is the
536 * only difference between the two matrices. It considers NaN values to be
537 * equal to themselves. So a matrix full of NaNs is "cheap equal" to
538 * another matrix full of NaNs iff the NaN values are bitwise identical
539 * while according to strict the strict == test a matrix with a NaN value
540 * is equal to nothing, including itself.
541 */
542 bool cheapEqualTo(const SkMatrix& m) const {
543 return 0 == memcmp(fMat, m.fMat, sizeof(fMat));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 }
545
junoved537422014-10-28 07:14:58 -0700546 friend SK_API bool operator==(const SkMatrix& a, const SkMatrix& b);
547 friend SK_API bool operator!=(const SkMatrix& a, const SkMatrix& b) {
reed@google.com3fb51872011-06-01 15:11:22 +0000548 return !(a == b);
549 }
reed@android.com0ad336f2009-06-29 16:02:20 +0000550
reed@android.com4b7577b2009-06-29 16:14:41 +0000551 enum {
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000552 // writeTo/readFromMemory will never return a value larger than this
reed@android.com4b7577b2009-06-29 16:14:41 +0000553 kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t)
554 };
reed@android.com0ad336f2009-06-29 16:02:20 +0000555 // return the number of bytes written, whether or not buffer is null
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000556 size_t writeToMemory(void* buffer) const;
557 /**
558 * Reads data from the buffer parameter
559 *
560 * @param buffer Memory to read from
561 * @param length Amount of memory available in the buffer
562 * @return number of bytes read (must be a multiple of 4) or
563 * 0 if there was not enough memory available
564 */
565 size_t readFromMemory(const void* buffer, size_t length);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000566
reed2cc22b82014-10-02 17:52:03 -0700567 void dump() const;
568 void toString(SkString*) const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000570 /**
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000571 * Calculates the minimum scaling factor of the matrix as computed from the SVD of the upper
572 * left 2x2. If the matrix has perspective -1 is returned.
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000573 *
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000574 * @return minumum scale factor
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000575 */
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000576 SkScalar getMinScale() const;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000577
578 /**
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000579 * Calculates the maximum scaling factor of the matrix as computed from the SVD of the upper
580 * left 2x2. If the matrix has perspective -1 is returned.
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000581 *
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000582 * @return maximum scale factor
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000583 */
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000584 SkScalar getMaxScale() const;
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000585
586 /**
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000587 * Gets both the min and max scale factors. The min scale factor is scaleFactors[0] and the max
588 * is scaleFactors[1]. If the matrix has perspective false will be returned and scaleFactors
589 * will be unchanged.
590 */
591 bool getMinMaxScales(SkScalar scaleFactors[2]) const;
skia.committer@gmail.com29de4332014-05-21 03:05:49 +0000592
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000593 /**
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000594 * Return a reference to a const identity matrix
595 */
596 static const SkMatrix& I();
597
598 /**
599 * Return a reference to a const matrix that is "invalid", one that could
600 * never be used.
601 */
602 static const SkMatrix& InvalidMatrix();
603
tomhudson@google.com317d5402011-06-24 18:30:49 +0000604 /**
commit-bot@chromium.org99bd7d82014-05-19 15:51:12 +0000605 * Return the concatenation of two matrices, a * b.
606 */
607 static SkMatrix Concat(const SkMatrix& a, const SkMatrix& b) {
608 SkMatrix result;
609 result.setConcat(a, b);
610 return result;
611 }
612
613 /**
tomhudson@google.com317d5402011-06-24 18:30:49 +0000614 * Testing routine; the matrix's type cache should never need to be
615 * manually invalidated during normal use.
616 */
617 void dirtyMatrixTypeCache() {
618 this->setTypeMask(kUnknown_Mask);
619 }
620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621private:
622 enum {
623 /** Set if the matrix will map a rectangle to another rectangle. This
624 can be true if the matrix is scale-only, or rotates a multiple of
bsalomon@google.com0e5104c2012-04-10 16:20:41 +0000625 90 degrees.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000626
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 This bit will be set on identity matrices
628 */
629 kRectStaysRect_Mask = 0x10,
630
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000631 /** Set if the perspective bit is valid even though the rest of
632 the matrix is Unknown.
633 */
634 kOnlyPerspectiveValid_Mask = 0x40,
635
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 kUnknown_Mask = 0x80,
reed@google.com3fb51872011-06-01 15:11:22 +0000637
638 kORableMasks = kTranslate_Mask |
639 kScale_Mask |
640 kAffine_Mask |
641 kPerspective_Mask,
642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 kAllMasks = kTranslate_Mask |
644 kScale_Mask |
645 kAffine_Mask |
646 kPerspective_Mask |
647 kRectStaysRect_Mask
648 };
649
tomhudson@google.com601d3122012-03-15 13:07:25 +0000650 SkScalar fMat[9];
mtklein0b544ae2014-07-08 19:37:47 -0700651 mutable SkTRacy<uint32_t> fTypeMask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652
reedd1f0ebd2014-10-02 12:58:43 -0700653 void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty) {
654 fMat[kMScaleX] = sx;
655 fMat[kMSkewX] = 0;
656 fMat[kMTransX] = tx;
657
658 fMat[kMSkewY] = 0;
659 fMat[kMScaleY] = sy;
660 fMat[kMTransY] = ty;
661
662 fMat[kMPersp0] = 0;
663 fMat[kMPersp1] = 0;
664 fMat[kMPersp2] = 1;
665
666 unsigned mask = 0;
667 if (sx != 1 || sy != 1) {
668 mask |= kScale_Mask;
669 }
670 if (tx || ty) {
671 mask |= kTranslate_Mask;
672 }
673 this->setTypeMask(mask | kRectStaysRect_Mask);
674 }
675
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 uint8_t computeTypeMask() const;
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000677 uint8_t computePerspectiveTypeMask() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678
679 void setTypeMask(int mask) {
680 // allow kUnknown or a valid mask
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000681 SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask ||
junov@chromium.org02662b72012-07-12 14:44:52 +0000682 ((kUnknown_Mask | kOnlyPerspectiveValid_Mask) & mask)
683 == (kUnknown_Mask | kOnlyPerspectiveValid_Mask));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 fTypeMask = SkToU8(mask);
685 }
reed@google.com3fb51872011-06-01 15:11:22 +0000686
687 void orTypeMask(int mask) {
688 SkASSERT((mask & kORableMasks) == mask);
689 fTypeMask = SkToU8(fTypeMask | mask);
690 }
691
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 void clearTypeMask(int mask) {
693 // only allow a valid mask
694 SkASSERT((mask & kAllMasks) == mask);
mtklein0b544ae2014-07-08 19:37:47 -0700695 fTypeMask = fTypeMask & ~mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 }
tomhudson@google.comdd5f7442011-08-30 15:13:55 +0000697
698 TypeMask getPerspectiveTypeMaskOnly() const {
699 if ((fTypeMask & kUnknown_Mask) &&
700 !(fTypeMask & kOnlyPerspectiveValid_Mask)) {
701 fTypeMask = this->computePerspectiveTypeMask();
702 }
703 return (TypeMask)(fTypeMask & 0xF);
704 }
705
706 /** Returns true if we already know that the matrix is identity;
707 false otherwise.
708 */
709 bool isTriviallyIdentity() const {
710 if (fTypeMask & kUnknown_Mask) {
711 return false;
712 }
713 return ((fTypeMask & 0xF) == 0);
714 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000715
bsalomon@google.com683c3c72012-10-31 16:50:38 +0000716 bool SK_WARN_UNUSED_RESULT invertNonIdentity(SkMatrix* inverse) const;
717
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
719 static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
720 static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
721
722 static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
723 static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
724 static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
725 static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
726 static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
727 static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
728 static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000729
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 static const MapXYProc gMapXYProcs[];
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000731
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int);
733 static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
734 static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
735 static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
736 int count);
737 static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
738 static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
739 int count);
740 static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000741
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 static const MapPtsProc gMapPtsProcs[];
743
744 friend class SkPerspIter;
745};
746
747#endif