blob: 0e7ad167c2d68f46495659b4c0131283bc6ea3d6 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.com8260a892011-06-13 14:02:52 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@google.com8260a892011-06-13 14:02:52 +00007 */
8
9
epoger@google.comec3ed6a2011-07-28 14:26:00 +000010
reed@google.com8260a892011-06-13 14:02:52 +000011#include "SkMatrix44.h"
12
13SkMatrix44::SkMatrix44() {
14 this->setIdentity();
15}
16
17SkMatrix44::SkMatrix44(const SkMatrix44& src) {
18 memcpy(this, &src, sizeof(src));
19}
20
21SkMatrix44::SkMatrix44(const SkMatrix44& a, const SkMatrix44& b) {
22 this->setConcat(a, b);
23}
24
25SkMScalar SkMatrix44::get(int row, int col) const {
26 SkASSERT(row <= 3 && row >= 0);
27 SkASSERT(col <= 3 && col >= 0);
28 return fMat[col][row];
29}
30
31void SkMatrix44::set(int row, int col, const SkMScalar& value) {
32 SkASSERT(row <= 3 && row >= 0);
33 SkASSERT(col <= 3 && col >= 0);
34 fMat[col][row] = value;
35}
36
37///////////////////////////////////////////////////////////////////////////////
38
reed@google.comda9fac02011-06-13 14:46:52 +000039void SkMatrix44::asColMajorf(float dst[]) const {
40 const SkMScalar* src = &fMat[0][0];
41#ifdef SK_MSCALAR_IS_DOUBLE
42 for (int i = 0; i < 16; ++i) {
43 dst[i] = SkMScalarToFloat(src[i]);
44 }
vollick@chromium.org5596a692012-11-13 20:12:00 +000045#elif defined SK_MSCALAR_IS_FLOAT
reed@google.comda9fac02011-06-13 14:46:52 +000046 memcpy(dst, src, 16 * sizeof(float));
47#endif
48}
49
50void SkMatrix44::asColMajord(double dst[]) const {
51 const SkMScalar* src = &fMat[0][0];
52#ifdef SK_MSCALAR_IS_DOUBLE
53 memcpy(dst, src, 16 * sizeof(double));
vollick@chromium.org5596a692012-11-13 20:12:00 +000054#elif defined SK_MSCALAR_IS_FLOAT
reed@google.comda9fac02011-06-13 14:46:52 +000055 for (int i = 0; i < 16; ++i) {
56 dst[i] = SkMScalarToDouble(src[i]);
57 }
58#endif
59}
60
61void SkMatrix44::asRowMajorf(float dst[]) const {
62 const SkMScalar* src = &fMat[0][0];
63 for (int i = 0; i < 4; ++i) {
64 dst[0] = SkMScalarToFloat(src[0]);
65 dst[4] = SkMScalarToFloat(src[1]);
66 dst[8] = SkMScalarToFloat(src[2]);
67 dst[12] = SkMScalarToFloat(src[3]);
68 src += 4;
69 dst += 1;
70 }
71}
72
73void SkMatrix44::asRowMajord(double dst[]) const {
74 const SkMScalar* src = &fMat[0][0];
75 for (int i = 0; i < 4; ++i) {
76 dst[0] = SkMScalarToDouble(src[0]);
77 dst[4] = SkMScalarToDouble(src[1]);
78 dst[8] = SkMScalarToDouble(src[2]);
79 dst[12] = SkMScalarToDouble(src[3]);
80 src += 4;
81 dst += 1;
82 }
83}
84
85///////////////////////////////////////////////////////////////////////////////
86
reed@google.com8260a892011-06-13 14:02:52 +000087bool SkMatrix44::isIdentity() const {
digit@google.com3e05f292012-01-10 10:00:59 +000088 static const SkMScalar sIdentityMat[4][4] = {
89 { 1, 0, 0, 0 },
90 { 0, 1, 0, 0 },
91 { 0, 0, 1, 0 },
92 { 0, 0, 0, 1 },
93 };
94 return !memcmp(fMat, sIdentityMat, sizeof(fMat));
reed@google.com8260a892011-06-13 14:02:52 +000095}
96
97///////////////////////////////////////////////////////////////////////////////
98
99void SkMatrix44::setIdentity() {
100 sk_bzero(fMat, sizeof(fMat));
101 fMat[0][0] = fMat[1][1] = fMat[2][2] = fMat[3][3] = 1;
102}
103
104void SkMatrix44::set3x3(SkMScalar m00, SkMScalar m01, SkMScalar m02,
105 SkMScalar m10, SkMScalar m11, SkMScalar m12,
106 SkMScalar m20, SkMScalar m21, SkMScalar m22) {
107 sk_bzero(fMat, sizeof(fMat));
108 fMat[0][0] = m00; fMat[0][1] = m01; fMat[0][2] = m02; fMat[0][3] = 0;
109 fMat[1][0] = m10; fMat[1][1] = m11; fMat[1][2] = m12; fMat[1][3] = 0;
110 fMat[2][0] = m20; fMat[2][1] = m21; fMat[2][2] = m22; fMat[2][3] = 0;
111 fMat[3][0] = 0; fMat[3][1] = 0; fMat[3][2] = 0; fMat[3][3] = 1;
112}
113
114///////////////////////////////////////////////////////////////////////////////
115
116void SkMatrix44::setTranslate(SkMScalar tx, SkMScalar ty, SkMScalar tz) {
117 this->setIdentity();
118 fMat[3][0] = tx;
119 fMat[3][1] = ty;
120 fMat[3][2] = tz;
121 fMat[3][3] = 1;
122}
123
124void SkMatrix44::preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
125 SkMatrix44 mat;
126 mat.setTranslate(dx, dy, dz);
127 this->preConcat(mat);
128}
129
130void SkMatrix44::postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
131 fMat[3][0] += dx;
132 fMat[3][1] += dy;
133 fMat[3][2] += dz;
134}
135
136///////////////////////////////////////////////////////////////////////////////
137
138void SkMatrix44::setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
139 sk_bzero(fMat, sizeof(fMat));
140 fMat[0][0] = sx;
141 fMat[1][1] = sy;
142 fMat[2][2] = sz;
143 fMat[3][3] = 1;
144}
145
146void SkMatrix44::preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
147 SkMatrix44 tmp;
148 tmp.setScale(sx, sy, sz);
149 this->preConcat(tmp);
150}
151
152void SkMatrix44::postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
153 for (int i = 0; i < 4; i++) {
154 fMat[i][0] *= sx;
155 fMat[i][1] *= sy;
156 fMat[i][2] *= sz;
157 }
158}
159
160///////////////////////////////////////////////////////////////////////////////
161
162void SkMatrix44::setRotateAbout(SkMScalar x, SkMScalar y, SkMScalar z,
163 SkMScalar radians) {
164 double len2 = x * x + y * y + z * z;
165 if (len2 != 1) {
166 if (len2 == 0) {
167 this->setIdentity();
168 return;
169 }
170 double scale = 1 / sqrt(len2);
bsalomon@google.com9d12f5c2011-09-29 18:08:18 +0000171 x = SkDoubleToMScalar(x * scale);
172 y = SkDoubleToMScalar(y * scale);
173 z = SkDoubleToMScalar(z * scale);
reed@google.com8260a892011-06-13 14:02:52 +0000174 }
175 this->setRotateAboutUnit(x, y, z, radians);
176}
177
178void SkMatrix44::setRotateAboutUnit(SkMScalar x, SkMScalar y, SkMScalar z,
179 SkMScalar radians) {
180 double c = cos(radians);
181 double s = sin(radians);
182 double C = 1 - c;
183 double xs = x * s;
184 double ys = y * s;
185 double zs = z * s;
186 double xC = x * C;
187 double yC = y * C;
188 double zC = z * C;
189 double xyC = x * yC;
190 double yzC = y * zC;
191 double zxC = z * xC;
192
193 // if you're looking at wikipedia, remember that we're column major.
bsalomon@google.com9d12f5c2011-09-29 18:08:18 +0000194 this->set3x3(SkDoubleToMScalar(x * xC + c), // scale x
195 SkDoubleToMScalar(xyC + zs), // skew x
196 SkDoubleToMScalar(zxC - ys), // trans x
197
198 SkDoubleToMScalar(xyC - zs), // skew y
199 SkDoubleToMScalar(y * yC + c), // scale y
200 SkDoubleToMScalar(yzC + xs), // trans y
201
202 SkDoubleToMScalar(zxC + ys), // persp x
203 SkDoubleToMScalar(yzC - xs), // persp y
204 SkDoubleToMScalar(z * zC + c)); // persp 2
reed@google.com8260a892011-06-13 14:02:52 +0000205}
206
207///////////////////////////////////////////////////////////////////////////////
208
209void SkMatrix44::setConcat(const SkMatrix44& a, const SkMatrix44& b) {
210 SkMScalar result[4][4];
211 for (int i = 0; i < 4; i++) {
212 for (int j = 0; j < 4; j++) {
213 double value = 0;
214 for (int k = 0; k < 4; k++) {
215 value += SkMScalarToDouble(a.fMat[k][i]) * b.fMat[j][k];
216 }
217 result[j][i] = SkDoubleToMScalar(value);
218 }
219 }
220 memcpy(fMat, result, sizeof(result));
221}
222
223///////////////////////////////////////////////////////////////////////////////
224
225static inline SkMScalar det2x2(double m00, double m01, double m10, double m11) {
bsalomon@google.com9d12f5c2011-09-29 18:08:18 +0000226 return SkDoubleToMScalar(m00 * m11 - m10 * m01);
reed@google.com8260a892011-06-13 14:02:52 +0000227}
228
229static inline double det3x3(double m00, double m01, double m02,
230 double m10, double m11, double m12,
231 double m20, double m21, double m22) {
232 return m00 * det2x2(m11, m12, m21, m22) -
233 m10 * det2x2(m01, m02, m21, m22) +
234 m20 * det2x2(m01, m02, m11, m12);
235}
236
237/** We always perform the calculation in doubles, to avoid prematurely losing
238 precision along the way. This relies on the compiler automatically
239 promoting our SkMScalar values to double (if needed).
240 */
241double SkMatrix44::determinant() const {
242 return fMat[0][0] * det3x3(fMat[1][1], fMat[1][2], fMat[1][3],
243 fMat[2][1], fMat[2][2], fMat[2][3],
244 fMat[3][1], fMat[3][2], fMat[3][3]) -
245 fMat[1][0] * det3x3(fMat[0][1], fMat[0][2], fMat[0][3],
246 fMat[2][1], fMat[2][2], fMat[2][3],
247 fMat[3][1], fMat[3][2], fMat[3][3]) +
248 fMat[2][0] * det3x3(fMat[0][1], fMat[0][2], fMat[0][3],
249 fMat[1][1], fMat[1][2], fMat[1][3],
250 fMat[3][1], fMat[3][2], fMat[3][3]) -
251 fMat[3][0] * det3x3(fMat[0][1], fMat[0][2], fMat[0][3],
252 fMat[1][1], fMat[1][2], fMat[1][3],
253 fMat[2][1], fMat[2][2], fMat[2][3]);
254}
255
256///////////////////////////////////////////////////////////////////////////////
257
258// just picked a small value. not sure how to pick the "right" one
259#define TOO_SMALL_FOR_DETERMINANT (1.e-8)
260
261static inline double dabs(double x) {
262 if (x < 0) {
263 x = -x;
264 }
265 return x;
266}
267
268bool SkMatrix44::invert(SkMatrix44* inverse) const {
269 double det = this->determinant();
270 if (dabs(det) < TOO_SMALL_FOR_DETERMINANT) {
271 return false;
272 }
273 if (NULL == inverse) {
274 return true;
275 }
276
277 // we explicitly promote to doubles to keep the intermediate values in
278 // higher precision (assuming SkMScalar isn't already a double)
279 double m00 = fMat[0][0];
280 double m01 = fMat[0][1];
281 double m02 = fMat[0][2];
282 double m03 = fMat[0][3];
283 double m10 = fMat[1][0];
284 double m11 = fMat[1][1];
285 double m12 = fMat[1][2];
286 double m13 = fMat[1][3];
287 double m20 = fMat[2][0];
288 double m21 = fMat[2][1];
289 double m22 = fMat[2][2];
290 double m23 = fMat[2][3];
291 double m30 = fMat[3][0];
292 double m31 = fMat[3][1];
293 double m32 = fMat[3][2];
294 double m33 = fMat[3][3];
295
296 double tmp[4][4];
297
298 tmp[0][0] = m12*m23*m31 - m13*m22*m31 + m13*m21*m32 - m11*m23*m32 - m12*m21*m33 + m11*m22*m33;
299 tmp[0][1] = m03*m22*m31 - m02*m23*m31 - m03*m21*m32 + m01*m23*m32 + m02*m21*m33 - m01*m22*m33;
300 tmp[0][2] = m02*m13*m31 - m03*m12*m31 + m03*m11*m32 - m01*m13*m32 - m02*m11*m33 + m01*m12*m33;
301 tmp[0][3] = m03*m12*m21 - m02*m13*m21 - m03*m11*m22 + m01*m13*m22 + m02*m11*m23 - m01*m12*m23;
302 tmp[1][0] = m13*m22*m30 - m12*m23*m30 - m13*m20*m32 + m10*m23*m32 + m12*m20*m33 - m10*m22*m33;
303 tmp[1][1] = m02*m23*m30 - m03*m22*m30 + m03*m20*m32 - m00*m23*m32 - m02*m20*m33 + m00*m22*m33;
304 tmp[1][2] = m03*m12*m30 - m02*m13*m30 - m03*m10*m32 + m00*m13*m32 + m02*m10*m33 - m00*m12*m33;
305 tmp[1][3] = m02*m13*m20 - m03*m12*m20 + m03*m10*m22 - m00*m13*m22 - m02*m10*m23 + m00*m12*m23;
306 tmp[2][0] = m11*m23*m30 - m13*m21*m30 + m13*m20*m31 - m10*m23*m31 - m11*m20*m33 + m10*m21*m33;
307 tmp[2][1] = m03*m21*m30 - m01*m23*m30 - m03*m20*m31 + m00*m23*m31 + m01*m20*m33 - m00*m21*m33;
308 tmp[2][2] = m01*m13*m30 - m03*m11*m30 + m03*m10*m31 - m00*m13*m31 - m01*m10*m33 + m00*m11*m33;
309 tmp[2][3] = m03*m11*m20 - m01*m13*m20 - m03*m10*m21 + m00*m13*m21 + m01*m10*m23 - m00*m11*m23;
310 tmp[3][0] = m12*m21*m30 - m11*m22*m30 - m12*m20*m31 + m10*m22*m31 + m11*m20*m32 - m10*m21*m32;
311 tmp[3][1] = m01*m22*m30 - m02*m21*m30 + m02*m20*m31 - m00*m22*m31 - m01*m20*m32 + m00*m21*m32;
312 tmp[3][2] = m02*m11*m30 - m01*m12*m30 - m02*m10*m31 + m00*m12*m31 + m01*m10*m32 - m00*m11*m32;
313 tmp[3][3] = m01*m12*m20 - m02*m11*m20 + m02*m10*m21 - m00*m12*m21 - m01*m10*m22 + m00*m11*m22;
314
315 double invDet = 1.0 / det;
316 for (int i = 0; i < 4; i++) {
317 for (int j = 0; j < 4; j++) {
318 inverse->fMat[i][j] = SkDoubleToMScalar(tmp[i][j] * invDet);
319 }
320 }
321 return true;
322}
323
324///////////////////////////////////////////////////////////////////////////////
325
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000326void SkMatrix44::transpose() {
327 SkTSwap(fMat[0][1], fMat[1][0]);
328 SkTSwap(fMat[0][2], fMat[2][0]);
329 SkTSwap(fMat[0][3], fMat[3][0]);
330 SkTSwap(fMat[1][2], fMat[2][1]);
331 SkTSwap(fMat[1][3], fMat[3][1]);
332 SkTSwap(fMat[2][3], fMat[3][2]);
333}
334
335///////////////////////////////////////////////////////////////////////////////
336
reed@google.com1ea95be2012-11-09 21:39:48 +0000337void SkMatrix44::mapScalars(const SkScalar src[4], SkScalar dst[4]) const {
reed@google.com8260a892011-06-13 14:02:52 +0000338 SkScalar result[4];
339 for (int i = 0; i < 4; i++) {
340 SkMScalar value = 0;
341 for (int j = 0; j < 4; j++) {
342 value += fMat[j][i] * src[j];
343 }
bsalomon@google.com72e49b82011-10-27 21:47:03 +0000344 result[i] = SkMScalarToScalar(value);
reed@google.com8260a892011-06-13 14:02:52 +0000345 }
346 memcpy(dst, result, sizeof(result));
347}
348
reed@google.com1ea95be2012-11-09 21:39:48 +0000349#ifdef SK_MSCALAR_IS_DOUBLE
350void SkMatrix44::mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const {
351 SkMScalar result[4];
352 for (int i = 0; i < 4; i++) {
353 SkMScalar value = 0;
354 for (int j = 0; j < 4; j++) {
355 value += fMat[j][i] * src[j];
356 }
357 result[i] = SkMScalarToScalar(value);
358 }
359 memcpy(dst, result, sizeof(result));
360}
361#endif
362
reed@google.com8260a892011-06-13 14:02:52 +0000363///////////////////////////////////////////////////////////////////////////////
364
365void SkMatrix44::dump() const {
tomhudson@google.com9ac4a892011-06-28 13:53:13 +0000366 static const char* format =
367 "[%g %g %g %g][%g %g %g %g][%g %g %g %g][%g %g %g %g]\n";
reed@google.com8260a892011-06-13 14:02:52 +0000368#if 0
tomhudson@google.com9ac4a892011-06-28 13:53:13 +0000369 SkDebugf(format,
reed@google.com8260a892011-06-13 14:02:52 +0000370 fMat[0][0], fMat[1][0], fMat[2][0], fMat[3][0],
371 fMat[0][1], fMat[1][1], fMat[2][1], fMat[3][1],
372 fMat[0][2], fMat[1][2], fMat[2][2], fMat[3][2],
373 fMat[0][3], fMat[1][3], fMat[2][3], fMat[3][3]);
tomhudson@google.com9ac4a892011-06-28 13:53:13 +0000374#else
375 SkDebugf(format,
376 fMat[0][0], fMat[0][1], fMat[0][2], fMat[0][3],
377 fMat[1][0], fMat[1][1], fMat[1][2], fMat[1][3],
378 fMat[2][0], fMat[2][1], fMat[2][2], fMat[2][3],
379 fMat[3][0], fMat[3][1], fMat[3][2], fMat[3][3]);
reed@google.com8260a892011-06-13 14:02:52 +0000380#endif
381}
382
383///////////////////////////////////////////////////////////////////////////////
384
385static void initFromMatrix(SkMScalar dst[4][4], const SkMatrix& src) {
386 sk_bzero(dst, 16 * sizeof(SkMScalar));
bsalomon@google.com72e49b82011-10-27 21:47:03 +0000387 dst[0][0] = SkScalarToMScalar(src[SkMatrix::kMScaleX]);
388 dst[1][0] = SkScalarToMScalar(src[SkMatrix::kMSkewX]);
389 dst[3][0] = SkScalarToMScalar(src[SkMatrix::kMTransX]);
390 dst[0][1] = SkScalarToMScalar(src[SkMatrix::kMSkewY]);
391 dst[1][1] = SkScalarToMScalar(src[SkMatrix::kMScaleY]);
392 dst[3][1] = SkScalarToMScalar(src[SkMatrix::kMTransY]);
reed@google.com8260a892011-06-13 14:02:52 +0000393 dst[2][2] = dst[3][3] = 1;
394}
395
396SkMatrix44::SkMatrix44(const SkMatrix& src) {
397 initFromMatrix(fMat, src);
398}
399
400SkMatrix44& SkMatrix44::operator=(const SkMatrix& src) {
401 initFromMatrix(fMat, src);
402 return *this;
403}
404
405SkMatrix44::operator SkMatrix() const {
406 SkMatrix dst;
407 dst.reset(); // setup our perspective correctly for identity
408
bsalomon@google.com72e49b82011-10-27 21:47:03 +0000409 dst[SkMatrix::kMScaleX] = SkMScalarToScalar(fMat[0][0]);
410 dst[SkMatrix::kMSkewX] = SkMScalarToScalar(fMat[1][0]);
411 dst[SkMatrix::kMTransX] = SkMScalarToScalar(fMat[3][0]);
reed@google.com8260a892011-06-13 14:02:52 +0000412
bsalomon@google.com72e49b82011-10-27 21:47:03 +0000413 dst[SkMatrix::kMSkewY] = SkMScalarToScalar(fMat[0][1]);
414 dst[SkMatrix::kMScaleY] = SkMScalarToScalar(fMat[1][1]);
415 dst[SkMatrix::kMTransY] = SkMScalarToScalar(fMat[3][1]);
reed@google.com8260a892011-06-13 14:02:52 +0000416
417 return dst;
418}