1/*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkM44.h"
9#include "include/core/SkMatrix.h"
10#include "include/private/SkVx.h"
11
12typedef skvx::Vec<4, float> sk4f;
13
14bool SkM44::operator==(const SkM44& other) const {
15 if (this == &other) {
16 return true;
17 }
18
19 sk4f a0 = sk4f::Load(fMat + 0);
20 sk4f a1 = sk4f::Load(fMat + 4);
21 sk4f a2 = sk4f::Load(fMat + 8);
22 sk4f a3 = sk4f::Load(fMat + 12);
23
24 sk4f b0 = sk4f::Load(other.fMat + 0);
25 sk4f b1 = sk4f::Load(other.fMat + 4);
26 sk4f b2 = sk4f::Load(other.fMat + 8);
27 sk4f b3 = sk4f::Load(other.fMat + 12);
28
29 auto eq = (a0 == b0) & (a1 == b1) & (a2 == b2) & (a3 == b3);
30 return (eq[0] & eq[1] & eq[2] & eq[3]) == ~0;
31}
32
33static void transpose_arrays(SkScalar dst[], const SkScalar src[]) {
34 dst[0] = src[0]; dst[1] = src[4]; dst[2] = src[8]; dst[3] = src[12];
35 dst[4] = src[1]; dst[5] = src[5]; dst[6] = src[9]; dst[7] = src[13];
36 dst[8] = src[2]; dst[9] = src[6]; dst[10] = src[10]; dst[11] = src[14];
37 dst[12] = src[3]; dst[13] = src[7]; dst[14] = src[11]; dst[15] = src[15];
38}
39
40void SkM44::getRowMajor(SkScalar v[]) const {
41 transpose_arrays(v, fMat);
42}
43
44SkM44& SkM44::setConcat(const SkM44& a, const SkM44& b) {
45 sk4f c0 = sk4f::Load(a.fMat + 0);
46 sk4f c1 = sk4f::Load(a.fMat + 4);
47 sk4f c2 = sk4f::Load(a.fMat + 8);
48 sk4f c3 = sk4f::Load(a.fMat + 12);
49
50 auto compute = [&](sk4f r) {
51 return skvx::mad(c0, r[0], skvx::mad(c1, r[1], skvx::mad(c2, r[2], c3 * r[3])));
52 };
53
54 sk4f m0 = compute(sk4f::Load(b.fMat + 0));
55 sk4f m1 = compute(sk4f::Load(b.fMat + 4));
56 sk4f m2 = compute(sk4f::Load(b.fMat + 8));
57 sk4f m3 = compute(sk4f::Load(b.fMat + 12));
58
59 m0.store(fMat + 0);
60 m1.store(fMat + 4);
61 m2.store(fMat + 8);
62 m3.store(fMat + 12);
63 return *this;
64}
65
66SkM44& SkM44::preConcat(const SkMatrix& b) {
67 sk4f c0 = sk4f::Load(fMat + 0);
68 sk4f c1 = sk4f::Load(fMat + 4);
69 sk4f c3 = sk4f::Load(fMat + 12);
70
71 auto compute = [&](float r0, float r1, float r3) {
72 return skvx::mad(c0, r0, skvx::mad(c1, r1, c3 * r3));
73 };
74
75 sk4f m0 = compute(b[0], b[3], b[6]);
76 sk4f m1 = compute(b[1], b[4], b[7]);
77 sk4f m3 = compute(b[2], b[5], b[8]);
78
79 m0.store(fMat + 0);
80 m1.store(fMat + 4);
81 m3.store(fMat + 12);
82 return *this;
83}
84
85SkM44& SkM44::preTranslate(SkScalar x, SkScalar y) {
86 sk4f c0 = sk4f::Load(fMat + 0);
87 sk4f c1 = sk4f::Load(fMat + 4);
88 sk4f c3 = sk4f::Load(fMat + 12);
89
90 // only need to update the last column
91 skvx::mad(c0, x, skvx::mad(c1, y, c3)).store(fMat + 12);
92 return *this;
93}
94
95SkM44& SkM44::preScale(SkScalar x, SkScalar y) {
96 sk4f c0 = sk4f::Load(fMat + 0);
97 sk4f c1 = sk4f::Load(fMat + 4);
98
99 (c0 * x).store(fMat + 0);
100 (c1 * y).store(fMat + 4);
101 return *this;
102}
103
104SkV4 SkM44::map(float x, float y, float z, float w) const {
105 sk4f c0 = sk4f::Load(fMat + 0);
106 sk4f c1 = sk4f::Load(fMat + 4);
107 sk4f c2 = sk4f::Load(fMat + 8);
108 sk4f c3 = sk4f::Load(fMat + 12);
109
110 SkV4 v;
111 skvx::mad(c0, x, skvx::mad(c1, y, skvx::mad(c2, z, c3 * w))).store(&v.x);
112 return v;
113}
114
115///////////////////////////////////////////////////////////////////////////////
116
117/** We always perform the calculation in doubles, to avoid prematurely losing
118 precision along the way. This relies on the compiler automatically
119 promoting our SkScalar values to double (if needed).
120 */
121double SkM44::determinant() const {
122 double a00 = fMat[0];
123 double a01 = fMat[1];
124 double a02 = fMat[2];
125 double a03 = fMat[3];
126 double a10 = fMat[4];
127 double a11 = fMat[5];
128 double a12 = fMat[6];
129 double a13 = fMat[7];
130 double a20 = fMat[8];
131 double a21 = fMat[9];
132 double a22 = fMat[10];
133 double a23 = fMat[11];
134 double a30 = fMat[12];
135 double a31 = fMat[13];
136 double a32 = fMat[14];
137 double a33 = fMat[15];
138
139 double b00 = a00 * a11 - a01 * a10;
140 double b01 = a00 * a12 - a02 * a10;
141 double b02 = a00 * a13 - a03 * a10;
142 double b03 = a01 * a12 - a02 * a11;
143 double b04 = a01 * a13 - a03 * a11;
144 double b05 = a02 * a13 - a03 * a12;
145 double b06 = a20 * a31 - a21 * a30;
146 double b07 = a20 * a32 - a22 * a30;
147 double b08 = a20 * a33 - a23 * a30;
148 double b09 = a21 * a32 - a22 * a31;
149 double b10 = a21 * a33 - a23 * a31;
150 double b11 = a22 * a33 - a23 * a32;
151
152 // Calculate the determinant
153 return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
154}
155
156///////////////////////////////////////////////////////////////////////////////
157
158bool SkM44::invert(SkM44* inverse) const {
159 double a00 = fMat[0];
160 double a01 = fMat[1];
161 double a02 = fMat[2];
162 double a03 = fMat[3];
163 double a10 = fMat[4];
164 double a11 = fMat[5];
165 double a12 = fMat[6];
166 double a13 = fMat[7];
167 double a20 = fMat[8];
168 double a21 = fMat[9];
169 double a22 = fMat[10];
170 double a23 = fMat[11];
171 double a30 = fMat[12];
172 double a31 = fMat[13];
173 double a32 = fMat[14];
174 double a33 = fMat[15];
175
176 double b00 = a00 * a11 - a01 * a10;
177 double b01 = a00 * a12 - a02 * a10;
178 double b02 = a00 * a13 - a03 * a10;
179 double b03 = a01 * a12 - a02 * a11;
180 double b04 = a01 * a13 - a03 * a11;
181 double b05 = a02 * a13 - a03 * a12;
182 double b06 = a20 * a31 - a21 * a30;
183 double b07 = a20 * a32 - a22 * a30;
184 double b08 = a20 * a33 - a23 * a30;
185 double b09 = a21 * a32 - a22 * a31;
186 double b10 = a21 * a33 - a23 * a31;
187 double b11 = a22 * a33 - a23 * a32;
188
189 // Calculate the determinant
190 double det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
191
192 double invdet = sk_ieee_double_divide(1.0, det);
193 // If det is zero, we want to return false. However, we also want to return false
194 // if 1/det overflows to infinity (i.e. det is denormalized). Both of these are
195 // handled by checking that 1/det is finite.
196 if (!SkScalarIsFinite(SkScalar(invdet))) {
197 return false;
198 }
199
200 b00 *= invdet;
201 b01 *= invdet;
202 b02 *= invdet;
203 b03 *= invdet;
204 b04 *= invdet;
205 b05 *= invdet;
206 b06 *= invdet;
207 b07 *= invdet;
208 b08 *= invdet;
209 b09 *= invdet;
210 b10 *= invdet;
211 b11 *= invdet;
212
213 SkScalar tmp[16] = {
214 SkDoubleToScalar(a11 * b11 - a12 * b10 + a13 * b09),
215 SkDoubleToScalar(a02 * b10 - a01 * b11 - a03 * b09),
216 SkDoubleToScalar(a31 * b05 - a32 * b04 + a33 * b03),
217 SkDoubleToScalar(a22 * b04 - a21 * b05 - a23 * b03),
218 SkDoubleToScalar(a12 * b08 - a10 * b11 - a13 * b07),
219 SkDoubleToScalar(a00 * b11 - a02 * b08 + a03 * b07),
220 SkDoubleToScalar(a32 * b02 - a30 * b05 - a33 * b01),
221 SkDoubleToScalar(a20 * b05 - a22 * b02 + a23 * b01),
222 SkDoubleToScalar(a10 * b10 - a11 * b08 + a13 * b06),
223 SkDoubleToScalar(a01 * b08 - a00 * b10 - a03 * b06),
224 SkDoubleToScalar(a30 * b04 - a31 * b02 + a33 * b00),
225 SkDoubleToScalar(a21 * b02 - a20 * b04 - a23 * b00),
226 SkDoubleToScalar(a11 * b07 - a10 * b09 - a12 * b06),
227 SkDoubleToScalar(a00 * b09 - a01 * b07 + a02 * b06),
228 SkDoubleToScalar(a31 * b01 - a30 * b03 - a32 * b00),
229 SkDoubleToScalar(a20 * b03 - a21 * b01 + a22 * b00),
230 };
231 if (!SkScalarsAreFinite(tmp, 16)) {
232 return false;
233 }
234 memcpy(inverse->fMat, tmp, sizeof(tmp));
235 return true;
236}
237
238SkM44 SkM44::transpose() const {
239 SkM44 trans(SkM44::kUninitialized_Constructor);
240 transpose_arrays(trans.fMat, fMat);
241 return trans;
242}
243
244SkM44& SkM44::setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle) {
245 // Taken from "Essential Mathematics for Games and Interactive Applications"
246 // James M. Van Verth and Lars M. Bishop -- third edition
247 SkScalar x = axis.x;
248 SkScalar y = axis.y;
249 SkScalar z = axis.z;
250 SkScalar c = cosAngle;
251 SkScalar s = sinAngle;
252 SkScalar t = 1 - c;
253
254 *this = { t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0,
255 t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0,
256 t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0,
257 0, 0, 0, 1 };
258 return *this;
259}
260
261SkM44& SkM44::setRotate(SkV3 axis, SkScalar radians) {
262 SkScalar len = axis.length();
263 if (len > 0 && SkScalarIsFinite(len)) {
264 this->setRotateUnit(axis * (SK_Scalar1 / len), radians);
265 } else {
266 this->setIdentity();
267 }
268 return *this;
269}
270
271///////////////////////////////////////////////////////////////////////////////
272
273void SkM44::dump() const {
274 static const char* format = "|%g %g %g %g|\n"
275 "|%g %g %g %g|\n"
276 "|%g %g %g %g|\n"
277 "|%g %g %g %g|\n";
278 SkDebugf(format,
279 fMat[0], fMat[4], fMat[8], fMat[12],
280 fMat[1], fMat[5], fMat[9], fMat[13],
281 fMat[2], fMat[6], fMat[10], fMat[14],
282 fMat[3], fMat[7], fMat[11], fMat[15]);
283}
284
285static SkV3 normalize(SkV3 v) { return v * (1.0f / v.length()); }
286
287static SkV4 v4(SkV3 v, SkScalar w) { return {v.x, v.y, v.z, w}; }
288
289SkM44 Sk3LookAt(const SkV3& eye, const SkV3& center, const SkV3& up) {
290 SkV3 f = normalize(center - eye);
291 SkV3 u = normalize(up);
292 SkV3 s = normalize(f.cross(u));
293
294 SkM44 m(SkM44::kUninitialized_Constructor);
295 if (!SkM44::Cols(v4(s, 0), v4(s.cross(f), 0), v4(-f, 0), v4(eye, 1)).invert(&m)) {
296 m.setIdentity();
297 }
298 return m;
299}
300
301SkM44 Sk3Perspective(float near, float far, float angle) {
302 SkASSERT(far > near);
303
304 float denomInv = sk_ieee_float_divide(1, far - near);
305 float halfAngle = angle * 0.5f;
306 float cot = sk_float_cos(halfAngle) / sk_float_sin(halfAngle);
307
308 SkM44 m;
309 m.setRC(0, 0, cot);
310 m.setRC(1, 1, cot);
311 m.setRC(2, 2, (far + near) * denomInv);
312 m.setRC(2, 3, 2 * far * near * denomInv);
313 m.setRC(3, 2, -1);
314 return m;
315}
316