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#ifndef SkM44_DEFINED
9#define SkM44_DEFINED
10
11#include "include/core/SkMatrix.h"
12#include "include/core/SkScalar.h"
13
14struct SkV2 {
15 float x, y;
16
17 bool operator==(const SkV2 v) const { return x == v.x && y == v.y; }
18 bool operator!=(const SkV2 v) const { return !(*this == v); }
19
20 static SkScalar Dot(SkV2 a, SkV2 b) { return a.x * b.x + a.y * b.y; }
21 static SkScalar Cross(SkV2 a, SkV2 b) { return a.x * b.y - a.y * b.x; }
22 static SkV2 Normalize(SkV2 v) { return v * (1.0f / v.length()); }
23
24 SkV2 operator-() const { return {-x, -y}; }
25 SkV2 operator+(SkV2 v) const { return {x+v.x, y+v.y}; }
26 SkV2 operator-(SkV2 v) const { return {x-v.x, y-v.y}; }
27
28 SkV2 operator*(SkV2 v) const { return {x*v.x, y*v.y}; }
29 friend SkV2 operator*(SkV2 v, SkScalar s) { return {v.x*s, v.y*s}; }
30 friend SkV2 operator*(SkScalar s, SkV2 v) { return {v.x*s, v.y*s}; }
31
32 void operator+=(SkV2 v) { *this = *this + v; }
33 void operator-=(SkV2 v) { *this = *this - v; }
34 void operator*=(SkV2 v) { *this = *this * v; }
35 void operator*=(SkScalar s) { *this = *this * s; }
36
37 SkScalar lengthSquared() const { return Dot(*this, *this); }
38 SkScalar length() const { return SkScalarSqrt(this->lengthSquared()); }
39
40 SkScalar dot(SkV2 v) const { return Dot(*this, v); }
41 SkScalar cross(SkV2 v) const { return Cross(*this, v); }
42 SkV2 normalize() const { return Normalize(*this); }
43
44 const float* ptr() const { return &x; }
45 float* ptr() { return &x; }
46};
47
48struct SkV3 {
49 float x, y, z;
50
51 bool operator==(const SkV3& v) const {
52 return x == v.x && y == v.y && z == v.z;
53 }
54 bool operator!=(const SkV3& v) const { return !(*this == v); }
55
56 static SkScalar Dot(const SkV3& a, const SkV3& b) { return a.x*b.x + a.y*b.y + a.z*b.z; }
57 static SkV3 Cross(const SkV3& a, const SkV3& b) {
58 return { a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x };
59 }
60 static SkV3 Normalize(const SkV3& v) { return v * (1.0f / v.length()); }
61
62 SkV3 operator-() const { return {-x, -y, -z}; }
63 SkV3 operator+(const SkV3& v) const { return { x + v.x, y + v.y, z + v.z }; }
64 SkV3 operator-(const SkV3& v) const { return { x - v.x, y - v.y, z - v.z }; }
65
66 SkV3 operator*(const SkV3& v) const {
67 return { x*v.x, y*v.y, z*v.z };
68 }
69 friend SkV3 operator*(const SkV3& v, SkScalar s) {
70 return { v.x*s, v.y*s, v.z*s };
71 }
72 friend SkV3 operator*(SkScalar s, const SkV3& v) { return v*s; }
73
74 void operator+=(SkV3 v) { *this = *this + v; }
75 void operator-=(SkV3 v) { *this = *this - v; }
76 void operator*=(SkV3 v) { *this = *this * v; }
77 void operator*=(SkScalar s) { *this = *this * s; }
78
79 SkScalar lengthSquared() const { return Dot(*this, *this); }
80 SkScalar length() const { return SkScalarSqrt(Dot(*this, *this)); }
81
82 SkScalar dot(const SkV3& v) const { return Dot(*this, v); }
83 SkV3 cross(const SkV3& v) const { return Cross(*this, v); }
84 SkV3 normalize() const { return Normalize(*this); }
85
86 const float* ptr() const { return &x; }
87 float* ptr() { return &x; }
88};
89
90struct SkV4 {
91 float x, y, z, w;
92
93 bool operator==(const SkV4& v) const {
94 return x == v.x && y == v.y && z == v.z && w == v.w;
95 }
96 bool operator!=(const SkV4& v) const { return !(*this == v); }
97
98 SkV4 operator-() const { return {-x, -y, -z, -w}; }
99 SkV4 operator+(const SkV4& v) const { return { x + v.x, y + v.y, z + v.z, w + v.w }; }
100 SkV4 operator-(const SkV4& v) const { return { x - v.x, y - v.y, z - v.z, w - v.w }; }
101
102 SkV4 operator*(const SkV4& v) const {
103 return { x*v.x, y*v.y, z*v.z, w*v.w };
104 }
105 friend SkV4 operator*(const SkV4& v, SkScalar s) {
106 return { v.x*s, v.y*s, v.z*s, v.w*s };
107 }
108 friend SkV4 operator*(SkScalar s, const SkV4& v) { return v*s; }
109
110 const float* ptr() const { return &x; }
111 float* ptr() { return &x; }
112};
113
114/**
115 * 4x4 matrix used by SkCanvas and other parts of Skia.
116 *
117 * Skia assumes a right-handed coordinate system:
118 * +X goes to the right
119 * +Y goes down
120 * +Z goes into the screen (away from the viewer)
121 */
122class SkM44 {
123public:
124 SkM44(const SkM44& src) = default;
125 SkM44& operator=(const SkM44& src) = default;
126
127 constexpr SkM44()
128 : fMat{1, 0, 0, 0,
129 0, 1, 0, 0,
130 0, 0, 1, 0,
131 0, 0, 0, 1}
132 {}
133
134 SkM44(const SkM44& a, const SkM44& b) {
135 this->setConcat(a, b);
136 }
137
138 enum Uninitialized_Constructor {
139 kUninitialized_Constructor
140 };
141 SkM44(Uninitialized_Constructor) {}
142
143 enum NaN_Constructor {
144 kNaN_Constructor
145 };
146 SkM44(NaN_Constructor)
147 : fMat{SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN,
148 SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN,
149 SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN,
150 SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN}
151 {}
152
153 /**
154 * Parameters are treated as row-major.
155 */
156 SkM44(SkScalar m0, SkScalar m4, SkScalar m8, SkScalar m12,
157 SkScalar m1, SkScalar m5, SkScalar m9, SkScalar m13,
158 SkScalar m2, SkScalar m6, SkScalar m10, SkScalar m14,
159 SkScalar m3, SkScalar m7, SkScalar m11, SkScalar m15)
160 {
161 fMat[0] = m0; fMat[4] = m4; fMat[8] = m8; fMat[12] = m12;
162 fMat[1] = m1; fMat[5] = m5; fMat[9] = m9; fMat[13] = m13;
163 fMat[2] = m2; fMat[6] = m6; fMat[10] = m10; fMat[14] = m14;
164 fMat[3] = m3; fMat[7] = m7; fMat[11] = m11; fMat[15] = m15;
165 }
166
167 static SkM44 Rows(const SkV4& r0, const SkV4& r1, const SkV4& r2, const SkV4& r3) {
168 SkM44 m(kUninitialized_Constructor);
169 m.setRow(0, r0);
170 m.setRow(1, r1);
171 m.setRow(2, r2);
172 m.setRow(3, r3);
173 return m;
174 }
175 static SkM44 Cols(const SkV4& c0, const SkV4& c1, const SkV4& c2, const SkV4& c3) {
176 SkM44 m(kUninitialized_Constructor);
177 m.setCol(0, c0);
178 m.setCol(1, c1);
179 m.setCol(2, c2);
180 m.setCol(3, c3);
181 return m;
182 }
183
184 static SkM44 RowMajor(const SkScalar r[16]) {
185 return SkM44(r[ 0], r[ 1], r[ 2], r[ 3],
186 r[ 4], r[ 5], r[ 6], r[ 7],
187 r[ 8], r[ 9], r[10], r[11],
188 r[12], r[13], r[14], r[15]);
189 }
190 static SkM44 ColMajor(const SkScalar c[16]) {
191 return SkM44(c[0], c[4], c[ 8], c[12],
192 c[1], c[5], c[ 9], c[13],
193 c[2], c[6], c[10], c[14],
194 c[3], c[7], c[11], c[15]);
195 }
196
197 static SkM44 Translate(SkScalar x, SkScalar y, SkScalar z = 0) {
198 return SkM44(1, 0, 0, x,
199 0, 1, 0, y,
200 0, 0, 1, z,
201 0, 0, 0, 1);
202 }
203
204 static SkM44 Scale(SkScalar x, SkScalar y, SkScalar z = 1) {
205 return SkM44(x, 0, 0, 0,
206 0, y, 0, 0,
207 0, 0, z, 0,
208 0, 0, 0, 1);
209 }
210
211 static SkM44 Rotate(SkV3 axis, SkScalar radians) {
212 SkM44 m(kUninitialized_Constructor);
213 m.setRotate(axis, radians);
214 return m;
215 }
216
217 bool operator==(const SkM44& other) const;
218 bool operator!=(const SkM44& other) const {
219 return !(other == *this);
220 }
221
222 void getColMajor(SkScalar v[]) const {
223 memcpy(v, fMat, sizeof(fMat));
224 }
225 void getRowMajor(SkScalar v[]) const;
226
227 SkScalar rc(int r, int c) const {
228 SkASSERT(r >= 0 && r <= 3);
229 SkASSERT(c >= 0 && c <= 3);
230 return fMat[c*4 + r];
231 }
232 void setRC(int r, int c, SkScalar value) {
233 SkASSERT(r >= 0 && r <= 3);
234 SkASSERT(c >= 0 && c <= 3);
235 fMat[c*4 + r] = value;
236 }
237
238 SkV4 row(int i) const {
239 SkASSERT(i >= 0 && i <= 3);
240 return {fMat[i + 0], fMat[i + 4], fMat[i + 8], fMat[i + 12]};
241 }
242 SkV4 col(int i) const {
243 SkASSERT(i >= 0 && i <= 3);
244 return {fMat[i*4 + 0], fMat[i*4 + 1], fMat[i*4 + 2], fMat[i*4 + 3]};
245 }
246
247 void setRow(int i, const SkV4& v) {
248 SkASSERT(i >= 0 && i <= 3);
249 fMat[i + 0] = v.x;
250 fMat[i + 4] = v.y;
251 fMat[i + 8] = v.z;
252 fMat[i + 12] = v.w;
253 }
254 void setCol(int i, const SkV4& v) {
255 SkASSERT(i >= 0 && i <= 3);
256 memcpy(&fMat[i*4], v.ptr(), sizeof(v));
257 }
258
259 SkM44& setIdentity() {
260 *this = { 1, 0, 0, 0,
261 0, 1, 0, 0,
262 0, 0, 1, 0,
263 0, 0, 0, 1 };
264 return *this;
265 }
266
267 SkM44& setTranslate(SkScalar x, SkScalar y, SkScalar z = 0) {
268 *this = { 1, 0, 0, x,
269 0, 1, 0, y,
270 0, 0, 1, z,
271 0, 0, 0, 1 };
272 return *this;
273 }
274
275 SkM44& setScale(SkScalar x, SkScalar y, SkScalar z = 1) {
276 *this = { x, 0, 0, 0,
277 0, y, 0, 0,
278 0, 0, z, 0,
279 0, 0, 0, 1 };
280 return *this;
281 }
282
283 /**
284 * Set this matrix to rotate about the specified unit-length axis vector,
285 * by an angle specified by its sin() and cos().
286 *
287 * This does not attempt to verify that axis.length() == 1 or that the sin,cos values
288 * are correct.
289 */
290 SkM44& setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle);
291
292 /**
293 * Set this matrix to rotate about the specified unit-length axis vector,
294 * by an angle specified in radians.
295 *
296 * This does not attempt to verify that axis.length() == 1.
297 */
298 SkM44& setRotateUnit(SkV3 axis, SkScalar radians) {
299 return this->setRotateUnitSinCos(axis, SkScalarSin(radians), SkScalarCos(radians));
300 }
301
302 /**
303 * Set this matrix to rotate about the specified axis vector,
304 * by an angle specified in radians.
305 *
306 * Note: axis is not assumed to be unit-length, so it will be normalized internally.
307 * If axis is already unit-length, call setRotateAboutUnitRadians() instead.
308 */
309 SkM44& setRotate(SkV3 axis, SkScalar radians);
310
311 SkM44& setConcat(const SkM44& a, const SkM44& b);
312
313 friend SkM44 operator*(const SkM44& a, const SkM44& b) {
314 return SkM44(a, b);
315 }
316
317 SkM44& preConcat(const SkM44& m) {
318 return this->setConcat(*this, m);
319 }
320
321 /** If this is invertible, return that in inverse and return true. If it is
322 * not invertible, return false and leave the inverse parameter unchanged.
323 */
324 bool SK_WARN_UNUSED_RESULT invert(SkM44* inverse) const;
325
326 SkM44 transpose() const;
327
328 void dump() const;
329
330 ////////////
331
332 SkV4 map(float x, float y, float z, float w) const;
333 SkV4 operator*(const SkV4& v) const {
334 return this->map(v.x, v.y, v.z, v.w);
335 }
336 SkV3 operator*(SkV3 v) const {
337 auto v4 = this->map(v.x, v.y, v.z, 0);
338 return {v4.x, v4.y, v4.z};
339 }
340
341 ////////////////////// Converting to/from SkMatrix
342
343 /* When converting from SkM44 to SkMatrix, the third row and
344 * column is dropped. When converting from SkMatrix to SkM44
345 * the third row and column remain as identity:
346 * [ a b c ] [ a b 0 c ]
347 * [ d e f ] -> [ d e 0 f ]
348 * [ g h i ] [ 0 0 1 0 ]
349 * [ g h 0 i ]
350 */
351 SkMatrix asM33() const {
352 return SkMatrix::MakeAll(fMat[0], fMat[4], fMat[12],
353 fMat[1], fMat[5], fMat[13],
354 fMat[3], fMat[7], fMat[15]);
355 }
356
357 SkM44(const SkMatrix& src)
358 : SkM44(src[SkMatrix::kMScaleX], src[SkMatrix::kMSkewX], 0, src[SkMatrix::kMTransX],
359 src[SkMatrix::kMSkewY], src[SkMatrix::kMScaleY], 0, src[SkMatrix::kMTransY],
360 0, 0, 1, 0,
361 src[SkMatrix::kMPersp0], src[SkMatrix::kMPersp1], 0, src[SkMatrix::kMPersp2])
362 {}
363
364 SkM44& operator=(const SkMatrix& src) {
365 *this = SkM44(src);
366 return *this;
367 }
368
369 SkM44& preTranslate(SkScalar x, SkScalar y);
370 SkM44& preScale(SkScalar x, SkScalar y);
371 SkM44& preConcat(const SkMatrix&);
372
373private:
374 /* Stored in column-major.
375 * Indices
376 * 0 4 8 12 1 0 0 trans_x
377 * 1 5 9 13 e.g. 0 1 0 trans_y
378 * 2 6 10 14 0 0 1 trans_z
379 * 3 7 11 15 0 0 0 1
380 */
381 SkScalar fMat[16];
382
383 double determinant() const;
384
385 friend class SkMatrixPriv;
386};
387
388SkM44 Sk3LookAt(const SkV3& eye, const SkV3& center, const SkV3& up);
389SkM44 Sk3Perspective(float near, float far, float angle);
390
391#endif
392