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