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