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