Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / gfx / 2d / Matrix.h
blob1b8566ee92c91f5e8aa49db6abf618df76642dbe
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/. */
7 #ifndef MOZILLA_GFX_MATRIX_H_
8 #define MOZILLA_GFX_MATRIX_H_
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"
23 namespace mozilla {
24 namespace gfx {
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;
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);
37 template <class T>
38 using BaseMatrixScales = BaseScaleFactors2D<UnknownUnits, UnknownUnits, T>;
40 using MatrixScales = BaseMatrixScales<float>;
41 using MatrixScalesDouble = BaseMatrixScales<double>;
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;
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;
62 T components[6];
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) {}
74 MOZ_ALWAYS_INLINE BaseMatrix Copy() const { return BaseMatrix<T>(*this); }
76 friend std::ostream& operator<<(std::ostream& aStream,
77 const BaseMatrix& aMatrix) {
78 if (aMatrix.IsIdentity()) {
79 return aStream << "[ I ]";
81 return aStream << "[ " << aMatrix._11 << " " << aMatrix._12 << "; "
82 << aMatrix._21 << " " << aMatrix._22 << "; " << aMatrix._31
83 << " " << aMatrix._32 << "; ]";
86 MatrixPoint TransformPoint(const MatrixPoint& aPoint) const {
87 MatrixPoint retPoint;
89 retPoint.x = aPoint.x * _11 + aPoint.y * _21 + _31;
90 retPoint.y = aPoint.x * _12 + aPoint.y * _22 + _32;
92 return retPoint;
95 MatrixSize TransformSize(const MatrixSize& aSize) const {
96 MatrixSize retSize;
98 retSize.width = aSize.width * _11 + aSize.height * _21;
99 retSize.height = aSize.width * _12 + aSize.height * _22;
101 return retSize;
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.
109 MatrixRect TransformRect(const MatrixRect& aRect) const {
110 return MatrixRect(TransformPoint(aRect.TopLeft()),
111 TransformSize(aRect.Size()));
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;
120 quad[0] = TransformPoint(aRect.TopLeft());
121 quad[1] = TransformPoint(aRect.TopRight());
122 quad[2] = TransformPoint(aRect.BottomLeft());
123 quad[3] = TransformPoint(aRect.BottomRight());
125 min_x = max_x = quad[0].x;
126 min_y = max_y = quad[0].y;
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;
132 if (quad[i].y < min_y) min_y = quad[i].y;
133 if (quad[i].y > max_y) max_y = quad[i].y;
136 return MatrixRect(min_x, min_y, max_x - min_x, max_y - min_y);
139 static BaseMatrix<T> Translation(T aX, T aY) {
140 return BaseMatrix<T>(1.0f, 0.0f, 0.0f, 1.0f, aX, aY);
143 static BaseMatrix<T> Translation(MatrixPoint aPoint) {
144 return Translation(aPoint.x, aPoint.y);
148 * Apply a translation to this matrix.
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.
155 * Calling this method will result in this matrix having the same value as
156 * the result of:
158 * BaseMatrix<T>::Translation(x, y) * this
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.)
166 BaseMatrix<T>& PreTranslate(T aX, T aY) {
167 _31 += _11 * aX + _21 * aY;
168 _32 += _12 * aX + _22 * aY;
170 return *this;
173 BaseMatrix<T>& PreTranslate(const MatrixPoint& aPoint) {
174 return PreTranslate(aPoint.x, aPoint.y);
178 * Similar to PreTranslate, but the translation is applied -after- this
179 * matrix's existing transformation instead of before it.
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.
189 BaseMatrix<T>& PostTranslate(T aX, T aY) {
190 _31 += aX;
191 _32 += aY;
192 return *this;
195 BaseMatrix<T>& PostTranslate(const MatrixPoint& aPoint) {
196 return PostTranslate(aPoint.x, aPoint.y);
199 static BaseMatrix<T> Scaling(T aScaleX, T aScaleY) {
200 return BaseMatrix<T>(aScaleX, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f);
203 static BaseMatrix<T> Scaling(const BaseMatrixScales<T>& scale) {
204 return Scaling(scale.xScale, scale.yScale);
208 * Similar to PreTranslate, but applies a scale instead of a translation.
210 BaseMatrix<T>& PreScale(T aX, T aY) {
211 _11 *= aX;
212 _12 *= aX;
213 _21 *= aY;
214 _22 *= aY;
216 return *this;
219 BaseMatrix<T>& PreScale(const BaseMatrixScales<T>& scale) {
220 return PreScale(scale.xScale, scale.yScale);
224 * Similar to PostTranslate, but applies a scale instead of a translation.
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;
234 return *this;
237 GFX2D_API static BaseMatrix<T> Rotation(T aAngle);
240 * Similar to PreTranslate, but applies a rotation instead of a translation.
242 BaseMatrix<T>& PreRotate(T aAngle) {
243 return *this = BaseMatrix<T>::Rotation(aAngle) * *this;
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;
255 T det = Determinant();
257 if (!det) {
258 return false;
261 T inv_det = 1 / det;
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;
270 return true;
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;
281 T Determinant() const { return _11 * _22 - _12 * _21; }
283 BaseMatrix<T> operator*(const BaseMatrix<T>& aMatrix) const {
284 BaseMatrix<T> resultMatrix;
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;
295 return resultMatrix;
298 BaseMatrix<T>& operator*=(const BaseMatrix<T>& aMatrix) {
299 *this = *this * aMatrix;
300 return *this;
304 * Multiplies *this with aMatrix and returns the result.
306 Matrix4x4 operator*(const Matrix4x4& aMatrix) const;
309 * Multiplies in the opposite order to operator=*.
311 BaseMatrix<T>& PreMultiply(const BaseMatrix<T>& aMatrix) {
312 *this = aMatrix * *this;
313 return *this;
317 * Please explicitly use either FuzzyEquals or ExactlyEquals.
319 bool operator==(const BaseMatrix<T>& other) const = delete;
320 bool operator!=(const BaseMatrix<T>& other) const = delete;
322 /* Returns true if the other matrix is fuzzy-equal to this matrix.
323 * Note that this isn't a cheap comparison!
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);
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;
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);
342 /* Returns true if the matrix is a rectilinear transformation (i.e.
343 * grid-aligned rectangles are transformed to grid-aligned rectangles)
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;
352 return false;
356 * Returns true if the matrix is anything other than a straight
357 * translation by integers.
359 bool HasNonIntegerTranslation() const {
360 return HasNonTranslation() || !FuzzyEqual(_31, floor(_31 + 0.5f)) ||
361 !FuzzyEqual(_32, floor(_32 + 0.5f));
365 * Returns true if the matrix only has an integer translation.
367 bool HasOnlyIntegerTranslation() const { return !HasNonIntegerTranslation(); }
370 * Returns true if the matrix has any transform other
371 * than a straight translation.
373 bool HasNonTranslation() const {
374 return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) ||
375 !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0);
379 * Returns true if the matrix has any transform other
380 * than a translation or a -1 y scale (y axis flip)
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);
388 /* Returns true if the matrix is an identity matrix.
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;
395 /* Returns true if the matrix is singular.
397 bool IsSingular() const {
398 T det = Determinant();
399 return !std::isfinite(det) || det == 0;
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;
412 bool IsTranslation() const {
413 return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) &&
414 FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f);
417 static bool FuzzyIsInteger(T aValue) {
418 return FuzzyEqual(aValue, floorf(aValue + 0.5f));
421 bool IsIntegerTranslation() const {
422 return IsTranslation() && FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
425 bool IsAllIntegers() const {
426 return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) && FuzzyIsInteger(_21) &&
427 FuzzyIsInteger(_22) && FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
430 MatrixPoint GetTranslation() const { return MatrixPoint(_31, _32); }
433 * Returns true if matrix is multiple of 90 degrees rotation with flipping,
434 * scaling and translation.
436 bool PreservesAxisAlignedRectangles() const {
437 return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0)) ||
438 (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0)));
442 * Returns true if the matrix has any transform other
443 * than a translation or scale; this is, if there is
444 * rotation.
446 bool HasNonAxisAlignedTransform() const {
447 return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
451 * Returns true if the matrix has negative scaling (i.e. flip).
453 bool HasNegativeScaling() const { return (_11 < 0.0) || (_22 < 0.0); }
456 * Computes the scale factors of this matrix; that is,
457 * the amounts each basis vector is scaled by.
459 BaseMatrixScales<T> ScaleFactors() const {
460 T det = Determinant();
462 if (det == 0.0) {
463 return BaseMatrixScales<T>(0.0, 0.0);
466 MatrixSize sz = MatrixSize(1.0, 0.0);
467 sz = TransformSize(sz);
469 T major = sqrt(sz.width * sz.width + sz.height * sz.height);
470 T minor = 0.0;
472 // ignore mirroring
473 if (det < 0.0) {
474 det = -det;
477 if (major) {
478 minor = det / major;
481 return BaseMatrixScales<T>(major, minor);
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.
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);
496 typedef BaseMatrix<Float> Matrix;
497 typedef BaseMatrix<Double> MatrixDouble;
499 // Helper functions used by Matrix4x4Typed defined in Matrix.cpp
500 double SafeTangent(double aTheta);
501 double FlushToZero(double aVal);
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.
511 // We want to interpolate aFirst and aSecond to find the point intersecting
512 // with the w=0 plane.
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);
518 // Use t to find the remainder of the components
519 return aFirst + (aSecond - aFirst) * t;
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;
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) {}
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) {}
571 explicit Matrix4x4Typed(const T aArray[16]) {
572 memcpy(components, aArray, sizeof(components));
575 Matrix4x4Typed(const Matrix4x4Typed& aOther) {
576 memcpy(components, aOther.components, sizeof(components));
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) {}
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;
606 T components[16];
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;
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;
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);
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);
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);
648 * Returns true if the matrix is isomorphic to a 2D affine transformation.
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;
656 return true;
659 bool Is2D(BaseMatrix<T>* aMatrix) const {
660 if (!Is2D()) {
661 return false;
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;
671 return true;
674 BaseMatrix<T> As2D() const {
675 MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform");
677 return BaseMatrix<T>(_11, _12, _21, _22, _41, _42);
680 bool CanDraw2D(BaseMatrix<T>* aMatrix = nullptr) const {
681 if (_14 != 0.0f || _24 != 0.0f || _44 != 1.0f) {
682 return false;
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;
692 return true;
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;
720 return *this;
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.
728 // The transformed value of z is computed as:
729 // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43;
731 // Solving for z when z' = 0 gives us:
732 F z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33;
734 // Compute the transformed point
735 return this->TransformPoint(
736 Point4DTyped<SourceUnits, F>(aPoint.x, aPoint.y, z, 1));
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
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.
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)
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.
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];
770 points[0] = ProjectPoint(aRect.TopLeft());
771 points[1] = ProjectPoint(aRect.TopRight());
772 points[2] = ProjectPoint(aRect.BottomRight());
773 points[3] = ProjectPoint(aRect.BottomLeft());
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();
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);
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();
805 if (intercept.y < 0.0f) {
806 min_y = aClip.Y();
807 } else if (intercept.y > 0.0f) {
808 max_y = aClip.YMost();
813 if (max_x < min_x || max_y < min_y) {
814 return RectTyped<TargetUnits, F>(0, 0, 0, 0);
817 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
818 max_y - min_y);
822 * TransformAndClipBounds transforms aRect as a bounding box, while clipping
823 * the transformed bounds to the extents of aClip.
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);
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);
843 if (max_x < min_x || max_y < min_y) {
844 return RectTyped<TargetUnits, F>(0, 0, 0, 0);
847 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
848 max_y - min_y);
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);
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.
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;
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)),
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];
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);
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();
916 // Emit only unique points
917 if (vertCount == 0 || p != aVerts[vertCount - 1]) {
918 aVerts[vertCount++] = p;
922 return vertCount;
925 static const int kTransformAndClipRectMaxVerts = 32;
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;
938 bool Is2DIntegerTranslation() const {
939 return Is2D() && As2D().IsIntegerTranslation();
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;
948 return TargetPoint4D(x, y, z, w);
951 template <class F>
952 Point4DTyped<TargetUnits, F> TransformPoint(
953 const Point4DTyped<SourceUnits, F>& aPoint) const {
954 Point4DTyped<TargetUnits, F> retPoint;
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;
965 return retPoint;
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;
976 result /= (aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44);
978 return result;
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();
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;
997 quad[0] = TransformPoint(aRect.TopLeft());
998 quad[1] = TransformPoint(aRect.TopRight());
999 quad[2] = TransformPoint(aRect.BottomLeft());
1000 quad[3] = TransformPoint(aRect.BottomRight());
1002 min_x = max_x = quad[0].x;
1003 min_y = max_y = quad[0].y;
1005 for (int i = 1; i < 4; i++) {
1006 if (quad[i].x < min_x) {
1007 min_x = quad[i].x;
1009 if (quad[i].x > max_x) {
1010 max_x = quad[i].x;
1013 if (quad[i].y < min_y) {
1014 min_y = quad[i].y;
1016 if (quad[i].y > max_y) {
1017 max_y = quad[i].y;
1021 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
1022 max_y - min_y);
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);
1030 static Matrix4x4Typed Translation(const TargetPoint3D& aP) {
1031 return Translation(aP.x, aP.y, aP.z);
1034 static Matrix4x4Typed Translation(const TargetPoint& aP) {
1035 return Translation(aP.x, aP.y, 0);
1039 * Apply a translation to this matrix.
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.
1046 * Calling this method will result in this matrix having the same value as
1047 * the result of:
1049 * Matrix4x4::Translation(x, y) * this
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.)
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;
1063 return *this;
1066 Matrix4x4Typed& PreTranslate(const Point3DTyped<UnknownUnits, T>& aPoint) {
1067 return PreTranslate(aPoint.x, aPoint.y, aPoint.z);
1071 * Similar to PreTranslate, but the translation is applied -after- this
1072 * matrix's existing transformation instead of before it.
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.
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;
1096 return *this;
1099 Matrix4x4Typed& PostTranslate(const TargetPoint3D& aPoint) {
1100 return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
1103 Matrix4x4Typed& PostTranslate(const TargetPoint& aPoint) {
1104 return PostTranslate(aPoint.x, aPoint.y, 0);
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);
1113 * Similar to PreTranslate, but applies a scale instead of a translation.
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;
1129 return *this;
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;
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;
1149 * Similar to PostTranslate, but applies a scale instead of a translation.
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;
1165 return *this;
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;
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;
1184 void SkewXY(T aSkew) { (*this)[1] += (*this)[0] * aSkew; }
1186 void SkewXZ(T aSkew) { (*this)[2] += (*this)[0] * aSkew; }
1188 void SkewYZ(T aSkew) { (*this)[2] += (*this)[1] * aSkew; }
1190 Matrix4x4Typed& ChangeBasis(const Point3DTyped<UnknownUnits, T>& aOrigin) {
1191 return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z);
1194 Matrix4x4Typed& ChangeBasis(T aX, T aY, T aZ) {
1195 // Translate to the origin before applying this matrix
1196 PreTranslate(-aX, -aY, -aZ);
1198 // Translate back into position after applying this matrix
1199 PostTranslate(aX, aY, aZ);
1201 return *this;
1204 Matrix4x4Typed& Transpose() {
1205 std::swap(_12, _21);
1206 std::swap(_13, _31);
1207 std::swap(_14, _41);
1209 std::swap(_23, _32);
1210 std::swap(_24, _42);
1212 std::swap(_34, _43);
1214 return *this;
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;
1225 bool operator!=(const Matrix4x4Typed& o) const { return !((*this) == o); }
1227 Matrix4x4Typed& operator=(const Matrix4x4Typed& aOther) = default;
1229 template <typename NewTargetUnits>
1230 Matrix4x4Typed<SourceUnits, NewTargetUnits, T> operator*(
1231 const Matrix4x4Typed<TargetUnits, NewTargetUnits, T>& aMatrix) const {
1232 Matrix4x4Typed<SourceUnits, NewTargetUnits, T> matrix;
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;
1267 return matrix;
1270 Matrix4x4Typed& operator*=(
1271 const Matrix4x4Typed<TargetUnits, TargetUnits, T>& aMatrix) {
1272 *this = *this * aMatrix;
1273 return *this;
1276 /* Returns true if the matrix is an identity matrix.
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;
1285 bool IsSingular() const { return Determinant() == 0.0; }
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;
1302 // Invert() is not unit-correct. Prefer Inverse() where possible.
1303 bool Invert() {
1304 T det = Determinant();
1305 if (!det) {
1306 return false;
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;
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;
1361 return true;
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;
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);
1379 return Nothing();
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];
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);
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);
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;
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;
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));
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;
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;
1471 Matrix4x4Typed mat = *this;
1472 mat.Normalize();
1473 if (HasPerspectiveComponent()) {
1474 // We do not support projection matrices
1475 return false;
1478 // Extract translation
1479 translation.x = mat._41;
1480 translation.y = mat._42;
1481 translation.z = mat._43;
1483 // Remove translation
1484 mat._41 = 0.0f;
1485 mat._42 = 0.0f;
1486 mat._43 = 0.0f;
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);
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;
1500 // Extract rotation
1501 rotation.SetFromRotationMatrix(this->ToUnknownMatrix());
1502 return true;
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;
1514 _11 = 1.0f - (yy + zz);
1515 _21 = xy - wz;
1516 _31 = xz + wy;
1517 _41 = 0.0f;
1519 _12 = xy + wz;
1520 _22 = 1.0f - (xx + zz);
1521 _32 = yz - wx;
1522 _42 = 0.0f;
1524 _13 = xz - wy;
1525 _23 = yz + wx;
1526 _33 = 1.0f - (xx + yy);
1527 _43 = 0.0f;
1529 _14 = _42 = _43 = 0.0f;
1530 _44 = 1.0f;
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>();
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);
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;
1569 temp = _11;
1570 _11 += tanY * _21;
1571 _21 += tanX * temp;
1573 temp = _12;
1574 _12 += tanY * _22;
1575 _22 += tanX * temp;
1577 temp = _13;
1578 _13 += tanY * _23;
1579 _23 += tanX * temp;
1581 temp = _14;
1582 _14 += tanY * _24;
1583 _24 += tanX * temp;
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));
1591 T temp;
1593 temp = _21;
1594 _21 = cosTheta * _21 + sinTheta * _31;
1595 _31 = -sinTheta * temp + cosTheta * _31;
1597 temp = _22;
1598 _22 = cosTheta * _22 + sinTheta * _32;
1599 _32 = -sinTheta * temp + cosTheta * _32;
1601 temp = _23;
1602 _23 = cosTheta * _23 + sinTheta * _33;
1603 _33 = -sinTheta * temp + cosTheta * _33;
1605 temp = _24;
1606 _24 = cosTheta * _24 + sinTheta * _34;
1607 _34 = -sinTheta * temp + cosTheta * _34;
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));
1615 T temp;
1617 temp = _11;
1618 _11 = cosTheta * _11 + -sinTheta * _31;
1619 _31 = sinTheta * temp + cosTheta * _31;
1621 temp = _12;
1622 _12 = cosTheta * _12 + -sinTheta * _32;
1623 _32 = sinTheta * temp + cosTheta * _32;
1625 temp = _13;
1626 _13 = cosTheta * _13 + -sinTheta * _33;
1627 _33 = sinTheta * temp + cosTheta * _33;
1629 temp = _14;
1630 _14 = cosTheta * _14 + -sinTheta * _34;
1631 _34 = sinTheta * temp + cosTheta * _34;
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));
1639 T temp;
1641 temp = _11;
1642 _11 = cosTheta * _11 + sinTheta * _21;
1643 _21 = -sinTheta * temp + cosTheta * _21;
1645 temp = _12;
1646 _12 = cosTheta * _12 + sinTheta * _22;
1647 _22 = -sinTheta * temp + cosTheta * _22;
1649 temp = _13;
1650 _13 = cosTheta * _13 + sinTheta * _23;
1651 _23 = -sinTheta * temp + cosTheta * _23;
1653 temp = _14;
1654 _14 = cosTheta * _14 + sinTheta * _24;
1655 _24 = -sinTheta * temp + cosTheta * _24;
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;
1667 vector.RobustNormalize();
1669 double x = vector.x;
1670 double y = vector.y;
1671 double z = vector.z;
1673 double cosTheta = FlushToZero(cos(aTheta));
1674 double sinTheta = FlushToZero(sin(aTheta));
1676 // sin(aTheta / 2) * cos(aTheta / 2)
1677 double sc = sinTheta / 2;
1678 // pow(sin(aTheta / 2), 2)
1679 double sq = (1 - cosTheta) / 2;
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;
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;
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));
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;
1721 return ac.CrossProduct(ab);
1725 * Returns true if the matrix has any transform other
1726 * than a straight translation.
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);
1737 * Returns true if the matrix is anything other than a straight
1738 * translation by integers.
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));
1747 * Return true if the matrix is with perspective (w).
1749 bool HasPerspectiveComponent() const {
1750 return _14 != 0 || _24 != 0 || _34 != 0 || _44 != 1;
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.
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;
1764 return false;
1768 * Convert between typed and untyped matrices.
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};
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};
1783 * For convenience, overload FromUnknownMatrix() for Maybe<Matrix>.
1785 static Maybe<Matrix4x4Typed> FromUnknownMatrix(
1786 const Maybe<UnknownMatrix>& aUnknown) {
1787 if (aUnknown.isSome()) {
1788 return Some(FromUnknownMatrix(*aUnknown));
1790 return Nothing();
1794 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
1795 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits, double> Matrix4x4Double;
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) {}
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;
1853 bool operator!=(const Matrix5x4& aMatrix) const {
1854 return !(*this == aMatrix);
1857 Matrix5x4 operator*(const Matrix5x4& aMatrix) const {
1858 Matrix5x4 resultMatrix;
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;
1905 return resultMatrix;
1908 Matrix5x4& operator*=(const Matrix5x4& aMatrix) {
1909 *this = *this * aMatrix;
1910 return *this;
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;
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;
1937 Float components[20];
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.
1948 enum class MatrixType : uint8_t {
1949 Identity,
1950 Simple, // 2x3 Matrix
1951 Full // 4x4 Matrix
1954 template <typename SourceUnits, typename TargetUnits>
1955 class Matrix4x4TypedFlagged
1956 : protected Matrix4x4Typed<SourceUnits, TargetUnits> {
1957 public:
1958 using Parent = Matrix4x4Typed<SourceUnits, TargetUnits>;
1959 using TargetPoint = PointTyped<TargetUnits>;
1960 using Parent::_11;
1961 using Parent::_12;
1962 using Parent::_13;
1963 using Parent::_14;
1964 using Parent::_21;
1965 using Parent::_22;
1966 using Parent::_23;
1967 using Parent::_24;
1968 using Parent::_31;
1969 using Parent::_32;
1970 using Parent::_33;
1971 using Parent::_34;
1972 using Parent::_41;
1973 using Parent::_42;
1974 using Parent::_43;
1975 using Parent::_44;
1977 Matrix4x4TypedFlagged() : mType(MatrixType::Identity) {}
1979 Matrix4x4TypedFlagged(Float a11, Float a12, Float a13, Float a14, Float a21,
1980 Float a22, Float a23, Float a24, Float a31, Float a32,
1981 Float a33, Float a34, Float a41, Float a42, Float a43,
1982 Float a44)
1983 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41,
1984 a42, a43, a44) {
1985 Analyze();
1988 MOZ_IMPLICIT Matrix4x4TypedFlagged(const Parent& aOther) : Parent(aOther) {
1989 Analyze();
1992 template <typename NewMatrix4x4TypedFlagged>
1993 [[nodiscard]] NewMatrix4x4TypedFlagged Cast() const {
1994 return NewMatrix4x4TypedFlagged(_11, _12, _13, _14, _21, _22, _23, _24, _31,
1995 _32, _33, _34, _41, _42, _43, _44, mType);
1998 static Matrix4x4TypedFlagged Translation2d(Float aX, Float aY) {
1999 MatrixType matrixType = MatrixType::Simple;
2000 if (aX == 0.0 && aY == 0.0) {
2001 matrixType = MatrixType::Identity;
2003 return Matrix4x4TypedFlagged(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
2004 0.0f, 0.0f, 1.0f, 0.0f, aX, aY, 0.0f, 1.0f,
2005 matrixType);
2008 static Matrix4x4TypedFlagged Scaling(Float aScaleX, Float aScaleY,
2009 Float aScaleZ) {
2010 MatrixType matrixType = MatrixType::Full;
2011 if (aScaleZ == 1.0) {
2012 if (aScaleX == 1.0 && aScaleY == 1.0) {
2013 matrixType = MatrixType::Identity;
2014 } else {
2015 matrixType = MatrixType::Simple;
2018 return Matrix4x4TypedFlagged(aScaleX, 0.0f, 0.0f, 0.0f, 0.0f, aScaleY, 0.0f,
2019 0.0f, 0.0f, 0.0f, aScaleZ, 0.0f, 0.0f, 0.0f,
2020 0.0f, 1.0f, matrixType);
2023 template <class F>
2024 PointTyped<TargetUnits, F> TransformPoint(
2025 const PointTyped<SourceUnits, F>& aPoint) const {
2026 if (mType == MatrixType::Identity) {
2027 return aPoint;
2030 if (mType == MatrixType::Simple) {
2031 return TransformPointSimple(aPoint);
2034 return Parent::TransformPoint(aPoint);
2037 template <class F>
2038 RectTyped<TargetUnits, F> TransformBounds(
2039 const RectTyped<SourceUnits, F>& aRect) const {
2040 if (mType == MatrixType::Identity) {
2041 return aRect;
2044 if (mType == MatrixType::Simple) {
2045 PointTyped<TargetUnits, F> quad[4];
2046 F min_x, max_x;
2047 F min_y, max_y;
2049 quad[0] = TransformPointSimple(aRect.TopLeft());
2050 quad[1] = TransformPointSimple(aRect.TopRight());
2051 quad[2] = TransformPointSimple(aRect.BottomLeft());
2052 quad[3] = TransformPointSimple(aRect.BottomRight());
2054 min_x = max_x = quad[0].x;
2055 min_y = max_y = quad[0].y;
2057 for (int i = 1; i < 4; i++) {
2058 if (quad[i].x < min_x) {
2059 min_x = quad[i].x;
2061 if (quad[i].x > max_x) {
2062 max_x = quad[i].x;
2065 if (quad[i].y < min_y) {
2066 min_y = quad[i].y;
2068 if (quad[i].y > max_y) {
2069 max_y = quad[i].y;
2073 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
2074 max_y - min_y);
2077 return Parent::TransformBounds(aRect);
2080 template <class F>
2081 RectTyped<TargetUnits, F> TransformAndClipBounds(
2082 const RectTyped<SourceUnits, F>& aRect,
2083 const RectTyped<TargetUnits, F>& aClip) const {
2084 if (mType == MatrixType::Identity) {
2085 const RectTyped<SourceUnits, F>& clipped = aRect.Intersect(aClip);
2086 return RectTyped<TargetUnits, F>(clipped.X(), clipped.Y(),
2087 clipped.Width(), clipped.Height());
2090 if (mType == MatrixType::Simple) {
2091 PointTyped<UnknownUnits, F> p1 = TransformPointSimple(aRect.TopLeft());
2092 PointTyped<UnknownUnits, F> p2 = TransformPointSimple(aRect.TopRight());
2093 PointTyped<UnknownUnits, F> p3 = TransformPointSimple(aRect.BottomLeft());
2094 PointTyped<UnknownUnits, F> p4 =
2095 TransformPointSimple(aRect.BottomRight());
2097 F min_x = std::min(std::min(std::min(p1.x, p2.x), p3.x), p4.x);
2098 F max_x = std::max(std::max(std::max(p1.x, p2.x), p3.x), p4.x);
2099 F min_y = std::min(std::min(std::min(p1.y, p2.y), p3.y), p4.y);
2100 F max_y = std::max(std::max(std::max(p1.y, p2.y), p3.y), p4.y);
2102 TargetPoint topLeft(std::max(min_x, aClip.x), std::max(min_y, aClip.y));
2103 F width = std::min(max_x, aClip.XMost()) - topLeft.x;
2104 F height = std::min(max_y, aClip.YMost()) - topLeft.y;
2106 return RectTyped<TargetUnits, F>(topLeft.x, topLeft.y, width, height);
2108 return Parent::TransformAndClipBounds(aRect, aClip);
2111 bool FuzzyEqual(const Parent& o) const { return Parent::FuzzyEqual(o); }
2113 bool FuzzyEqual(const Matrix4x4TypedFlagged& o) const {
2114 if (mType == MatrixType::Identity && o.mType == MatrixType::Identity) {
2115 return true;
2117 return Parent::FuzzyEqual(o);
2120 Matrix4x4TypedFlagged& PreTranslate(Float aX, Float aY, Float aZ) {
2121 if (mType == MatrixType::Identity) {
2122 _41 = aX;
2123 _42 = aY;
2124 _43 = aZ;
2126 if (!aZ) {
2127 mType = MatrixType::Simple;
2128 return *this;
2130 mType = MatrixType::Full;
2131 return *this;
2134 Parent::PreTranslate(aX, aY, aZ);
2136 if (aZ != 0) {
2137 mType = MatrixType::Full;
2140 return *this;
2143 Matrix4x4TypedFlagged& PostTranslate(Float aX, Float aY, Float aZ) {
2144 if (mType == MatrixType::Identity) {
2145 _41 = aX;
2146 _42 = aY;
2147 _43 = aZ;
2149 if (!aZ) {
2150 mType = MatrixType::Simple;
2151 return *this;
2153 mType = MatrixType::Full;
2154 return *this;
2157 Parent::PostTranslate(aX, aY, aZ);
2159 if (aZ != 0) {
2160 mType = MatrixType::Full;
2163 return *this;
2166 Matrix4x4TypedFlagged& ChangeBasis(Float aX, Float aY, Float aZ) {
2167 // Translate to the origin before applying this matrix
2168 PreTranslate(-aX, -aY, -aZ);
2170 // Translate back into position after applying this matrix
2171 PostTranslate(aX, aY, aZ);
2173 return *this;
2176 bool IsIdentity() const { return mType == MatrixType::Identity; }
2178 template <class F>
2179 Point4DTyped<TargetUnits, F> ProjectPoint(
2180 const PointTyped<SourceUnits, F>& aPoint) const {
2181 if (mType == MatrixType::Identity) {
2182 return Point4DTyped<TargetUnits, F>(aPoint.x, aPoint.y, 0, 1);
2185 if (mType == MatrixType::Simple) {
2186 TargetPoint point = TransformPointSimple(aPoint);
2187 return Point4DTyped<TargetUnits, F>(point.x, point.y, 0, 1);
2190 return Parent::ProjectPoint(aPoint);
2193 Matrix4x4TypedFlagged& ProjectTo2D() {
2194 if (mType == MatrixType::Full) {
2195 Parent::ProjectTo2D();
2197 return *this;
2200 bool IsSingular() const {
2201 if (mType == MatrixType::Identity) {
2202 return false;
2204 return Parent::Determinant() == 0.0;
2207 bool Invert() {
2208 if (mType == MatrixType::Identity) {
2209 return true;
2212 return Parent::Invert();
2215 Matrix4x4TypedFlagged<TargetUnits, SourceUnits> Inverse() const {
2216 typedef Matrix4x4TypedFlagged<TargetUnits, SourceUnits> InvertedMatrix;
2217 InvertedMatrix clone = Cast<InvertedMatrix>();
2218 if (mType == MatrixType::Identity) {
2219 return clone;
2221 DebugOnly<bool> inverted = clone.Invert();
2222 MOZ_ASSERT(inverted,
2223 "Attempted to get the inverse of a non-invertible matrix");
2225 // Inverting a 2D Matrix should result in a 2D matrix, ergo mType doesn't
2226 // change.
2227 return clone;
2230 Maybe<Matrix4x4TypedFlagged<TargetUnits, SourceUnits>> MaybeInverse() const {
2231 typedef Matrix4x4TypedFlagged<TargetUnits, SourceUnits> InvertedMatrix;
2232 InvertedMatrix clone = Cast<InvertedMatrix>();
2233 if (clone.Invert()) {
2234 return Some(clone);
2236 return Nothing();
2239 template <typename NewTargetUnits>
2240 bool operator==(
2241 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2242 if (mType == MatrixType::Identity &&
2243 aMatrix.mType == MatrixType::Identity) {
2244 return true;
2246 // Depending on the usage it may make sense to compare more flags.
2247 return Parent::operator==(aMatrix);
2250 template <typename NewTargetUnits>
2251 bool operator!=(
2252 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2253 if (mType == MatrixType::Identity &&
2254 aMatrix.mType == MatrixType::Identity) {
2255 return false;
2257 // Depending on the usage it may make sense to compare more flags.
2258 return Parent::operator!=(aMatrix);
2261 template <typename NewTargetUnits>
2262 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> operator*(
2263 const Matrix4x4Typed<TargetUnits, NewTargetUnits>& aMatrix) const {
2264 if (mType == MatrixType::Identity) {
2265 return aMatrix;
2268 if (mType == MatrixType::Simple) {
2269 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2270 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
2271 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
2272 matrix._31 = aMatrix._31;
2273 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41;
2274 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
2275 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
2276 matrix._32 = aMatrix._32;
2277 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42;
2278 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23;
2279 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23;
2280 matrix._33 = aMatrix._33;
2281 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + aMatrix._43;
2282 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24;
2283 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24;
2284 matrix._34 = aMatrix._34;
2285 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + aMatrix._44;
2286 matrix.Analyze();
2287 return matrix;
2290 return Parent::operator*(aMatrix);
2293 template <typename NewTargetUnits>
2294 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> operator*(
2295 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2296 if (mType == MatrixType::Identity) {
2297 return aMatrix;
2300 if (aMatrix.mType == MatrixType::Identity) {
2301 return Cast<Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits>>();
2304 if (mType == MatrixType::Simple && aMatrix.mType == MatrixType::Simple) {
2305 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2306 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
2307 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
2308 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41;
2309 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
2310 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
2311 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42;
2312 matrix.mType = MatrixType::Simple;
2313 return matrix;
2314 } else if (mType == MatrixType::Simple) {
2315 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2316 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
2317 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
2318 matrix._31 = aMatrix._31;
2319 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41;
2320 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
2321 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
2322 matrix._32 = aMatrix._32;
2323 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42;
2324 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23;
2325 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23;
2326 matrix._33 = aMatrix._33;
2327 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + aMatrix._43;
2328 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24;
2329 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24;
2330 matrix._34 = aMatrix._34;
2331 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + aMatrix._44;
2332 matrix.mType = MatrixType::Full;
2333 return matrix;
2334 } else if (aMatrix.mType == MatrixType::Simple) {
2335 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2336 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _14 * aMatrix._41;
2337 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _24 * aMatrix._41;
2338 matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _34 * aMatrix._41;
2339 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _44 * aMatrix._41;
2340 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _14 * aMatrix._42;
2341 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _24 * aMatrix._42;
2342 matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _34 * aMatrix._42;
2343 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _44 * aMatrix._42;
2344 matrix._13 = _13;
2345 matrix._23 = _23;
2346 matrix._33 = _33;
2347 matrix._43 = _43;
2348 matrix._14 = _14;
2349 matrix._24 = _24;
2350 matrix._34 = _34;
2351 matrix._44 = _44;
2352 matrix.mType = MatrixType::Full;
2353 return matrix;
2356 return Parent::operator*(aMatrix);
2359 bool Is2D() const { return mType != MatrixType::Full; }
2361 bool CanDraw2D(Matrix* aMatrix = nullptr) const {
2362 if (mType != MatrixType::Full) {
2363 if (aMatrix) {
2364 aMatrix->_11 = _11;
2365 aMatrix->_12 = _12;
2366 aMatrix->_21 = _21;
2367 aMatrix->_22 = _22;
2368 aMatrix->_31 = _41;
2369 aMatrix->_32 = _42;
2371 return true;
2373 return Parent::CanDraw2D(aMatrix);
2376 bool Is2D(Matrix* aMatrix) const {
2377 if (!Is2D()) {
2378 return false;
2380 if (aMatrix) {
2381 aMatrix->_11 = _11;
2382 aMatrix->_12 = _12;
2383 aMatrix->_21 = _21;
2384 aMatrix->_22 = _22;
2385 aMatrix->_31 = _41;
2386 aMatrix->_32 = _42;
2388 return true;
2391 template <class F>
2392 RectTyped<TargetUnits, F> ProjectRectBounds(
2393 const RectTyped<SourceUnits, F>& aRect,
2394 const RectTyped<TargetUnits, F>& aClip) const {
2395 return Parent::ProjectRectBounds(aRect, aClip);
2398 const Parent& GetMatrix() const { return *this; }
2400 Matrix4x4Flagged ToUnknownMatrix() const {
2401 return Matrix4x4Flagged{_11, _12, _13, _14, _21, _22, _23, _24, _31,
2402 _32, _33, _34, _41, _42, _43, _44, mType};
2405 private:
2406 Matrix4x4TypedFlagged(Float a11, Float a12, Float a13, Float a14, Float a21,
2407 Float a22, Float a23, Float a24, Float a31, Float a32,
2408 Float a33, Float a34, Float a41, Float a42, Float a43,
2409 Float a44, const MatrixType aType)
2410 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41,
2411 a42, a43, a44),
2412 mType(aType) {}
2413 static Matrix4x4TypedFlagged FromUnknownMatrix(
2414 const Matrix4x4Flagged& aUnknown) {
2415 return Matrix4x4TypedFlagged{
2416 aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14, aUnknown._21,
2417 aUnknown._22, aUnknown._23, aUnknown._24, aUnknown._31, aUnknown._32,
2418 aUnknown._33, aUnknown._34, aUnknown._41, aUnknown._42, aUnknown._43,
2419 aUnknown._44, aUnknown.mType};
2422 template <class F>
2423 PointTyped<TargetUnits, F> TransformPointSimple(
2424 const PointTyped<SourceUnits, F>& aPoint) const {
2425 PointTyped<SourceUnits, F> temp;
2426 temp.x = aPoint.x * _11 + aPoint.y * +_21 + _41;
2427 temp.y = aPoint.x * _12 + aPoint.y * +_22 + _42;
2428 return temp;
2431 void Analyze() {
2432 if (Parent::IsIdentity()) {
2433 mType = MatrixType::Identity;
2434 return;
2437 if (Parent::Is2D()) {
2438 mType = MatrixType::Simple;
2439 return;
2442 mType = MatrixType::Full;
2445 MatrixType mType;
2447 template <typename, typename>
2448 friend class Matrix4x4TypedFlagged;
2451 using Matrix4x4Flagged = Matrix4x4TypedFlagged<UnknownUnits, UnknownUnits>;
2453 } // namespace gfx
2454 } // namespace mozilla
2456 #endif /* MOZILLA_GFX_MATRIX_H_ */