Matrix.h (82092B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZILLA_GFX_MATRIX_H_ 8 #define MOZILLA_GFX_MATRIX_H_ 9 10 #include "Types.h" 11 #include "Triangle.h" 12 #include "Rect.h" 13 #include "Point.h" 14 #include "Quaternion.h" 15 #include <iosfwd> 16 #include <math.h> 17 #include "mozilla/Attributes.h" 18 #include "mozilla/DebugOnly.h" 19 #include "mozilla/FloatingPoint.h" 20 #include "mozilla/gfx/ScaleFactors2D.h" 21 #include "mozilla/Span.h" 22 23 namespace mozilla { 24 namespace gfx { 25 26 static inline bool FuzzyEqual(Float aV1, Float aV2) { 27 // XXX - Check if fabs does the smart thing and just negates the sign bit. 28 return fabs(aV2 - aV1) < 1e-6; 29 } 30 31 template <typename F> 32 Span<Point4DTyped<UnknownUnits, F>> IntersectPolygon( 33 Span<Point4DTyped<UnknownUnits, F>> aPoints, 34 const Point4DTyped<UnknownUnits, F>& aPlaneNormal, 35 Span<Point4DTyped<UnknownUnits, F>> aDestBuffer); 36 37 template <class T> 38 using BaseMatrixScales = BaseScaleFactors2D<UnknownUnits, UnknownUnits, T>; 39 40 using MatrixScales = BaseMatrixScales<float>; 41 using MatrixScalesDouble = BaseMatrixScales<double>; 42 43 template <class T> 44 class BaseMatrix { 45 // Alias that maps to either Point or PointDouble depending on whether T is a 46 // float or a double. 47 typedef PointTyped<UnknownUnits, T> MatrixPoint; 48 // Same for size and rect 49 typedef SizeTyped<UnknownUnits, T> MatrixSize; 50 typedef RectTyped<UnknownUnits, T> MatrixRect; 51 52 public: 53 BaseMatrix() : _11(1.0f), _12(0), _21(0), _22(1.0f), _31(0), _32(0) {} 54 BaseMatrix(T a11, T a12, T a21, T a22, T a31, T a32) 55 : _11(a11), _12(a12), _21(a21), _22(a22), _31(a31), _32(a32) {} 56 union { 57 struct { 58 T _11, _12; 59 T _21, _22; 60 T _31, _32; 61 }; 62 T components[6]; 63 }; 64 65 template <class T2> 66 explicit BaseMatrix(const BaseMatrix<T2>& aOther) 67 : _11(aOther._11), 68 _12(aOther._12), 69 _21(aOther._21), 70 _22(aOther._22), 71 _31(aOther._31), 72 _32(aOther._32) {} 73 74 MOZ_ALWAYS_INLINE BaseMatrix Copy() const { return BaseMatrix<T>(*this); } 75 76 friend std::ostream& operator<<(std::ostream& aStream, 77 const BaseMatrix& aMatrix) { 78 if (aMatrix.IsIdentity()) { 79 return aStream << "[ I ]"; 80 } 81 return aStream << "[ " << aMatrix._11 << " " << aMatrix._12 << "; " 82 << aMatrix._21 << " " << aMatrix._22 << "; " << aMatrix._31 83 << " " << aMatrix._32 << "; ]"; 84 } 85 86 MatrixPoint TransformPoint(const MatrixPoint& aPoint) const { 87 MatrixPoint retPoint; 88 89 retPoint.x = aPoint.x * _11 + aPoint.y * _21 + _31; 90 retPoint.y = aPoint.x * _12 + aPoint.y * _22 + _32; 91 92 return retPoint; 93 } 94 95 MatrixSize TransformSize(const MatrixSize& aSize) const { 96 MatrixSize retSize; 97 98 retSize.width = aSize.width * _11 + aSize.height * _21; 99 retSize.height = aSize.width * _12 + aSize.height * _22; 100 101 return retSize; 102 } 103 104 /** 105 * In most cases you probably want to use TransformBounds. This function 106 * just transforms the top-left and size separately and constructs a rect 107 * from those results. 108 */ 109 MatrixRect TransformRect(const MatrixRect& aRect) const { 110 return MatrixRect(TransformPoint(aRect.TopLeft()), 111 TransformSize(aRect.Size())); 112 } 113 114 GFX2D_API MatrixRect TransformBounds(const MatrixRect& aRect) const { 115 int i; 116 MatrixPoint quad[4]; 117 T min_x, max_x; 118 T min_y, max_y; 119 120 quad[0] = TransformPoint(aRect.TopLeft()); 121 quad[1] = TransformPoint(aRect.TopRight()); 122 quad[2] = TransformPoint(aRect.BottomLeft()); 123 quad[3] = TransformPoint(aRect.BottomRight()); 124 125 min_x = max_x = quad[0].x; 126 min_y = max_y = quad[0].y; 127 128 for (i = 1; i < 4; i++) { 129 if (quad[i].x < min_x) min_x = quad[i].x; 130 if (quad[i].x > max_x) max_x = quad[i].x; 131 132 if (quad[i].y < min_y) min_y = quad[i].y; 133 if (quad[i].y > max_y) max_y = quad[i].y; 134 } 135 136 return MatrixRect(min_x, min_y, max_x - min_x, max_y - min_y); 137 } 138 139 static BaseMatrix<T> Translation(T aX, T aY) { 140 return BaseMatrix<T>(1.0f, 0.0f, 0.0f, 1.0f, aX, aY); 141 } 142 143 static BaseMatrix<T> Translation(MatrixPoint aPoint) { 144 return Translation(aPoint.x, aPoint.y); 145 } 146 147 /** 148 * Apply a translation to this matrix. 149 * 150 * The "Pre" in this method's name means that the translation is applied 151 * -before- this matrix's existing transformation. That is, any vector that 152 * is multiplied by the resulting matrix will first be translated, then be 153 * transformed by the original transform. 154 * 155 * Calling this method will result in this matrix having the same value as 156 * the result of: 157 * 158 * BaseMatrix<T>::Translation(x, y) * this 159 * 160 * (Note that in performance critical code multiplying by the result of a 161 * Translation()/Scaling() call is not recommended since that results in a 162 * full matrix multiply involving 12 floating-point multiplications. Calling 163 * this method would be preferred since it only involves four floating-point 164 * multiplications.) 165 */ 166 BaseMatrix<T>& PreTranslate(T aX, T aY) { 167 _31 += _11 * aX + _21 * aY; 168 _32 += _12 * aX + _22 * aY; 169 170 return *this; 171 } 172 173 BaseMatrix<T>& PreTranslate(const MatrixPoint& aPoint) { 174 return PreTranslate(aPoint.x, aPoint.y); 175 } 176 177 /** 178 * Similar to PreTranslate, but the translation is applied -after- this 179 * matrix's existing transformation instead of before it. 180 * 181 * This method is generally less used than PreTranslate since typically code 182 * want to adjust an existing user space to device space matrix to create a 183 * transform to device space from a -new- user space (translated from the 184 * previous user space). In that case consumers will need to use the Pre* 185 * variants of the matrix methods rather than using the Post* methods, since 186 * the Post* methods add a transform to the device space end of the 187 * transformation. 188 */ 189 BaseMatrix<T>& PostTranslate(T aX, T aY) { 190 _31 += aX; 191 _32 += aY; 192 return *this; 193 } 194 195 BaseMatrix<T>& PostTranslate(const MatrixPoint& aPoint) { 196 return PostTranslate(aPoint.x, aPoint.y); 197 } 198 199 static BaseMatrix<T> Scaling(T aScaleX, T aScaleY) { 200 return BaseMatrix<T>(aScaleX, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f); 201 } 202 203 static BaseMatrix<T> Scaling(const BaseMatrixScales<T>& scale) { 204 return Scaling(scale.xScale, scale.yScale); 205 } 206 207 /** 208 * Similar to PreTranslate, but applies a scale instead of a translation. 209 */ 210 BaseMatrix<T>& PreScale(T aX, T aY) { 211 _11 *= aX; 212 _12 *= aX; 213 _21 *= aY; 214 _22 *= aY; 215 216 return *this; 217 } 218 219 BaseMatrix<T>& PreScale(const BaseMatrixScales<T>& scale) { 220 return PreScale(scale.xScale, scale.yScale); 221 } 222 223 /** 224 * Similar to PostTranslate, but applies a scale instead of a translation. 225 */ 226 BaseMatrix<T>& PostScale(T aScaleX, T aScaleY) { 227 _11 *= aScaleX; 228 _12 *= aScaleY; 229 _21 *= aScaleX; 230 _22 *= aScaleY; 231 _31 *= aScaleX; 232 _32 *= aScaleY; 233 234 return *this; 235 } 236 237 GFX2D_API static BaseMatrix<T> Rotation(T aAngle); 238 239 /** 240 * Similar to PreTranslate, but applies a rotation instead of a translation. 241 */ 242 BaseMatrix<T>& PreRotate(T aAngle) { 243 return *this = BaseMatrix<T>::Rotation(aAngle) * *this; 244 } 245 246 bool Invert() { 247 // Compute co-factors. 248 T A = _22; 249 T B = -_21; 250 T C = _21 * _32 - _22 * _31; 251 T D = -_12; 252 T E = _11; 253 T F = _31 * _12 - _11 * _32; 254 255 T det = Determinant(); 256 257 if (!det) { 258 return false; 259 } 260 261 T inv_det = 1 / det; 262 263 _11 = inv_det * A; 264 _12 = inv_det * D; 265 _21 = inv_det * B; 266 _22 = inv_det * E; 267 _31 = inv_det * C; 268 _32 = inv_det * F; 269 270 return true; 271 } 272 273 BaseMatrix<T> Inverse() const { 274 BaseMatrix<T> clone = *this; 275 DebugOnly<bool> inverted = clone.Invert(); 276 MOZ_ASSERT(inverted, 277 "Attempted to get the inverse of a non-invertible matrix"); 278 return clone; 279 } 280 281 T Determinant() const { return _11 * _22 - _12 * _21; } 282 283 BaseMatrix<T> operator*(const BaseMatrix<T>& aMatrix) const { 284 BaseMatrix<T> resultMatrix; 285 286 resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21; 287 resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22; 288 resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21; 289 resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22; 290 resultMatrix._31 = 291 this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._31; 292 resultMatrix._32 = 293 this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._32; 294 295 return resultMatrix; 296 } 297 298 BaseMatrix<T>& operator*=(const BaseMatrix<T>& aMatrix) { 299 *this = *this * aMatrix; 300 return *this; 301 } 302 303 /** 304 * Multiplies *this with aMatrix and returns the result. 305 */ 306 Matrix4x4 operator*(const Matrix4x4& aMatrix) const; 307 308 /** 309 * Multiplies in the opposite order to operator=*. 310 */ 311 BaseMatrix<T>& PreMultiply(const BaseMatrix<T>& aMatrix) { 312 *this = aMatrix * *this; 313 return *this; 314 } 315 316 /** 317 * Please explicitly use either FuzzyEquals or ExactlyEquals. 318 */ 319 bool operator==(const BaseMatrix<T>& other) const = delete; 320 bool operator!=(const BaseMatrix<T>& other) const = delete; 321 322 /* Returns true if the other matrix is fuzzy-equal to this matrix. 323 * Note that this isn't a cheap comparison! 324 */ 325 bool FuzzyEquals(const BaseMatrix<T>& o) const { 326 return FuzzyEqual(_11, o._11) && FuzzyEqual(_12, o._12) && 327 FuzzyEqual(_21, o._21) && FuzzyEqual(_22, o._22) && 328 FuzzyEqual(_31, o._31) && FuzzyEqual(_32, o._32); 329 } 330 331 bool ExactlyEquals(const BaseMatrix<T>& o) const { 332 return _11 == o._11 && _12 == o._12 && _21 == o._21 && _22 == o._22 && 333 _31 == o._31 && _32 == o._32; 334 } 335 336 /* Verifies that the matrix contains no Infs or NaNs. */ 337 bool IsFinite() const { 338 return std::isfinite(_11) && std::isfinite(_12) && std::isfinite(_21) && 339 std::isfinite(_22) && std::isfinite(_31) && std::isfinite(_32); 340 } 341 342 /* Returns true if the matrix is a rectilinear transformation (i.e. 343 * grid-aligned rectangles are transformed to grid-aligned rectangles) 344 */ 345 bool IsRectilinear() const { 346 if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) { 347 return true; 348 } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) { 349 return true; 350 } 351 352 return false; 353 } 354 355 /** 356 * Returns true if the matrix is anything other than a straight 357 * translation by integers. 358 */ 359 bool HasNonIntegerTranslation() const { 360 return HasNonTranslation() || !FuzzyEqual(_31, floor(_31 + 0.5f)) || 361 !FuzzyEqual(_32, floor(_32 + 0.5f)); 362 } 363 364 /** 365 * Returns true if the matrix only has an integer translation. 366 */ 367 bool HasOnlyIntegerTranslation() const { return !HasNonIntegerTranslation(); } 368 369 /** 370 * Returns true if the matrix has any transform other 371 * than a straight translation. 372 */ 373 bool HasNonTranslation() const { 374 return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) || 375 !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0); 376 } 377 378 /** 379 * Returns true if the matrix has any transform other 380 * than a translation or a -1 y scale (y axis flip) 381 */ 382 bool HasNonTranslationOrFlip() const { 383 return !FuzzyEqual(_11, 1.0) || 384 (!FuzzyEqual(_22, 1.0) && !FuzzyEqual(_22, -1.0)) || 385 !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0); 386 } 387 388 /* Returns true if the matrix is an identity matrix. 389 */ 390 bool IsIdentity() const { 391 return _11 == 1.0f && _12 == 0.0f && _21 == 0.0f && _22 == 1.0f && 392 _31 == 0.0f && _32 == 0.0f; 393 } 394 395 /* Returns true if the matrix is singular. 396 */ 397 bool IsSingular() const { 398 T det = Determinant(); 399 return !std::isfinite(det) || det == 0; 400 } 401 402 GFX2D_API BaseMatrix<T>& NudgeToIntegers() { 403 NudgeToInteger(&_11); 404 NudgeToInteger(&_12); 405 NudgeToInteger(&_21); 406 NudgeToInteger(&_22); 407 NudgeToInteger(&_31); 408 NudgeToInteger(&_32); 409 return *this; 410 } 411 412 bool IsTranslation() const { 413 return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) && 414 FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f); 415 } 416 417 static bool FuzzyIsInteger(T aValue) { 418 return FuzzyEqual(aValue, floorf(aValue + 0.5f)); 419 } 420 421 bool IsIntegerTranslation() const { 422 return IsTranslation() && FuzzyIsInteger(_31) && FuzzyIsInteger(_32); 423 } 424 425 bool IsAllIntegers() const { 426 return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) && FuzzyIsInteger(_21) && 427 FuzzyIsInteger(_22) && FuzzyIsInteger(_31) && FuzzyIsInteger(_32); 428 } 429 430 MatrixPoint GetTranslation() const { return MatrixPoint(_31, _32); } 431 432 /** 433 * Returns true if matrix is multiple of 90 degrees rotation with flipping, 434 * scaling and translation. 435 */ 436 bool PreservesAxisAlignedRectangles() const { 437 return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0)) || 438 (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0))); 439 } 440 441 /** 442 * Returns true if the matrix has any transform other 443 * than a translation or scale; this is, if there is 444 * rotation. 445 */ 446 bool HasNonAxisAlignedTransform() const { 447 return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0); 448 } 449 450 /** 451 * Returns true if the matrix has negative scaling (i.e. flip). 452 */ 453 bool HasNegativeScaling() const { return (_11 < 0.0) || (_22 < 0.0); } 454 455 /** 456 * Computes the scale factors of this matrix; that is, 457 * the amounts each basis vector is scaled by. 458 */ 459 BaseMatrixScales<T> ScaleFactors() const { 460 T det = Determinant(); 461 462 if (det == 0.0) { 463 return BaseMatrixScales<T>(0.0, 0.0); 464 } 465 466 MatrixSize sz = MatrixSize(1.0, 0.0); 467 sz = TransformSize(sz); 468 469 T major = sqrt(sz.width * sz.width + sz.height * sz.height); 470 T minor = 0.0; 471 472 // ignore mirroring 473 if (det < 0.0) { 474 det = -det; 475 } 476 477 if (major) { 478 minor = det / major; 479 } 480 481 return BaseMatrixScales<T>(major, minor); 482 } 483 484 /** 485 * Returns true if the matrix preserves distances, i.e. a rigid transformation 486 * that doesn't change size or shape). Such a matrix has uniform unit scaling 487 * and an orthogonal basis. 488 */ 489 bool PreservesDistance() const { 490 return FuzzyEqual(_11 * _11 + _12 * _12, 1.0) && 491 FuzzyEqual(_21 * _21 + _22 * _22, 1.0) && 492 FuzzyEqual(_11 * _21 + _12 * _22, 0.0); 493 } 494 }; 495 496 typedef BaseMatrix<Float> Matrix; 497 typedef BaseMatrix<Double> MatrixDouble; 498 499 // Helper functions used by Matrix4x4Typed defined in Matrix.cpp 500 double SafeTangent(double aTheta); 501 double FlushToZero(double aVal); 502 503 template <class Units, class F> 504 Point4DTyped<Units, F> ComputePerspectivePlaneIntercept( 505 const Point4DTyped<Units, F>& aFirst, 506 const Point4DTyped<Units, F>& aSecond) { 507 // This function will always return a point with a w value of 0. 508 // The X, Y, and Z components will point towards an infinite vanishing 509 // point. 510 511 // We want to interpolate aFirst and aSecond to find the point intersecting 512 // with the w=0 plane. 513 514 // Since we know what we want the w component to be, we can rearrange the 515 // interpolation equation and solve for t. 516 float t = -aFirst.w / (aSecond.w - aFirst.w); 517 518 // Use t to find the remainder of the components 519 return aFirst + (aSecond - aFirst) * t; 520 } 521 522 template <class SourceUnits, class TargetUnits, class T> 523 class Matrix4x4Typed { 524 public: 525 typedef PointTyped<SourceUnits, T> SourcePoint; 526 typedef PointTyped<TargetUnits, T> TargetPoint; 527 typedef Point3DTyped<SourceUnits, T> SourcePoint3D; 528 typedef Point3DTyped<TargetUnits, T> TargetPoint3D; 529 typedef Point4DTyped<SourceUnits, T> SourcePoint4D; 530 typedef Point4DTyped<TargetUnits, T> TargetPoint4D; 531 typedef RectTyped<SourceUnits, T> SourceRect; 532 typedef RectTyped<TargetUnits, T> TargetRect; 533 534 Matrix4x4Typed() 535 : _11(1.0f), 536 _12(0.0f), 537 _13(0.0f), 538 _14(0.0f), 539 _21(0.0f), 540 _22(1.0f), 541 _23(0.0f), 542 _24(0.0f), 543 _31(0.0f), 544 _32(0.0f), 545 _33(1.0f), 546 _34(0.0f), 547 _41(0.0f), 548 _42(0.0f), 549 _43(0.0f), 550 _44(1.0f) {} 551 552 Matrix4x4Typed(T a11, T a12, T a13, T a14, T a21, T a22, T a23, T a24, T a31, 553 T a32, T a33, T a34, T a41, T a42, T a43, T a44) 554 : _11(a11), 555 _12(a12), 556 _13(a13), 557 _14(a14), 558 _21(a21), 559 _22(a22), 560 _23(a23), 561 _24(a24), 562 _31(a31), 563 _32(a32), 564 _33(a33), 565 _34(a34), 566 _41(a41), 567 _42(a42), 568 _43(a43), 569 _44(a44) {} 570 571 explicit Matrix4x4Typed(const T aArray[16]) { 572 memcpy(components, aArray, sizeof(components)); 573 } 574 575 Matrix4x4Typed(const Matrix4x4Typed& aOther) { 576 memcpy(components, aOther.components, sizeof(components)); 577 } 578 579 template <class T2> 580 explicit Matrix4x4Typed( 581 const Matrix4x4Typed<SourceUnits, TargetUnits, T2>& aOther) 582 : _11(aOther._11), 583 _12(aOther._12), 584 _13(aOther._13), 585 _14(aOther._14), 586 _21(aOther._21), 587 _22(aOther._22), 588 _23(aOther._23), 589 _24(aOther._24), 590 _31(aOther._31), 591 _32(aOther._32), 592 _33(aOther._33), 593 _34(aOther._34), 594 _41(aOther._41), 595 _42(aOther._42), 596 _43(aOther._43), 597 _44(aOther._44) {} 598 599 union { 600 struct { 601 T _11, _12, _13, _14; 602 T _21, _22, _23, _24; 603 T _31, _32, _33, _34; 604 T _41, _42, _43, _44; 605 }; 606 T components[16]; 607 }; 608 609 friend std::ostream& operator<<(std::ostream& aStream, 610 const Matrix4x4Typed& aMatrix) { 611 if (aMatrix.Is2D()) { 612 BaseMatrix<T> matrix = aMatrix.As2D(); 613 return aStream << matrix; 614 } 615 const T* f = &aMatrix._11; 616 aStream << "[ " << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';'; 617 f += 4; 618 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';'; 619 f += 4; 620 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';'; 621 f += 4; 622 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] 623 << "; ]"; 624 return aStream; 625 } 626 627 Point4DTyped<UnknownUnits, T>& operator[](int aIndex) { 628 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); 629 return *reinterpret_cast<Point4DTyped<UnknownUnits, T>*>((&_11) + 630 4 * aIndex); 631 } 632 const Point4DTyped<UnknownUnits, T>& operator[](int aIndex) const { 633 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); 634 return *reinterpret_cast<const Point4DTyped<UnknownUnits, T>*>((&_11) + 635 4 * aIndex); 636 } 637 638 // External code should avoid calling this, and instead use 639 // ViewAs() from UnitTransforms.h, which requires providing 640 // a justification. 641 template <typename NewMatrix4x4Typed> 642 [[nodiscard]] NewMatrix4x4Typed Cast() const { 643 return NewMatrix4x4Typed(_11, _12, _13, _14, _21, _22, _23, _24, _31, _32, 644 _33, _34, _41, _42, _43, _44); 645 } 646 647 /** 648 * Returns true if the matrix is isomorphic to a 2D affine transformation. 649 */ 650 bool Is2D() const { 651 if (_13 != 0.0f || _14 != 0.0f || _23 != 0.0f || _24 != 0.0f || 652 _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f || 653 _43 != 0.0f || _44 != 1.0f) { 654 return false; 655 } 656 return true; 657 } 658 659 bool Is2D(BaseMatrix<T>* aMatrix) const { 660 if (!Is2D()) { 661 return false; 662 } 663 if (aMatrix) { 664 aMatrix->_11 = _11; 665 aMatrix->_12 = _12; 666 aMatrix->_21 = _21; 667 aMatrix->_22 = _22; 668 aMatrix->_31 = _41; 669 aMatrix->_32 = _42; 670 } 671 return true; 672 } 673 674 BaseMatrix<T> As2D() const { 675 MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform"); 676 677 return BaseMatrix<T>(_11, _12, _21, _22, _41, _42); 678 } 679 680 bool CanDraw2D(BaseMatrix<T>* aMatrix = nullptr) const { 681 if (_14 != 0.0f || _24 != 0.0f || _44 != 1.0f) { 682 return false; 683 } 684 if (aMatrix) { 685 aMatrix->_11 = _11; 686 aMatrix->_12 = _12; 687 aMatrix->_21 = _21; 688 aMatrix->_22 = _22; 689 aMatrix->_31 = _41; 690 aMatrix->_32 = _42; 691 } 692 return true; 693 } 694 695 Matrix4x4Typed& ProjectTo2D() { 696 _31 = 0.0f; 697 _32 = 0.0f; 698 _13 = 0.0f; 699 _23 = 0.0f; 700 _33 = 1.0f; 701 _43 = 0.0f; 702 _34 = 0.0f; 703 // Some matrices, such as those derived from perspective transforms, 704 // can modify _44 from 1, while leaving the rest of the fourth column 705 // (_14, _24) at 0. In this case, after resetting the third row and 706 // third column above, the value of _44 functions only to scale the 707 // coordinate transform divide by W. The matrix can be converted to 708 // a true 2D matrix by normalizing out the scaling effect of _44 on 709 // the remaining components ahead of time. 710 if (_14 == 0.0f && _24 == 0.0f && _44 != 1.0f && _44 != 0.0f) { 711 T scale = 1.0f / _44; 712 _11 *= scale; 713 _12 *= scale; 714 _21 *= scale; 715 _22 *= scale; 716 _41 *= scale; 717 _42 *= scale; 718 _44 = 1.0f; 719 } 720 return *this; 721 } 722 723 template <class F> 724 Point4DTyped<TargetUnits, F> ProjectPoint( 725 const PointTyped<SourceUnits, F>& aPoint) const { 726 // Find a value for z that will transform to 0. 727 728 // The transformed value of z is computed as: 729 // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43; 730 731 // Solving for z when z' = 0 gives us: 732 F z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33; 733 734 // Compute the transformed point 735 return this->TransformPoint( 736 Point4DTyped<SourceUnits, F>(aPoint.x, aPoint.y, z, 1)); 737 } 738 739 template <class F> 740 RectTyped<TargetUnits, F> ProjectRectBounds( 741 const RectTyped<SourceUnits, F>& aRect, 742 const RectTyped<TargetUnits, F>& aClip) const { 743 // This function must never return std::numeric_limits<Float>::max() or any 744 // other arbitrary large value in place of inifinity. This often occurs 745 // when aRect is an inversed projection matrix or when aRect is transformed 746 // to be partly behind and in front of the camera (w=0 plane in homogenous 747 // coordinates) - See Bug 1035611 748 749 // Some call-sites will call RoundGfxRectToAppRect which clips both the 750 // extents and dimensions of the rect to be bounded by nscoord_MAX. 751 // If we return a Rect that, when converted to nscoords, has a width or 752 // height greater than nscoord_MAX, RoundGfxRectToAppRect will clip the 753 // overflow off both the min and max end of the rect after clipping the 754 // extents of the rect, resulting in a translation of the rect towards the 755 // infinite end. 756 757 // The bounds returned by ProjectRectBounds are expected to be clipped only 758 // on the edges beyond the bounds of the coordinate system; otherwise, the 759 // clipped bounding box would be smaller than the correct one and result 760 // bugs such as incorrect culling (eg. Bug 1073056) 761 762 // To address this without requiring all code to work in homogenous 763 // coordinates or interpret infinite values correctly, a specialized 764 // clipping function is integrated into ProjectRectBounds. 765 766 // Callers should pass an aClip value that represents the extents to clip 767 // the result to, in the same coordinate system as aRect. 768 Point4DTyped<TargetUnits, F> points[4]; 769 770 points[0] = ProjectPoint(aRect.TopLeft()); 771 points[1] = ProjectPoint(aRect.TopRight()); 772 points[2] = ProjectPoint(aRect.BottomRight()); 773 points[3] = ProjectPoint(aRect.BottomLeft()); 774 775 F min_x = std::numeric_limits<F>::max(); 776 F min_y = std::numeric_limits<F>::max(); 777 F max_x = -std::numeric_limits<F>::max(); 778 F max_y = -std::numeric_limits<F>::max(); 779 780 for (int i = 0; i < 4; i++) { 781 // Only use points that exist above the w=0 plane 782 if (points[i].HasPositiveWCoord()) { 783 PointTyped<TargetUnits, F> point2d = 784 aClip.ClampPoint(points[i].As2DPoint()); 785 min_x = std::min<F>(point2d.x, min_x); 786 max_x = std::max<F>(point2d.x, max_x); 787 min_y = std::min<F>(point2d.y, min_y); 788 max_y = std::max<F>(point2d.y, max_y); 789 } 790 791 int next = (i == 3) ? 0 : i + 1; 792 if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) { 793 // If the line between two points crosses the w=0 plane, then 794 // interpolate to find the point of intersection with the w=0 plane and 795 // use that instead. 796 Point4DTyped<TargetUnits, F> intercept = 797 ComputePerspectivePlaneIntercept(points[i], points[next]); 798 // Since intercept.w will always be 0 here, we interpret x,y,z as a 799 // direction towards an infinite vanishing point. 800 if (intercept.x < 0.0f) { 801 min_x = aClip.X(); 802 } else if (intercept.x > 0.0f) { 803 max_x = aClip.XMost(); 804 } 805 if (intercept.y < 0.0f) { 806 min_y = aClip.Y(); 807 } else if (intercept.y > 0.0f) { 808 max_y = aClip.YMost(); 809 } 810 } 811 } 812 813 if (max_x < min_x || max_y < min_y) { 814 return RectTyped<TargetUnits, F>(0, 0, 0, 0); 815 } 816 817 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, 818 max_y - min_y); 819 } 820 821 /** 822 * TransformAndClipBounds transforms aRect as a bounding box, while clipping 823 * the transformed bounds to the extents of aClip. 824 */ 825 template <class F> 826 RectTyped<TargetUnits, F> TransformAndClipBounds( 827 const RectTyped<SourceUnits, F>& aRect, 828 const RectTyped<TargetUnits, F>& aClip) const { 829 PointTyped<UnknownUnits, F> verts[kTransformAndClipRectMaxVerts]; 830 size_t vertCount = TransformAndClipRect(aRect, aClip, verts); 831 832 F min_x = std::numeric_limits<F>::max(); 833 F min_y = std::numeric_limits<F>::max(); 834 F max_x = -std::numeric_limits<F>::max(); 835 F max_y = -std::numeric_limits<F>::max(); 836 for (size_t i = 0; i < vertCount; i++) { 837 min_x = std::min(min_x, verts[i].x.value); 838 max_x = std::max(max_x, verts[i].x.value); 839 min_y = std::min(min_y, verts[i].y.value); 840 max_y = std::max(max_y, verts[i].y.value); 841 } 842 843 if (max_x < min_x || max_y < min_y) { 844 return RectTyped<TargetUnits, F>(0, 0, 0, 0); 845 } 846 847 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, 848 max_y - min_y); 849 } 850 851 template <class F> 852 RectTyped<TargetUnits, F> TransformAndClipBounds( 853 const TriangleTyped<SourceUnits, F>& aTriangle, 854 const RectTyped<TargetUnits, F>& aClip) const { 855 return TransformAndClipBounds(aTriangle.BoundingBox(), aClip); 856 } 857 858 /** 859 * TransformAndClipRect projects a rectangle and clips against view frustum 860 * clipping planes in homogenous space so that its projected vertices are 861 * constrained within the 2d rectangle passed in aClip. 862 * The resulting vertices are populated in aVerts. aVerts must be 863 * pre-allocated to hold at least kTransformAndClipRectMaxVerts Points. 864 * The vertex count is returned by TransformAndClipRect. It is possible to 865 * emit fewer than 3 vertices, indicating that aRect will not be visible 866 * within aClip. 867 */ 868 template <class F> 869 size_t TransformAndClipRect(const RectTyped<SourceUnits, F>& aRect, 870 const RectTyped<TargetUnits, F>& aClip, 871 PointTyped<TargetUnits, F>* aVerts) const { 872 typedef Point4DTyped<UnknownUnits, F> P4D; 873 874 // The initial polygon is made up by the corners of aRect in homogenous 875 // space, mapped into the destination space of this transform. 876 P4D rectCorners[] = { 877 TransformPoint(P4D(aRect.X(), aRect.Y(), 0, 1)), 878 TransformPoint(P4D(aRect.XMost(), aRect.Y(), 0, 1)), 879 TransformPoint(P4D(aRect.XMost(), aRect.YMost(), 0, 1)), 880 TransformPoint(P4D(aRect.X(), aRect.YMost(), 0, 1)), 881 }; 882 883 // Cut off pieces of the polygon that are outside of aClip (the "view 884 // frustrum"), by consecutively intersecting the polygon with the half space 885 // induced by the clipping plane for each side of aClip. 886 // View frustum clipping planes are described as normals originating from 887 // the 0,0,0,0 origin. 888 // Each pass can increase or decrease the number of points that make up the 889 // current clipped polygon. We double buffer the set of points, alternating 890 // between polygonBufA and polygonBufB. Duplicated points in the polygons 891 // are kept around until all clipping is done. The loop at the end filters 892 // out any consecutive duplicates. 893 P4D polygonBufA[kTransformAndClipRectMaxVerts]; 894 P4D polygonBufB[kTransformAndClipRectMaxVerts]; 895 896 Span<P4D> polygon(rectCorners); 897 polygon = IntersectPolygon<F>(polygon, P4D(1.0, 0.0, 0.0, -aClip.X()), 898 polygonBufA); 899 polygon = IntersectPolygon<F>(polygon, P4D(-1.0, 0.0, 0.0, aClip.XMost()), 900 polygonBufB); 901 polygon = IntersectPolygon<F>(polygon, P4D(0.0, 1.0, 0.0, -aClip.Y()), 902 polygonBufA); 903 polygon = IntersectPolygon<F>(polygon, P4D(0.0, -1.0, 0.0, aClip.YMost()), 904 polygonBufB); 905 906 size_t vertCount = 0; 907 for (const auto& srcPoint : polygon) { 908 PointTyped<TargetUnits, F> p; 909 if (srcPoint.w == 0.0) { 910 // If a point lies on the intersection of the clipping planes at 911 // (0,0,0,0), we must avoid a division by zero w component. 912 p = PointTyped<TargetUnits, F>(0.0, 0.0); 913 } else { 914 p = srcPoint.As2DPoint(); 915 } 916 // Emit only unique points 917 if (vertCount == 0 || p != aVerts[vertCount - 1]) { 918 aVerts[vertCount++] = p; 919 } 920 } 921 922 return vertCount; 923 } 924 925 static const int kTransformAndClipRectMaxVerts = 32; 926 927 static Matrix4x4Typed From2D(const BaseMatrix<T>& aMatrix) { 928 Matrix4x4Typed matrix; 929 matrix._11 = aMatrix._11; 930 matrix._12 = aMatrix._12; 931 matrix._21 = aMatrix._21; 932 matrix._22 = aMatrix._22; 933 matrix._41 = aMatrix._31; 934 matrix._42 = aMatrix._32; 935 return matrix; 936 } 937 938 bool Is2DIntegerTranslation() const { 939 return Is2D() && As2D().IsIntegerTranslation(); 940 } 941 942 TargetPoint4D TransposeTransform4D(const SourcePoint4D& aPoint) const { 943 Float x = aPoint.x * _11 + aPoint.y * _12 + aPoint.z * _13 + aPoint.w * _14; 944 Float y = aPoint.x * _21 + aPoint.y * _22 + aPoint.z * _23 + aPoint.w * _24; 945 Float z = aPoint.x * _31 + aPoint.y * _32 + aPoint.z * _33 + aPoint.w * _34; 946 Float w = aPoint.x * _41 + aPoint.y * _42 + aPoint.z * _43 + aPoint.w * _44; 947 948 return TargetPoint4D(x, y, z, w); 949 } 950 951 template <class F> 952 Point4DTyped<TargetUnits, F> TransformPoint( 953 const Point4DTyped<SourceUnits, F>& aPoint) const { 954 Point4DTyped<TargetUnits, F> retPoint; 955 956 retPoint.x = 957 aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + aPoint.w * _41; 958 retPoint.y = 959 aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + aPoint.w * _42; 960 retPoint.z = 961 aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + aPoint.w * _43; 962 retPoint.w = 963 aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + aPoint.w * _44; 964 965 return retPoint; 966 } 967 968 template <class F> 969 Point3DTyped<TargetUnits, F> TransformPoint( 970 const Point3DTyped<SourceUnits, F>& aPoint) const { 971 Point3DTyped<TargetUnits, F> result; 972 result.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41; 973 result.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42; 974 result.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43; 975 976 result /= (aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44); 977 978 return result; 979 } 980 981 template <class F> 982 PointTyped<TargetUnits, F> TransformPoint( 983 const PointTyped<SourceUnits, F>& aPoint) const { 984 Point4DTyped<SourceUnits, F> temp(aPoint.x, aPoint.y, 0, 1); 985 return TransformPoint(temp).As2DPoint(); 986 } 987 988 template <class F> 989 GFX2D_API RectTyped<TargetUnits, F> TransformBounds( 990 const RectTyped<SourceUnits, F>& aRect) const { 991 // If you change this also change Matrix4x4TypedFlagged::TransformBounds to 992 // match. 993 PointTyped<TargetUnits, F> quad[4]; 994 F min_x, max_x; 995 F min_y, max_y; 996 997 quad[0] = TransformPoint(aRect.TopLeft()); 998 quad[1] = TransformPoint(aRect.TopRight()); 999 quad[2] = TransformPoint(aRect.BottomLeft()); 1000 quad[3] = TransformPoint(aRect.BottomRight()); 1001 1002 min_x = max_x = quad[0].x; 1003 min_y = max_y = quad[0].y; 1004 1005 for (int i = 1; i < 4; i++) { 1006 if (quad[i].x < min_x) { 1007 min_x = quad[i].x; 1008 } 1009 if (quad[i].x > max_x) { 1010 max_x = quad[i].x; 1011 } 1012 1013 if (quad[i].y < min_y) { 1014 min_y = quad[i].y; 1015 } 1016 if (quad[i].y > max_y) { 1017 max_y = quad[i].y; 1018 } 1019 } 1020 1021 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, 1022 max_y - min_y); 1023 } 1024 1025 static Matrix4x4Typed Translation(T aX, T aY, T aZ) { 1026 return Matrix4x4Typed(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1027 0.0f, 1.0f, 0.0f, aX, aY, aZ, 1.0f); 1028 } 1029 1030 static Matrix4x4Typed Translation(const TargetPoint3D& aP) { 1031 return Translation(aP.x, aP.y, aP.z); 1032 } 1033 1034 static Matrix4x4Typed Translation(const TargetPoint& aP) { 1035 return Translation(aP.x, aP.y, 0); 1036 } 1037 1038 /** 1039 * Apply a translation to this matrix. 1040 * 1041 * The "Pre" in this method's name means that the translation is applied 1042 * -before- this matrix's existing transformation. That is, any vector that 1043 * is multiplied by the resulting matrix will first be translated, then be 1044 * transformed by the original transform. 1045 * 1046 * Calling this method will result in this matrix having the same value as 1047 * the result of: 1048 * 1049 * Matrix4x4::Translation(x, y) * this 1050 * 1051 * (Note that in performance critical code multiplying by the result of a 1052 * Translation()/Scaling() call is not recommended since that results in a 1053 * full matrix multiply involving 64 floating-point multiplications. Calling 1054 * this method would be preferred since it only involves 12 floating-point 1055 * multiplications.) 1056 */ 1057 Matrix4x4Typed& PreTranslate(T aX, T aY, T aZ) { 1058 _41 += aX * _11 + aY * _21 + aZ * _31; 1059 _42 += aX * _12 + aY * _22 + aZ * _32; 1060 _43 += aX * _13 + aY * _23 + aZ * _33; 1061 _44 += aX * _14 + aY * _24 + aZ * _34; 1062 1063 return *this; 1064 } 1065 1066 Matrix4x4Typed& PreTranslate(const Point3DTyped<UnknownUnits, T>& aPoint) { 1067 return PreTranslate(aPoint.x, aPoint.y, aPoint.z); 1068 } 1069 1070 /** 1071 * Similar to PreTranslate, but the translation is applied -after- this 1072 * matrix's existing transformation instead of before it. 1073 * 1074 * This method is generally less used than PreTranslate since typically code 1075 * wants to adjust an existing user space to device space matrix to create a 1076 * transform to device space from a -new- user space (translated from the 1077 * previous user space). In that case consumers will need to use the Pre* 1078 * variants of the matrix methods rather than using the Post* methods, since 1079 * the Post* methods add a transform to the device space end of the 1080 * transformation. 1081 */ 1082 Matrix4x4Typed& PostTranslate(T aX, T aY, T aZ) { 1083 _11 += _14 * aX; 1084 _21 += _24 * aX; 1085 _31 += _34 * aX; 1086 _41 += _44 * aX; 1087 _12 += _14 * aY; 1088 _22 += _24 * aY; 1089 _32 += _34 * aY; 1090 _42 += _44 * aY; 1091 _13 += _14 * aZ; 1092 _23 += _24 * aZ; 1093 _33 += _34 * aZ; 1094 _43 += _44 * aZ; 1095 1096 return *this; 1097 } 1098 1099 Matrix4x4Typed& PostTranslate(const TargetPoint3D& aPoint) { 1100 return PostTranslate(aPoint.x, aPoint.y, aPoint.z); 1101 } 1102 1103 Matrix4x4Typed& PostTranslate(const TargetPoint& aPoint) { 1104 return PostTranslate(aPoint.x, aPoint.y, 0); 1105 } 1106 1107 static Matrix4x4Typed Scaling(T aScaleX, T aScaleY, T aScaleZ) { 1108 return Matrix4x4Typed(aScaleX, 0.0f, 0.0f, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f, 1109 0.0f, 0.0f, aScaleZ, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); 1110 } 1111 1112 /** 1113 * Similar to PreTranslate, but applies a scale instead of a translation. 1114 */ 1115 Matrix4x4Typed& PreScale(T aX, T aY, T aZ) { 1116 _11 *= aX; 1117 _12 *= aX; 1118 _13 *= aX; 1119 _14 *= aX; 1120 _21 *= aY; 1121 _22 *= aY; 1122 _23 *= aY; 1123 _24 *= aY; 1124 _31 *= aZ; 1125 _32 *= aZ; 1126 _33 *= aZ; 1127 _34 *= aZ; 1128 1129 return *this; 1130 } 1131 1132 template <typename NewSourceUnits> 1133 [[nodiscard]] Matrix4x4Typed<NewSourceUnits, TargetUnits> PreScale( 1134 const ScaleFactor<NewSourceUnits, SourceUnits>& aScale) const { 1135 auto clone = Cast<Matrix4x4Typed<NewSourceUnits, TargetUnits>>(); 1136 clone.PreScale(aScale.scale, aScale.scale, 1); 1137 return clone; 1138 } 1139 1140 template <typename NewSourceUnits> 1141 [[nodiscard]] Matrix4x4Typed<NewSourceUnits, TargetUnits> PreScale( 1142 const BaseScaleFactors2D<NewSourceUnits, SourceUnits, T>& aScale) const { 1143 auto clone = Cast<Matrix4x4Typed<NewSourceUnits, TargetUnits>>(); 1144 clone.PreScale(aScale.xScale, aScale.yScale, 1); 1145 return clone; 1146 } 1147 1148 /** 1149 * Similar to PostTranslate, but applies a scale instead of a translation. 1150 */ 1151 Matrix4x4Typed& PostScale(T aScaleX, T aScaleY, T aScaleZ) { 1152 _11 *= aScaleX; 1153 _21 *= aScaleX; 1154 _31 *= aScaleX; 1155 _41 *= aScaleX; 1156 _12 *= aScaleY; 1157 _22 *= aScaleY; 1158 _32 *= aScaleY; 1159 _42 *= aScaleY; 1160 _13 *= aScaleZ; 1161 _23 *= aScaleZ; 1162 _33 *= aScaleZ; 1163 _43 *= aScaleZ; 1164 1165 return *this; 1166 } 1167 1168 template <typename NewTargetUnits> 1169 [[nodiscard]] Matrix4x4Typed<SourceUnits, NewTargetUnits> PostScale( 1170 const ScaleFactor<TargetUnits, NewTargetUnits>& aScale) const { 1171 auto clone = Cast<Matrix4x4Typed<SourceUnits, NewTargetUnits>>(); 1172 clone.PostScale(aScale.scale, aScale.scale, 1); 1173 return clone; 1174 } 1175 1176 template <typename NewTargetUnits> 1177 [[nodiscard]] Matrix4x4Typed<SourceUnits, NewTargetUnits> PostScale( 1178 const BaseScaleFactors2D<TargetUnits, NewTargetUnits, T>& aScale) const { 1179 auto clone = Cast<Matrix4x4Typed<SourceUnits, NewTargetUnits>>(); 1180 clone.PostScale(aScale.xScale, aScale.yScale, 1); 1181 return clone; 1182 } 1183 1184 void SkewXY(T aSkew) { (*this)[1] += (*this)[0] * aSkew; } 1185 1186 void SkewXZ(T aSkew) { (*this)[2] += (*this)[0] * aSkew; } 1187 1188 void SkewYZ(T aSkew) { (*this)[2] += (*this)[1] * aSkew; } 1189 1190 Matrix4x4Typed& ChangeBasis(const Point3DTyped<UnknownUnits, T>& aOrigin) { 1191 return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z); 1192 } 1193 1194 Matrix4x4Typed& ChangeBasis(T aX, T aY, T aZ) { 1195 // Translate to the origin before applying this matrix 1196 PreTranslate(-aX, -aY, -aZ); 1197 1198 // Translate back into position after applying this matrix 1199 PostTranslate(aX, aY, aZ); 1200 1201 return *this; 1202 } 1203 1204 Matrix4x4Typed& Transpose() { 1205 std::swap(_12, _21); 1206 std::swap(_13, _31); 1207 std::swap(_14, _41); 1208 1209 std::swap(_23, _32); 1210 std::swap(_24, _42); 1211 1212 std::swap(_34, _43); 1213 1214 return *this; 1215 } 1216 1217 bool operator==(const Matrix4x4Typed& o) const { 1218 // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics 1219 return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 && 1220 _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 && 1221 _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 && 1222 _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44; 1223 } 1224 1225 bool operator!=(const Matrix4x4Typed& o) const { return !((*this) == o); } 1226 1227 Matrix4x4Typed& operator=(const Matrix4x4Typed& aOther) = default; 1228 1229 template <typename NewTargetUnits> 1230 Matrix4x4Typed<SourceUnits, NewTargetUnits, T> operator*( 1231 const Matrix4x4Typed<TargetUnits, NewTargetUnits, T>& aMatrix) const { 1232 Matrix4x4Typed<SourceUnits, NewTargetUnits, T> matrix; 1233 1234 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 + 1235 _14 * aMatrix._41; 1236 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 + 1237 _24 * aMatrix._41; 1238 matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 + 1239 _34 * aMatrix._41; 1240 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 + 1241 _44 * aMatrix._41; 1242 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 + 1243 _14 * aMatrix._42; 1244 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 + 1245 _24 * aMatrix._42; 1246 matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 + 1247 _34 * aMatrix._42; 1248 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 + 1249 _44 * aMatrix._42; 1250 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 + 1251 _14 * aMatrix._43; 1252 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 + 1253 _24 * aMatrix._43; 1254 matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 + 1255 _34 * aMatrix._43; 1256 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 + 1257 _44 * aMatrix._43; 1258 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 + 1259 _14 * aMatrix._44; 1260 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 + 1261 _24 * aMatrix._44; 1262 matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 + 1263 _34 * aMatrix._44; 1264 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 + 1265 _44 * aMatrix._44; 1266 1267 return matrix; 1268 } 1269 1270 Matrix4x4Typed& operator*=( 1271 const Matrix4x4Typed<TargetUnits, TargetUnits, T>& aMatrix) { 1272 *this = *this * aMatrix; 1273 return *this; 1274 } 1275 1276 /* Returns true if the matrix is an identity matrix. 1277 */ 1278 bool IsIdentity() const { 1279 return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f && 1280 _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f && 1281 _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f && 1282 _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f; 1283 } 1284 1285 bool IsSingular() const { return Determinant() == 0.0; } 1286 1287 T Determinant() const { 1288 return _14 * _23 * _32 * _41 - _13 * _24 * _32 * _41 - 1289 _14 * _22 * _33 * _41 + _12 * _24 * _33 * _41 + 1290 _13 * _22 * _34 * _41 - _12 * _23 * _34 * _41 - 1291 _14 * _23 * _31 * _42 + _13 * _24 * _31 * _42 + 1292 _14 * _21 * _33 * _42 - _11 * _24 * _33 * _42 - 1293 _13 * _21 * _34 * _42 + _11 * _23 * _34 * _42 + 1294 _14 * _22 * _31 * _43 - _12 * _24 * _31 * _43 - 1295 _14 * _21 * _32 * _43 + _11 * _24 * _32 * _43 + 1296 _12 * _21 * _34 * _43 - _11 * _22 * _34 * _43 - 1297 _13 * _22 * _31 * _44 + _12 * _23 * _31 * _44 + 1298 _13 * _21 * _32 * _44 - _11 * _23 * _32 * _44 - 1299 _12 * _21 * _33 * _44 + _11 * _22 * _33 * _44; 1300 } 1301 1302 // Invert() is not unit-correct. Prefer Inverse() where possible. 1303 bool Invert() { 1304 T det = Determinant(); 1305 if (!det) { 1306 return false; 1307 } 1308 1309 Matrix4x4Typed<SourceUnits, TargetUnits, T> result; 1310 result._11 = _23 * _34 * _42 - _24 * _33 * _42 + _24 * _32 * _43 - 1311 _22 * _34 * _43 - _23 * _32 * _44 + _22 * _33 * _44; 1312 result._12 = _14 * _33 * _42 - _13 * _34 * _42 - _14 * _32 * _43 + 1313 _12 * _34 * _43 + _13 * _32 * _44 - _12 * _33 * _44; 1314 result._13 = _13 * _24 * _42 - _14 * _23 * _42 + _14 * _22 * _43 - 1315 _12 * _24 * _43 - _13 * _22 * _44 + _12 * _23 * _44; 1316 result._14 = _14 * _23 * _32 - _13 * _24 * _32 - _14 * _22 * _33 + 1317 _12 * _24 * _33 + _13 * _22 * _34 - _12 * _23 * _34; 1318 result._21 = _24 * _33 * _41 - _23 * _34 * _41 - _24 * _31 * _43 + 1319 _21 * _34 * _43 + _23 * _31 * _44 - _21 * _33 * _44; 1320 result._22 = _13 * _34 * _41 - _14 * _33 * _41 + _14 * _31 * _43 - 1321 _11 * _34 * _43 - _13 * _31 * _44 + _11 * _33 * _44; 1322 result._23 = _14 * _23 * _41 - _13 * _24 * _41 - _14 * _21 * _43 + 1323 _11 * _24 * _43 + _13 * _21 * _44 - _11 * _23 * _44; 1324 result._24 = _13 * _24 * _31 - _14 * _23 * _31 + _14 * _21 * _33 - 1325 _11 * _24 * _33 - _13 * _21 * _34 + _11 * _23 * _34; 1326 result._31 = _22 * _34 * _41 - _24 * _32 * _41 + _24 * _31 * _42 - 1327 _21 * _34 * _42 - _22 * _31 * _44 + _21 * _32 * _44; 1328 result._32 = _14 * _32 * _41 - _12 * _34 * _41 - _14 * _31 * _42 + 1329 _11 * _34 * _42 + _12 * _31 * _44 - _11 * _32 * _44; 1330 result._33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 - 1331 _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44; 1332 result._34 = _14 * _22 * _31 - _12 * _24 * _31 - _14 * _21 * _32 + 1333 _11 * _24 * _32 + _12 * _21 * _34 - _11 * _22 * _34; 1334 result._41 = _23 * _32 * _41 - _22 * _33 * _41 - _23 * _31 * _42 + 1335 _21 * _33 * _42 + _22 * _31 * _43 - _21 * _32 * _43; 1336 result._42 = _12 * _33 * _41 - _13 * _32 * _41 + _13 * _31 * _42 - 1337 _11 * _33 * _42 - _12 * _31 * _43 + _11 * _32 * _43; 1338 result._43 = _13 * _22 * _41 - _12 * _23 * _41 - _13 * _21 * _42 + 1339 _11 * _23 * _42 + _12 * _21 * _43 - _11 * _22 * _43; 1340 result._44 = _12 * _23 * _31 - _13 * _22 * _31 + _13 * _21 * _32 - 1341 _11 * _23 * _32 - _12 * _21 * _33 + _11 * _22 * _33; 1342 1343 result._11 /= det; 1344 result._12 /= det; 1345 result._13 /= det; 1346 result._14 /= det; 1347 result._21 /= det; 1348 result._22 /= det; 1349 result._23 /= det; 1350 result._24 /= det; 1351 result._31 /= det; 1352 result._32 /= det; 1353 result._33 /= det; 1354 result._34 /= det; 1355 result._41 /= det; 1356 result._42 /= det; 1357 result._43 /= det; 1358 result._44 /= det; 1359 *this = result; 1360 1361 return true; 1362 } 1363 1364 Matrix4x4Typed<TargetUnits, SourceUnits, T> Inverse() const { 1365 typedef Matrix4x4Typed<TargetUnits, SourceUnits, T> InvertedMatrix; 1366 InvertedMatrix clone = Cast<InvertedMatrix>(); 1367 DebugOnly<bool> inverted = clone.Invert(); 1368 MOZ_ASSERT(inverted, 1369 "Attempted to get the inverse of a non-invertible matrix"); 1370 return clone; 1371 } 1372 1373 Maybe<Matrix4x4Typed<TargetUnits, SourceUnits, T>> MaybeInverse() const { 1374 typedef Matrix4x4Typed<TargetUnits, SourceUnits, T> InvertedMatrix; 1375 InvertedMatrix clone = Cast<InvertedMatrix>(); 1376 if (clone.Invert()) { 1377 return Some(clone); 1378 } 1379 return Nothing(); 1380 } 1381 1382 void Normalize() { 1383 for (int i = 0; i < 4; i++) { 1384 for (int j = 0; j < 4; j++) { 1385 (*this)[i][j] /= (*this)[3][3]; 1386 } 1387 } 1388 } 1389 1390 bool FuzzyEqual(const Matrix4x4Typed& o) const { 1391 return gfx::FuzzyEqual(_11, o._11) && gfx::FuzzyEqual(_12, o._12) && 1392 gfx::FuzzyEqual(_13, o._13) && gfx::FuzzyEqual(_14, o._14) && 1393 gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) && 1394 gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) && 1395 gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) && 1396 gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) && 1397 gfx::FuzzyEqual(_41, o._41) && gfx::FuzzyEqual(_42, o._42) && 1398 gfx::FuzzyEqual(_43, o._43) && gfx::FuzzyEqual(_44, o._44); 1399 } 1400 1401 bool FuzzyEqualsMultiplicative(const Matrix4x4Typed& o) const { 1402 return ::mozilla::FuzzyEqualsMultiplicative(_11, o._11) && 1403 ::mozilla::FuzzyEqualsMultiplicative(_12, o._12) && 1404 ::mozilla::FuzzyEqualsMultiplicative(_13, o._13) && 1405 ::mozilla::FuzzyEqualsMultiplicative(_14, o._14) && 1406 ::mozilla::FuzzyEqualsMultiplicative(_21, o._21) && 1407 ::mozilla::FuzzyEqualsMultiplicative(_22, o._22) && 1408 ::mozilla::FuzzyEqualsMultiplicative(_23, o._23) && 1409 ::mozilla::FuzzyEqualsMultiplicative(_24, o._24) && 1410 ::mozilla::FuzzyEqualsMultiplicative(_31, o._31) && 1411 ::mozilla::FuzzyEqualsMultiplicative(_32, o._32) && 1412 ::mozilla::FuzzyEqualsMultiplicative(_33, o._33) && 1413 ::mozilla::FuzzyEqualsMultiplicative(_34, o._34) && 1414 ::mozilla::FuzzyEqualsMultiplicative(_41, o._41) && 1415 ::mozilla::FuzzyEqualsMultiplicative(_42, o._42) && 1416 ::mozilla::FuzzyEqualsMultiplicative(_43, o._43) && 1417 ::mozilla::FuzzyEqualsMultiplicative(_44, o._44); 1418 } 1419 1420 bool IsBackfaceVisible() const { 1421 // Inverse()._33 < 0; 1422 T det = Determinant(); 1423 T __33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 - 1424 _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44; 1425 return (__33 * det) < 0; 1426 } 1427 1428 Matrix4x4Typed& NudgeToIntegersFixedEpsilon() { 1429 NudgeToInteger(&_11); 1430 NudgeToInteger(&_12); 1431 NudgeToInteger(&_13); 1432 NudgeToInteger(&_14); 1433 NudgeToInteger(&_21); 1434 NudgeToInteger(&_22); 1435 NudgeToInteger(&_23); 1436 NudgeToInteger(&_24); 1437 NudgeToInteger(&_31); 1438 NudgeToInteger(&_32); 1439 NudgeToInteger(&_33); 1440 NudgeToInteger(&_34); 1441 static const float error = 1e-5f; 1442 NudgeToInteger(&_41, error); 1443 NudgeToInteger(&_42, error); 1444 NudgeToInteger(&_43, error); 1445 NudgeToInteger(&_44, error); 1446 return *this; 1447 } 1448 1449 Point4D TransposedVector(int aIndex) const { 1450 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); 1451 return Point4DTyped<UnknownUnits, T>(*((&_11) + aIndex), *((&_21) + aIndex), 1452 *((&_31) + aIndex), 1453 *((&_41) + aIndex)); 1454 } 1455 1456 void SetTransposedVector(int aIndex, Point4DTyped<UnknownUnits, T>& aVector) { 1457 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index"); 1458 *((&_11) + aIndex) = aVector.x; 1459 *((&_21) + aIndex) = aVector.y; 1460 *((&_31) + aIndex) = aVector.z; 1461 *((&_41) + aIndex) = aVector.w; 1462 } 1463 1464 bool Decompose(Point3DTyped<UnknownUnits, T>& translation, 1465 BaseQuaternion<T>& rotation, 1466 Point3DTyped<UnknownUnits, T>& scale) const { 1467 // Ensure matrix can be normalized 1468 if (gfx::FuzzyEqual(_44, 0.0f)) { 1469 return false; 1470 } 1471 Matrix4x4Typed mat = *this; 1472 mat.Normalize(); 1473 if (HasPerspectiveComponent()) { 1474 // We do not support projection matrices 1475 return false; 1476 } 1477 1478 // Extract translation 1479 translation.x = mat._41; 1480 translation.y = mat._42; 1481 translation.z = mat._43; 1482 1483 // Remove translation 1484 mat._41 = 0.0f; 1485 mat._42 = 0.0f; 1486 mat._43 = 0.0f; 1487 1488 // Extract scale 1489 scale.x = sqrtf(_11 * _11 + _21 * _21 + _31 * _31); 1490 scale.y = sqrtf(_12 * _12 + _22 * _22 + _32 * _32); 1491 scale.z = sqrtf(_13 * _13 + _23 * _23 + _33 * _33); 1492 1493 // Remove scale 1494 if (gfx::FuzzyEqual(scale.x, 0.0f) || gfx::FuzzyEqual(scale.y, 0.0f) || 1495 gfx::FuzzyEqual(scale.z, 0.0f)) { 1496 // We do not support matrices with a zero scale component 1497 return false; 1498 } 1499 1500 // Extract rotation 1501 rotation.SetFromRotationMatrix(this->ToUnknownMatrix()); 1502 return true; 1503 } 1504 1505 // Sets this matrix to a rotation matrix given by aQuat. 1506 // This quaternion *MUST* be normalized! 1507 // Implemented in Quaternion.cpp 1508 void SetRotationFromQuaternion(const BaseQuaternion<T>& q) { 1509 const T x2 = q.x + q.x, y2 = q.y + q.y, z2 = q.z + q.z; 1510 const T xx = q.x * x2, xy = q.x * y2, xz = q.x * z2; 1511 const T yy = q.y * y2, yz = q.y * z2, zz = q.z * z2; 1512 const T wx = q.w * x2, wy = q.w * y2, wz = q.w * z2; 1513 1514 _11 = 1.0f - (yy + zz); 1515 _21 = xy - wz; 1516 _31 = xz + wy; 1517 _41 = 0.0f; 1518 1519 _12 = xy + wz; 1520 _22 = 1.0f - (xx + zz); 1521 _32 = yz - wx; 1522 _42 = 0.0f; 1523 1524 _13 = xz - wy; 1525 _23 = yz + wx; 1526 _33 = 1.0f - (xx + yy); 1527 _43 = 0.0f; 1528 1529 _14 = _42 = _43 = 0.0f; 1530 _44 = 1.0f; 1531 } 1532 1533 // Set all the members of the matrix to NaN 1534 void SetNAN() { 1535 _11 = UnspecifiedNaN<T>(); 1536 _21 = UnspecifiedNaN<T>(); 1537 _31 = UnspecifiedNaN<T>(); 1538 _41 = UnspecifiedNaN<T>(); 1539 _12 = UnspecifiedNaN<T>(); 1540 _22 = UnspecifiedNaN<T>(); 1541 _32 = UnspecifiedNaN<T>(); 1542 _42 = UnspecifiedNaN<T>(); 1543 _13 = UnspecifiedNaN<T>(); 1544 _23 = UnspecifiedNaN<T>(); 1545 _33 = UnspecifiedNaN<T>(); 1546 _43 = UnspecifiedNaN<T>(); 1547 _14 = UnspecifiedNaN<T>(); 1548 _24 = UnspecifiedNaN<T>(); 1549 _34 = UnspecifiedNaN<T>(); 1550 _44 = UnspecifiedNaN<T>(); 1551 } 1552 1553 // Verifies that the matrix contains no Infs or NaNs 1554 bool IsFinite() const { 1555 return std::isfinite(_11) && std::isfinite(_12) && std::isfinite(_13) && 1556 std::isfinite(_14) && std::isfinite(_21) && std::isfinite(_22) && 1557 std::isfinite(_23) && std::isfinite(_24) && std::isfinite(_31) && 1558 std::isfinite(_32) && std::isfinite(_33) && std::isfinite(_34) && 1559 std::isfinite(_41) && std::isfinite(_42) && std::isfinite(_43) && 1560 std::isfinite(_44); 1561 } 1562 1563 void SkewXY(double aXSkew, double aYSkew) { 1564 // XXX Is double precision really necessary here 1565 T tanX = SafeTangent(aXSkew); 1566 T tanY = SafeTangent(aYSkew); 1567 T temp; 1568 1569 temp = _11; 1570 _11 += tanY * _21; 1571 _21 += tanX * temp; 1572 1573 temp = _12; 1574 _12 += tanY * _22; 1575 _22 += tanX * temp; 1576 1577 temp = _13; 1578 _13 += tanY * _23; 1579 _23 += tanX * temp; 1580 1581 temp = _14; 1582 _14 += tanY * _24; 1583 _24 += tanX * temp; 1584 } 1585 1586 void RotateX(double aTheta) { 1587 // XXX Is double precision really necessary here 1588 double cosTheta = FlushToZero(cos(aTheta)); 1589 double sinTheta = FlushToZero(sin(aTheta)); 1590 1591 T temp; 1592 1593 temp = _21; 1594 _21 = cosTheta * _21 + sinTheta * _31; 1595 _31 = -sinTheta * temp + cosTheta * _31; 1596 1597 temp = _22; 1598 _22 = cosTheta * _22 + sinTheta * _32; 1599 _32 = -sinTheta * temp + cosTheta * _32; 1600 1601 temp = _23; 1602 _23 = cosTheta * _23 + sinTheta * _33; 1603 _33 = -sinTheta * temp + cosTheta * _33; 1604 1605 temp = _24; 1606 _24 = cosTheta * _24 + sinTheta * _34; 1607 _34 = -sinTheta * temp + cosTheta * _34; 1608 } 1609 1610 void RotateY(double aTheta) { 1611 // XXX Is double precision really necessary here 1612 double cosTheta = FlushToZero(cos(aTheta)); 1613 double sinTheta = FlushToZero(sin(aTheta)); 1614 1615 T temp; 1616 1617 temp = _11; 1618 _11 = cosTheta * _11 + -sinTheta * _31; 1619 _31 = sinTheta * temp + cosTheta * _31; 1620 1621 temp = _12; 1622 _12 = cosTheta * _12 + -sinTheta * _32; 1623 _32 = sinTheta * temp + cosTheta * _32; 1624 1625 temp = _13; 1626 _13 = cosTheta * _13 + -sinTheta * _33; 1627 _33 = sinTheta * temp + cosTheta * _33; 1628 1629 temp = _14; 1630 _14 = cosTheta * _14 + -sinTheta * _34; 1631 _34 = sinTheta * temp + cosTheta * _34; 1632 } 1633 1634 void RotateZ(double aTheta) { 1635 // XXX Is double precision really necessary here 1636 double cosTheta = FlushToZero(cos(aTheta)); 1637 double sinTheta = FlushToZero(sin(aTheta)); 1638 1639 T temp; 1640 1641 temp = _11; 1642 _11 = cosTheta * _11 + sinTheta * _21; 1643 _21 = -sinTheta * temp + cosTheta * _21; 1644 1645 temp = _12; 1646 _12 = cosTheta * _12 + sinTheta * _22; 1647 _22 = -sinTheta * temp + cosTheta * _22; 1648 1649 temp = _13; 1650 _13 = cosTheta * _13 + sinTheta * _23; 1651 _23 = -sinTheta * temp + cosTheta * _23; 1652 1653 temp = _14; 1654 _14 = cosTheta * _14 + sinTheta * _24; 1655 _24 = -sinTheta * temp + cosTheta * _24; 1656 } 1657 1658 // Sets this matrix to a rotation matrix about a 1659 // vector [x,y,z] by angle theta. The vector is normalized 1660 // to a unit vector. 1661 // https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined 1662 void SetRotateAxisAngle(double aX, double aY, double aZ, double aTheta) { 1663 Point3DTyped<UnknownUnits, T> vector(aX, aY, aZ); 1664 if (!vector.Length()) { 1665 return; 1666 } 1667 vector.RobustNormalize(); 1668 1669 double x = vector.x; 1670 double y = vector.y; 1671 double z = vector.z; 1672 1673 double cosTheta = FlushToZero(cos(aTheta)); 1674 double sinTheta = FlushToZero(sin(aTheta)); 1675 1676 // sin(aTheta / 2) * cos(aTheta / 2) 1677 double sc = sinTheta / 2; 1678 // pow(sin(aTheta / 2), 2) 1679 double sq = (1 - cosTheta) / 2; 1680 1681 _11 = 1 - 2 * (y * y + z * z) * sq; 1682 _12 = 2 * (x * y * sq + z * sc); 1683 _13 = 2 * (x * z * sq - y * sc); 1684 _14 = 0.0f; 1685 _21 = 2 * (x * y * sq - z * sc); 1686 _22 = 1 - 2 * (x * x + z * z) * sq; 1687 _23 = 2 * (y * z * sq + x * sc); 1688 _24 = 0.0f; 1689 _31 = 2 * (x * z * sq + y * sc); 1690 _32 = 2 * (y * z * sq - x * sc); 1691 _33 = 1 - 2 * (x * x + y * y) * sq; 1692 _34 = 0.0f; 1693 _41 = 0.0f; 1694 _42 = 0.0f; 1695 _43 = 0.0f; 1696 _44 = 1.0f; 1697 } 1698 1699 void Perspective(T aDepth) { 1700 MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!"); 1701 _31 += -1.0 / aDepth * _41; 1702 _32 += -1.0 / aDepth * _42; 1703 _33 += -1.0 / aDepth * _43; 1704 _34 += -1.0 / aDepth * _44; 1705 } 1706 1707 Point3D GetNormalVector() const { 1708 // Define a plane in transformed space as the transformations 1709 // of 3 points on the z=0 screen plane. 1710 Point3DTyped<UnknownUnits, T> a = 1711 TransformPoint(Point3DTyped<UnknownUnits, T>(0, 0, 0)); 1712 Point3DTyped<UnknownUnits, T> b = 1713 TransformPoint(Point3DTyped<UnknownUnits, T>(0, 1, 0)); 1714 Point3DTyped<UnknownUnits, T> c = 1715 TransformPoint(Point3DTyped<UnknownUnits, T>(1, 0, 0)); 1716 1717 // Convert to two vectors on the surface of the plane. 1718 Point3DTyped<UnknownUnits, T> ab = b - a; 1719 Point3DTyped<UnknownUnits, T> ac = c - a; 1720 1721 return ac.CrossProduct(ab); 1722 } 1723 1724 /** 1725 * Returns true if the matrix has any transform other 1726 * than a straight translation. 1727 */ 1728 bool HasNonTranslation() const { 1729 return !gfx::FuzzyEqual(_11, 1.0) || !gfx::FuzzyEqual(_22, 1.0) || 1730 !gfx::FuzzyEqual(_12, 0.0) || !gfx::FuzzyEqual(_21, 0.0) || 1731 !gfx::FuzzyEqual(_13, 0.0) || !gfx::FuzzyEqual(_23, 0.0) || 1732 !gfx::FuzzyEqual(_31, 0.0) || !gfx::FuzzyEqual(_32, 0.0) || 1733 !gfx::FuzzyEqual(_33, 1.0); 1734 } 1735 1736 /** 1737 * Returns true if the matrix is anything other than a straight 1738 * translation by integers. 1739 */ 1740 bool HasNonIntegerTranslation() const { 1741 return HasNonTranslation() || !gfx::FuzzyEqual(_41, floor(_41 + 0.5)) || 1742 !gfx::FuzzyEqual(_42, floor(_42 + 0.5)) || 1743 !gfx::FuzzyEqual(_43, floor(_43 + 0.5)); 1744 } 1745 1746 /** 1747 * Return true if the matrix is with perspective (w). 1748 */ 1749 bool HasPerspectiveComponent() const { 1750 return _14 != 0 || _24 != 0 || _34 != 0 || _44 != 1; 1751 } 1752 1753 /* Returns true if the matrix is a rectilinear transformation (i.e. 1754 * grid-aligned rectangles are transformed to grid-aligned rectangles). 1755 * This should only be called on 2D matrices. 1756 */ 1757 bool IsRectilinear() const { 1758 MOZ_ASSERT(Is2D()); 1759 if (gfx::FuzzyEqual(_12, 0) && gfx::FuzzyEqual(_21, 0)) { 1760 return true; 1761 } else if (gfx::FuzzyEqual(_22, 0) && gfx::FuzzyEqual(_11, 0)) { 1762 return true; 1763 } 1764 return false; 1765 } 1766 1767 /** 1768 * Convert between typed and untyped matrices. 1769 */ 1770 using UnknownMatrix = Matrix4x4Typed<UnknownUnits, UnknownUnits, T>; 1771 UnknownMatrix ToUnknownMatrix() const { 1772 return UnknownMatrix{_11, _12, _13, _14, _21, _22, _23, _24, 1773 _31, _32, _33, _34, _41, _42, _43, _44}; 1774 } 1775 static Matrix4x4Typed FromUnknownMatrix(const UnknownMatrix& aUnknown) { 1776 return Matrix4x4Typed{ 1777 aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14, 1778 aUnknown._21, aUnknown._22, aUnknown._23, aUnknown._24, 1779 aUnknown._31, aUnknown._32, aUnknown._33, aUnknown._34, 1780 aUnknown._41, aUnknown._42, aUnknown._43, aUnknown._44}; 1781 } 1782 /** 1783 * For convenience, overload FromUnknownMatrix() for Maybe<Matrix>. 1784 */ 1785 static Maybe<Matrix4x4Typed> FromUnknownMatrix( 1786 const Maybe<UnknownMatrix>& aUnknown) { 1787 if (aUnknown.isSome()) { 1788 return Some(FromUnknownMatrix(*aUnknown)); 1789 } 1790 return Nothing(); 1791 } 1792 }; 1793 1794 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4; 1795 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits, double> Matrix4x4Double; 1796 1797 class Matrix5x4 { 1798 public: 1799 Matrix5x4() 1800 : _11(1.0f), 1801 _12(0), 1802 _13(0), 1803 _14(0), 1804 _21(0), 1805 _22(1.0f), 1806 _23(0), 1807 _24(0), 1808 _31(0), 1809 _32(0), 1810 _33(1.0f), 1811 _34(0), 1812 _41(0), 1813 _42(0), 1814 _43(0), 1815 _44(1.0f), 1816 _51(0), 1817 _52(0), 1818 _53(0), 1819 _54(0) {} 1820 Matrix5x4(Float a11, Float a12, Float a13, Float a14, Float a21, Float a22, 1821 Float a23, Float a24, Float a31, Float a32, Float a33, Float a34, 1822 Float a41, Float a42, Float a43, Float a44, Float a51, Float a52, 1823 Float a53, Float a54) 1824 : _11(a11), 1825 _12(a12), 1826 _13(a13), 1827 _14(a14), 1828 _21(a21), 1829 _22(a22), 1830 _23(a23), 1831 _24(a24), 1832 _31(a31), 1833 _32(a32), 1834 _33(a33), 1835 _34(a34), 1836 _41(a41), 1837 _42(a42), 1838 _43(a43), 1839 _44(a44), 1840 _51(a51), 1841 _52(a52), 1842 _53(a53), 1843 _54(a54) {} 1844 1845 bool operator==(const Matrix5x4& o) const { 1846 return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 && 1847 _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 && 1848 _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 && 1849 _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44 && 1850 _51 == o._51 && _52 == o._52 && _53 == o._53 && _54 == o._54; 1851 } 1852 1853 bool operator!=(const Matrix5x4& aMatrix) const { 1854 return !(*this == aMatrix); 1855 } 1856 1857 Matrix5x4 operator*(const Matrix5x4& aMatrix) const { 1858 Matrix5x4 resultMatrix; 1859 1860 resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21 + 1861 this->_13 * aMatrix._31 + this->_14 * aMatrix._41; 1862 resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22 + 1863 this->_13 * aMatrix._32 + this->_14 * aMatrix._42; 1864 resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23 + 1865 this->_13 * aMatrix._33 + this->_14 * aMatrix._43; 1866 resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24 + 1867 this->_13 * aMatrix._34 + this->_14 * aMatrix._44; 1868 resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21 + 1869 this->_23 * aMatrix._31 + this->_24 * aMatrix._41; 1870 resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22 + 1871 this->_23 * aMatrix._32 + this->_24 * aMatrix._42; 1872 resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23 + 1873 this->_23 * aMatrix._33 + this->_24 * aMatrix._43; 1874 resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24 + 1875 this->_23 * aMatrix._34 + this->_24 * aMatrix._44; 1876 resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + 1877 this->_33 * aMatrix._31 + this->_34 * aMatrix._41; 1878 resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + 1879 this->_33 * aMatrix._32 + this->_34 * aMatrix._42; 1880 resultMatrix._33 = this->_31 * aMatrix._13 + this->_32 * aMatrix._23 + 1881 this->_33 * aMatrix._33 + this->_34 * aMatrix._43; 1882 resultMatrix._34 = this->_31 * aMatrix._14 + this->_32 * aMatrix._24 + 1883 this->_33 * aMatrix._34 + this->_34 * aMatrix._44; 1884 resultMatrix._41 = this->_41 * aMatrix._11 + this->_42 * aMatrix._21 + 1885 this->_43 * aMatrix._31 + this->_44 * aMatrix._41; 1886 resultMatrix._42 = this->_41 * aMatrix._12 + this->_42 * aMatrix._22 + 1887 this->_43 * aMatrix._32 + this->_44 * aMatrix._42; 1888 resultMatrix._43 = this->_41 * aMatrix._13 + this->_42 * aMatrix._23 + 1889 this->_43 * aMatrix._33 + this->_44 * aMatrix._43; 1890 resultMatrix._44 = this->_41 * aMatrix._14 + this->_42 * aMatrix._24 + 1891 this->_43 * aMatrix._34 + this->_44 * aMatrix._44; 1892 resultMatrix._51 = this->_51 * aMatrix._11 + this->_52 * aMatrix._21 + 1893 this->_53 * aMatrix._31 + this->_54 * aMatrix._41 + 1894 aMatrix._51; 1895 resultMatrix._52 = this->_51 * aMatrix._12 + this->_52 * aMatrix._22 + 1896 this->_53 * aMatrix._32 + this->_54 * aMatrix._42 + 1897 aMatrix._52; 1898 resultMatrix._53 = this->_51 * aMatrix._13 + this->_52 * aMatrix._23 + 1899 this->_53 * aMatrix._33 + this->_54 * aMatrix._43 + 1900 aMatrix._53; 1901 resultMatrix._54 = this->_51 * aMatrix._14 + this->_52 * aMatrix._24 + 1902 this->_53 * aMatrix._34 + this->_54 * aMatrix._44 + 1903 aMatrix._54; 1904 1905 return resultMatrix; 1906 } 1907 1908 Matrix5x4& operator*=(const Matrix5x4& aMatrix) { 1909 *this = *this * aMatrix; 1910 return *this; 1911 } 1912 1913 friend std::ostream& operator<<(std::ostream& aStream, 1914 const Matrix5x4& aMatrix) { 1915 const Float* f = &aMatrix._11; 1916 aStream << "[ " << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';'; 1917 f += 4; 1918 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';'; 1919 f += 4; 1920 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';'; 1921 f += 4; 1922 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';'; 1923 f += 4; 1924 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] 1925 << "; ]"; 1926 return aStream; 1927 } 1928 1929 union { 1930 struct { 1931 Float _11, _12, _13, _14; 1932 Float _21, _22, _23, _24; 1933 Float _31, _32, _33, _34; 1934 Float _41, _42, _43, _44; 1935 Float _51, _52, _53, _54; 1936 }; 1937 Float components[20]; 1938 }; 1939 }; 1940 1941 /* This Matrix class will carry one additional type field in order to 1942 * track what type of 4x4 matrix we're dealing with, it can then execute 1943 * simplified versions of certain operations when applicable. 1944 * This does not allow access to the parent class directly, as a caller 1945 * could then mutate the parent class without updating the type. 1946 */ 1947 1948 enum class MatrixType : uint8_t { 1949 Identity, 1950 Simple, // 2x3 Matrix 1951 Full // 4x4 Matrix 1952 }; 1953 1954 template <typename SourceUnits, typename TargetUnits> 1955 class Matrix4x4TypedFlagged 1956 : protected Matrix4x4Typed<SourceUnits, TargetUnits> { 1957 public: 1958 using Parent = Matrix4x4Typed<SourceUnits, TargetUnits>; 1959 using Parent::_11; 1960 using Parent::_12; 1961 using Parent::_13; 1962 using Parent::_14; 1963 using Parent::_21; 1964 using Parent::_22; 1965 using Parent::_23; 1966 using Parent::_24; 1967 using Parent::_31; 1968 using Parent::_32; 1969 using Parent::_33; 1970 using Parent::_34; 1971 using Parent::_41; 1972 using Parent::_42; 1973 using Parent::_43; 1974 using Parent::_44; 1975 1976 Matrix4x4TypedFlagged() : mType(MatrixType::Identity) {} 1977 1978 Matrix4x4TypedFlagged(Float a11, Float a12, Float a13, Float a14, Float a21, 1979 Float a22, Float a23, Float a24, Float a31, Float a32, 1980 Float a33, Float a34, Float a41, Float a42, Float a43, 1981 Float a44) 1982 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, 1983 a42, a43, a44) { 1984 Analyze(); 1985 } 1986 1987 MOZ_IMPLICIT Matrix4x4TypedFlagged(const Parent& aOther) : Parent(aOther) { 1988 Analyze(); 1989 } 1990 1991 template <typename NewMatrix4x4TypedFlagged> 1992 [[nodiscard]] NewMatrix4x4TypedFlagged Cast() const { 1993 return NewMatrix4x4TypedFlagged(_11, _12, _13, _14, _21, _22, _23, _24, _31, 1994 _32, _33, _34, _41, _42, _43, _44, mType); 1995 } 1996 1997 static Matrix4x4TypedFlagged Translation2d(Float aX, Float aY) { 1998 MatrixType matrixType = MatrixType::Simple; 1999 if (aX == 0.0 && aY == 0.0) { 2000 matrixType = MatrixType::Identity; 2001 } 2002 return Matrix4x4TypedFlagged(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 2003 0.0f, 0.0f, 1.0f, 0.0f, aX, aY, 0.0f, 1.0f, 2004 matrixType); 2005 } 2006 2007 static Matrix4x4TypedFlagged Scaling(Float aScaleX, Float aScaleY, 2008 Float aScaleZ) { 2009 MatrixType matrixType = MatrixType::Full; 2010 if (aScaleZ == 1.0) { 2011 if (aScaleX == 1.0 && aScaleY == 1.0) { 2012 matrixType = MatrixType::Identity; 2013 } else { 2014 matrixType = MatrixType::Simple; 2015 } 2016 } 2017 return Matrix4x4TypedFlagged(aScaleX, 0.0f, 0.0f, 0.0f, 0.0f, aScaleY, 0.0f, 2018 0.0f, 0.0f, 0.0f, aScaleZ, 0.0f, 0.0f, 0.0f, 2019 0.0f, 1.0f, matrixType); 2020 } 2021 2022 template <class F> 2023 PointTyped<TargetUnits, F> TransformPoint( 2024 const PointTyped<SourceUnits, F>& aPoint) const { 2025 if (mType == MatrixType::Identity) { 2026 return aPoint; 2027 } 2028 2029 if (mType == MatrixType::Simple) { 2030 return TransformPointSimple(aPoint); 2031 } 2032 2033 return Parent::TransformPoint(aPoint); 2034 } 2035 2036 template <class F> 2037 RectTyped<TargetUnits, F> TransformBounds( 2038 const RectTyped<SourceUnits, F>& aRect) const { 2039 if (mType == MatrixType::Identity) { 2040 return aRect; 2041 } 2042 2043 if (mType == MatrixType::Simple) { 2044 PointTyped<TargetUnits, F> quad[4]; 2045 F min_x, max_x; 2046 F min_y, max_y; 2047 2048 quad[0] = TransformPointSimple(aRect.TopLeft()); 2049 quad[1] = TransformPointSimple(aRect.TopRight()); 2050 quad[2] = TransformPointSimple(aRect.BottomLeft()); 2051 quad[3] = TransformPointSimple(aRect.BottomRight()); 2052 2053 min_x = max_x = quad[0].x; 2054 min_y = max_y = quad[0].y; 2055 2056 for (int i = 1; i < 4; i++) { 2057 if (quad[i].x < min_x) { 2058 min_x = quad[i].x; 2059 } 2060 if (quad[i].x > max_x) { 2061 max_x = quad[i].x; 2062 } 2063 2064 if (quad[i].y < min_y) { 2065 min_y = quad[i].y; 2066 } 2067 if (quad[i].y > max_y) { 2068 max_y = quad[i].y; 2069 } 2070 } 2071 2072 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x, 2073 max_y - min_y); 2074 } 2075 2076 return Parent::TransformBounds(aRect); 2077 } 2078 2079 template <class F> 2080 RectTyped<TargetUnits, F> TransformAndClipBounds( 2081 const RectTyped<SourceUnits, F>& aRect, 2082 const RectTyped<TargetUnits, F>& aClip) const { 2083 if (mType == MatrixType::Identity) { 2084 const RectTyped<SourceUnits, F>& clipped = aRect.Intersect(aClip); 2085 return RectTyped<TargetUnits, F>(clipped.X(), clipped.Y(), 2086 clipped.Width(), clipped.Height()); 2087 } 2088 2089 if (mType == MatrixType::Simple) { 2090 PointTyped<UnknownUnits, F> p1 = TransformPointSimple(aRect.TopLeft()); 2091 PointTyped<UnknownUnits, F> p2 = TransformPointSimple(aRect.TopRight()); 2092 PointTyped<UnknownUnits, F> p3 = TransformPointSimple(aRect.BottomLeft()); 2093 PointTyped<UnknownUnits, F> p4 = 2094 TransformPointSimple(aRect.BottomRight()); 2095 2096 F min_x = std::min(std::min(std::min(p1.x, p2.x), p3.x), p4.x); 2097 F max_x = std::max(std::max(std::max(p1.x, p2.x), p3.x), p4.x); 2098 F min_y = std::min(std::min(std::min(p1.y, p2.y), p3.y), p4.y); 2099 F max_y = std::max(std::max(std::max(p1.y, p2.y), p3.y), p4.y); 2100 2101 PointTyped<TargetUnits, F> topLeft( 2102 std::min(std::max(min_x, aClip.x), aClip.XMost()), 2103 std::min(std::max(min_y, aClip.y), aClip.YMost())); 2104 F width = std::min(std::max(max_x, aClip.x), aClip.XMost()) - topLeft.x; 2105 F height = std::min(std::max(max_y, aClip.y), aClip.YMost()) - topLeft.y; 2106 2107 return RectTyped<TargetUnits, F>(topLeft.x, topLeft.y, width, height); 2108 } 2109 return Parent::TransformAndClipBounds(aRect, aClip); 2110 } 2111 2112 bool FuzzyEqual(const Parent& o) const { return Parent::FuzzyEqual(o); } 2113 2114 bool FuzzyEqual(const Matrix4x4TypedFlagged& o) const { 2115 if (mType == MatrixType::Identity && o.mType == MatrixType::Identity) { 2116 return true; 2117 } 2118 return Parent::FuzzyEqual(o); 2119 } 2120 2121 Matrix4x4TypedFlagged& PreTranslate(Float aX, Float aY, Float aZ) { 2122 if (mType == MatrixType::Identity) { 2123 _41 = aX; 2124 _42 = aY; 2125 _43 = aZ; 2126 2127 if (!aZ) { 2128 mType = MatrixType::Simple; 2129 return *this; 2130 } 2131 mType = MatrixType::Full; 2132 return *this; 2133 } 2134 2135 Parent::PreTranslate(aX, aY, aZ); 2136 2137 if (aZ != 0) { 2138 mType = MatrixType::Full; 2139 } 2140 2141 return *this; 2142 } 2143 2144 Matrix4x4TypedFlagged& PostTranslate(Float aX, Float aY, Float aZ) { 2145 if (mType == MatrixType::Identity) { 2146 _41 = aX; 2147 _42 = aY; 2148 _43 = aZ; 2149 2150 if (!aZ) { 2151 mType = MatrixType::Simple; 2152 return *this; 2153 } 2154 mType = MatrixType::Full; 2155 return *this; 2156 } 2157 2158 Parent::PostTranslate(aX, aY, aZ); 2159 2160 if (aZ != 0) { 2161 mType = MatrixType::Full; 2162 } 2163 2164 return *this; 2165 } 2166 2167 Matrix4x4TypedFlagged& ChangeBasis(Float aX, Float aY, Float aZ) { 2168 // Translate to the origin before applying this matrix 2169 PreTranslate(-aX, -aY, -aZ); 2170 2171 // Translate back into position after applying this matrix 2172 PostTranslate(aX, aY, aZ); 2173 2174 return *this; 2175 } 2176 2177 bool IsIdentity() const { return mType == MatrixType::Identity; } 2178 2179 template <class F> 2180 Point4DTyped<TargetUnits, F> ProjectPoint( 2181 const PointTyped<SourceUnits, F>& aPoint) const { 2182 if (mType == MatrixType::Identity) { 2183 return Point4DTyped<TargetUnits, F>(aPoint.x, aPoint.y, 0, 1); 2184 } 2185 2186 if (mType == MatrixType::Simple) { 2187 PointTyped<TargetUnits, F> point = TransformPointSimple(aPoint); 2188 return Point4DTyped<TargetUnits, F>(point.x, point.y, 0, 1); 2189 } 2190 2191 return Parent::ProjectPoint(aPoint); 2192 } 2193 2194 Matrix4x4TypedFlagged& ProjectTo2D() { 2195 if (mType == MatrixType::Full) { 2196 Parent::ProjectTo2D(); 2197 } 2198 return *this; 2199 } 2200 2201 bool IsSingular() const { 2202 if (mType == MatrixType::Identity) { 2203 return false; 2204 } 2205 return Parent::Determinant() == 0.0; 2206 } 2207 2208 bool Invert() { 2209 if (mType == MatrixType::Identity) { 2210 return true; 2211 } 2212 2213 return Parent::Invert(); 2214 } 2215 2216 Matrix4x4TypedFlagged<TargetUnits, SourceUnits> Inverse() const { 2217 typedef Matrix4x4TypedFlagged<TargetUnits, SourceUnits> InvertedMatrix; 2218 InvertedMatrix clone = Cast<InvertedMatrix>(); 2219 if (mType == MatrixType::Identity) { 2220 return clone; 2221 } 2222 DebugOnly<bool> inverted = clone.Invert(); 2223 MOZ_ASSERT(inverted, 2224 "Attempted to get the inverse of a non-invertible matrix"); 2225 2226 // Inverting a 2D Matrix should result in a 2D matrix, ergo mType doesn't 2227 // change. 2228 return clone; 2229 } 2230 2231 Maybe<Matrix4x4TypedFlagged<TargetUnits, SourceUnits>> MaybeInverse() const { 2232 typedef Matrix4x4TypedFlagged<TargetUnits, SourceUnits> InvertedMatrix; 2233 InvertedMatrix clone = Cast<InvertedMatrix>(); 2234 if (clone.Invert()) { 2235 return Some(clone); 2236 } 2237 return Nothing(); 2238 } 2239 2240 template <typename NewTargetUnits> 2241 bool operator==( 2242 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const { 2243 if (mType == MatrixType::Identity && 2244 aMatrix.mType == MatrixType::Identity) { 2245 return true; 2246 } 2247 // Depending on the usage it may make sense to compare more flags. 2248 return Parent::operator==(aMatrix); 2249 } 2250 2251 template <typename NewTargetUnits> 2252 bool operator!=( 2253 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const { 2254 if (mType == MatrixType::Identity && 2255 aMatrix.mType == MatrixType::Identity) { 2256 return false; 2257 } 2258 // Depending on the usage it may make sense to compare more flags. 2259 return Parent::operator!=(aMatrix); 2260 } 2261 2262 template <typename NewTargetUnits> 2263 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> operator*( 2264 const Matrix4x4Typed<TargetUnits, NewTargetUnits>& aMatrix) const { 2265 if (mType == MatrixType::Identity) { 2266 return aMatrix; 2267 } 2268 2269 if (mType == MatrixType::Simple) { 2270 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix; 2271 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21; 2272 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21; 2273 matrix._31 = aMatrix._31; 2274 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41; 2275 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22; 2276 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22; 2277 matrix._32 = aMatrix._32; 2278 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42; 2279 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23; 2280 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23; 2281 matrix._33 = aMatrix._33; 2282 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + aMatrix._43; 2283 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24; 2284 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24; 2285 matrix._34 = aMatrix._34; 2286 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + aMatrix._44; 2287 matrix.Analyze(); 2288 return matrix; 2289 } 2290 2291 return Parent::operator*(aMatrix); 2292 } 2293 2294 template <typename NewTargetUnits> 2295 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> operator*( 2296 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const { 2297 if (mType == MatrixType::Identity) { 2298 return aMatrix; 2299 } 2300 2301 if (aMatrix.mType == MatrixType::Identity) { 2302 return Cast<Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits>>(); 2303 } 2304 2305 if (mType == MatrixType::Simple && aMatrix.mType == MatrixType::Simple) { 2306 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix; 2307 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21; 2308 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21; 2309 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41; 2310 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22; 2311 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22; 2312 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42; 2313 matrix.mType = MatrixType::Simple; 2314 return matrix; 2315 } else if (mType == MatrixType::Simple) { 2316 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix; 2317 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21; 2318 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21; 2319 matrix._31 = aMatrix._31; 2320 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41; 2321 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22; 2322 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22; 2323 matrix._32 = aMatrix._32; 2324 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42; 2325 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23; 2326 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23; 2327 matrix._33 = aMatrix._33; 2328 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + aMatrix._43; 2329 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24; 2330 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24; 2331 matrix._34 = aMatrix._34; 2332 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + aMatrix._44; 2333 matrix.mType = MatrixType::Full; 2334 return matrix; 2335 } else if (aMatrix.mType == MatrixType::Simple) { 2336 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix; 2337 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _14 * aMatrix._41; 2338 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _24 * aMatrix._41; 2339 matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _34 * aMatrix._41; 2340 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _44 * aMatrix._41; 2341 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _14 * aMatrix._42; 2342 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _24 * aMatrix._42; 2343 matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _34 * aMatrix._42; 2344 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _44 * aMatrix._42; 2345 matrix._13 = _13; 2346 matrix._23 = _23; 2347 matrix._33 = _33; 2348 matrix._43 = _43; 2349 matrix._14 = _14; 2350 matrix._24 = _24; 2351 matrix._34 = _34; 2352 matrix._44 = _44; 2353 matrix.mType = MatrixType::Full; 2354 return matrix; 2355 } 2356 2357 return Parent::operator*(aMatrix); 2358 } 2359 2360 bool Is2D() const { return mType != MatrixType::Full; } 2361 2362 bool CanDraw2D(Matrix* aMatrix = nullptr) const { 2363 if (mType != MatrixType::Full) { 2364 if (aMatrix) { 2365 aMatrix->_11 = _11; 2366 aMatrix->_12 = _12; 2367 aMatrix->_21 = _21; 2368 aMatrix->_22 = _22; 2369 aMatrix->_31 = _41; 2370 aMatrix->_32 = _42; 2371 } 2372 return true; 2373 } 2374 return Parent::CanDraw2D(aMatrix); 2375 } 2376 2377 bool Is2D(Matrix* aMatrix) const { 2378 if (!Is2D()) { 2379 return false; 2380 } 2381 if (aMatrix) { 2382 aMatrix->_11 = _11; 2383 aMatrix->_12 = _12; 2384 aMatrix->_21 = _21; 2385 aMatrix->_22 = _22; 2386 aMatrix->_31 = _41; 2387 aMatrix->_32 = _42; 2388 } 2389 return true; 2390 } 2391 2392 template <class F> 2393 RectTyped<TargetUnits, F> ProjectRectBounds( 2394 const RectTyped<SourceUnits, F>& aRect, 2395 const RectTyped<TargetUnits, F>& aClip) const { 2396 return Parent::ProjectRectBounds(aRect, aClip); 2397 } 2398 2399 const Parent& GetMatrix() const { return *this; } 2400 2401 Matrix4x4Flagged ToUnknownMatrix() const { 2402 return Matrix4x4Flagged{_11, _12, _13, _14, _21, _22, _23, _24, _31, 2403 _32, _33, _34, _41, _42, _43, _44, mType}; 2404 } 2405 2406 static Matrix4x4TypedFlagged FromUnknownMatrix( 2407 const Matrix4x4Flagged& aUnknown) { 2408 return Matrix4x4TypedFlagged{ 2409 aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14, aUnknown._21, 2410 aUnknown._22, aUnknown._23, aUnknown._24, aUnknown._31, aUnknown._32, 2411 aUnknown._33, aUnknown._34, aUnknown._41, aUnknown._42, aUnknown._43, 2412 aUnknown._44, aUnknown.mType}; 2413 } 2414 2415 private: 2416 Matrix4x4TypedFlagged(Float a11, Float a12, Float a13, Float a14, Float a21, 2417 Float a22, Float a23, Float a24, Float a31, Float a32, 2418 Float a33, Float a34, Float a41, Float a42, Float a43, 2419 Float a44, const MatrixType aType) 2420 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, 2421 a42, a43, a44), 2422 mType(aType) {} 2423 2424 template <class F> 2425 PointTyped<TargetUnits, F> TransformPointSimple( 2426 const PointTyped<SourceUnits, F>& aPoint) const { 2427 PointTyped<SourceUnits, F> temp; 2428 temp.x = aPoint.x * _11 + aPoint.y * _21 + _41; 2429 temp.y = aPoint.x * _12 + aPoint.y * _22 + _42; 2430 return temp; 2431 } 2432 2433 void Analyze() { 2434 if (Parent::IsIdentity()) { 2435 mType = MatrixType::Identity; 2436 return; 2437 } 2438 2439 if (Parent::Is2D()) { 2440 mType = MatrixType::Simple; 2441 return; 2442 } 2443 2444 mType = MatrixType::Full; 2445 } 2446 2447 MatrixType mType; 2448 2449 template <typename, typename> 2450 friend class Matrix4x4TypedFlagged; 2451 }; 2452 2453 using Matrix4x4Flagged = Matrix4x4TypedFlagged<UnknownUnits, UnknownUnits>; 2454 2455 } // namespace gfx 2456 } // namespace mozilla 2457 2458 #endif /* MOZILLA_GFX_MATRIX_H_ */