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
15struct 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
52struct 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
94struct 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 */
146class SK_API SkM44 {
147public:
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
425private:
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

source code of flutter_engine/third_party/skia/include/core/SkM44.h