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