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