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_
14 #include "Quaternion.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"
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;
32 Span
<Point4DTyped
<UnknownUnits
, F
>> IntersectPolygon(
33 Span
<Point4DTyped
<UnknownUnits
, F
>> aPoints
,
34 const Point4DTyped
<UnknownUnits
, F
>& aPlaneNormal
,
35 Span
<Point4DTyped
<UnknownUnits
, F
>> aDestBuffer
);
38 using BaseMatrixScales
= BaseScaleFactors2D
<UnknownUnits
, UnknownUnits
, T
>;
40 using MatrixScales
= BaseMatrixScales
<float>;
41 using MatrixScalesDouble
= BaseMatrixScales
<double>;
45 // Alias that maps to either Point or PointDouble depending on whether T is a
47 typedef PointTyped
<UnknownUnits
, T
> MatrixPoint
;
48 // Same for size and rect
49 typedef SizeTyped
<UnknownUnits
, T
> MatrixSize
;
50 typedef RectTyped
<UnknownUnits
, T
> MatrixRect
;
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
) {}
66 explicit BaseMatrix(const BaseMatrix
<T2
>& aOther
)
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 {
89 retPoint
.x
= aPoint
.x
* _11
+ aPoint
.y
* _21
+ _31
;
90 retPoint
.y
= aPoint
.x
* _12
+ aPoint
.y
* _22
+ _32
;
95 MatrixSize
TransformSize(const MatrixSize
& aSize
) const {
98 retSize
.width
= aSize
.width
* _11
+ aSize
.height
* _21
;
99 retSize
.height
= aSize
.width
* _12
+ aSize
.height
* _22
;
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 {
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
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
166 BaseMatrix
<T
>& PreTranslate(T aX
, T aY
) {
167 _31
+= _11
* aX
+ _21
* aY
;
168 _32
+= _12
* aX
+ _22
* aY
;
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
189 BaseMatrix
<T
>& PostTranslate(T aX
, T aY
) {
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
) {
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
) {
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;
247 // Compute co-factors.
250 T C
= _21
* _32
- _22
* _31
;
253 T F
= _31
* _12
- _11
* _32
;
255 T det
= Determinant();
273 BaseMatrix
<T
> Inverse() const {
274 BaseMatrix
<T
> clone
= *this;
275 DebugOnly
<bool> inverted
= clone
.Invert();
277 "Attempted to get the inverse of a non-invertible matrix");
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
;
291 this->_31
* aMatrix
._11
+ this->_32
* aMatrix
._21
+ aMatrix
._31
;
293 this->_31
* aMatrix
._12
+ this->_32
* aMatrix
._22
+ aMatrix
._32
;
298 BaseMatrix
<T
>& operator*=(const BaseMatrix
<T
>& aMatrix
) {
299 *this = *this * aMatrix
;
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;
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)) {
348 } else if (FuzzyEqual(_22
, 0) && FuzzyEqual(_11
, 0)) {
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
);
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
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();
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
);
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
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
{
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
;
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
)
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
));
580 explicit Matrix4x4Typed(
581 const Matrix4x4Typed
<SourceUnits
, TargetUnits
, T2
>& aOther
)
601 T _11
, _12
, _13
, _14
;
602 T _21
, _22
, _23
, _24
;
603 T _31
, _32
, _33
, _34
;
604 T _41
, _42
, _43
, _44
;
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] << ';';
618 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
620 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
622 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3]
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
) +
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
) +
638 // External code should avoid calling this, and instead use
639 // ViewAs() from UnitTransforms.h, which requires providing
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.
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
) {
659 bool Is2D(BaseMatrix
<T
>* aMatrix
) const {
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
) {
695 Matrix4x4Typed
& ProjectTo2D() {
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
;
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));
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
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
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
) {
802 } else if (intercept
.x
> 0.0f
) {
803 max_x
= aClip
.XMost();
805 if (intercept
.y
< 0.0f
) {
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
,
822 * TransformAndClipBounds transforms aRect as a bounding box, while clipping
823 * the transformed bounds to the extents of aClip.
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
,
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
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()),
899 polygon
= IntersectPolygon
<F
>(polygon
, P4D(-1.0, 0.0, 0.0, aClip
.XMost()),
901 polygon
= IntersectPolygon
<F
>(polygon
, P4D(0.0, 1.0, 0.0, -aClip
.Y()),
903 polygon
= IntersectPolygon
<F
>(polygon
, P4D(0.0, -1.0, 0.0, aClip
.YMost()),
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);
914 p
= srcPoint
.As2DPoint();
916 // Emit only unique points
917 if (vertCount
== 0 || p
!= aVerts
[vertCount
- 1]) {
918 aVerts
[vertCount
++] = p
;
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
;
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
);
952 Point4DTyped
<TargetUnits
, F
> TransformPoint(
953 const Point4DTyped
<SourceUnits
, F
>& aPoint
) const {
954 Point4DTyped
<TargetUnits
, F
> retPoint
;
957 aPoint
.x
* _11
+ aPoint
.y
* _21
+ aPoint
.z
* _31
+ aPoint
.w
* _41
;
959 aPoint
.x
* _12
+ aPoint
.y
* _22
+ aPoint
.z
* _32
+ aPoint
.w
* _42
;
961 aPoint
.x
* _13
+ aPoint
.y
* _23
+ aPoint
.z
* _33
+ aPoint
.w
* _43
;
963 aPoint
.x
* _14
+ aPoint
.y
* _24
+ aPoint
.z
* _34
+ aPoint
.w
* _44
;
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
);
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();
989 GFX2D_API RectTyped
<TargetUnits
, F
> TransformBounds(
990 const RectTyped
<SourceUnits
, F
>& aRect
) const {
991 PointTyped
<TargetUnits
, F
> quad
[4];
995 quad
[0] = TransformPoint(aRect
.TopLeft());
996 quad
[1] = TransformPoint(aRect
.TopRight());
997 quad
[2] = TransformPoint(aRect
.BottomLeft());
998 quad
[3] = TransformPoint(aRect
.BottomRight());
1000 min_x
= max_x
= quad
[0].x
;
1001 min_y
= max_y
= quad
[0].y
;
1003 for (int i
= 1; i
< 4; i
++) {
1004 if (quad
[i
].x
< min_x
) {
1007 if (quad
[i
].x
> max_x
) {
1011 if (quad
[i
].y
< min_y
) {
1014 if (quad
[i
].y
> max_y
) {
1019 return RectTyped
<TargetUnits
, F
>(min_x
, min_y
, max_x
- min_x
,
1023 static Matrix4x4Typed
Translation(T aX
, T aY
, T aZ
) {
1024 return Matrix4x4Typed(1.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
,
1025 0.0f
, 1.0f
, 0.0f
, aX
, aY
, aZ
, 1.0f
);
1028 static Matrix4x4Typed
Translation(const TargetPoint3D
& aP
) {
1029 return Translation(aP
.x
, aP
.y
, aP
.z
);
1032 static Matrix4x4Typed
Translation(const TargetPoint
& aP
) {
1033 return Translation(aP
.x
, aP
.y
, 0);
1037 * Apply a translation to this matrix.
1039 * The "Pre" in this method's name means that the translation is applied
1040 * -before- this matrix's existing transformation. That is, any vector that
1041 * is multiplied by the resulting matrix will first be translated, then be
1042 * transformed by the original transform.
1044 * Calling this method will result in this matrix having the same value as
1047 * Matrix4x4::Translation(x, y) * this
1049 * (Note that in performance critical code multiplying by the result of a
1050 * Translation()/Scaling() call is not recommended since that results in a
1051 * full matrix multiply involving 64 floating-point multiplications. Calling
1052 * this method would be preferred since it only involves 12 floating-point
1055 Matrix4x4Typed
& PreTranslate(T aX
, T aY
, T aZ
) {
1056 _41
+= aX
* _11
+ aY
* _21
+ aZ
* _31
;
1057 _42
+= aX
* _12
+ aY
* _22
+ aZ
* _32
;
1058 _43
+= aX
* _13
+ aY
* _23
+ aZ
* _33
;
1059 _44
+= aX
* _14
+ aY
* _24
+ aZ
* _34
;
1064 Matrix4x4Typed
& PreTranslate(const Point3DTyped
<UnknownUnits
, T
>& aPoint
) {
1065 return PreTranslate(aPoint
.x
, aPoint
.y
, aPoint
.z
);
1069 * Similar to PreTranslate, but the translation is applied -after- this
1070 * matrix's existing transformation instead of before it.
1072 * This method is generally less used than PreTranslate since typically code
1073 * wants to adjust an existing user space to device space matrix to create a
1074 * transform to device space from a -new- user space (translated from the
1075 * previous user space). In that case consumers will need to use the Pre*
1076 * variants of the matrix methods rather than using the Post* methods, since
1077 * the Post* methods add a transform to the device space end of the
1080 Matrix4x4Typed
& PostTranslate(T aX
, T aY
, T aZ
) {
1097 Matrix4x4Typed
& PostTranslate(const TargetPoint3D
& aPoint
) {
1098 return PostTranslate(aPoint
.x
, aPoint
.y
, aPoint
.z
);
1101 Matrix4x4Typed
& PostTranslate(const TargetPoint
& aPoint
) {
1102 return PostTranslate(aPoint
.x
, aPoint
.y
, 0);
1105 static Matrix4x4Typed
Scaling(T aScaleX
, T aScaleY
, T aScaleZ
) {
1106 return Matrix4x4Typed(aScaleX
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, aScaleY
, 0.0f
, 0.0f
,
1107 0.0f
, 0.0f
, aScaleZ
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 1.0f
);
1111 * Similar to PreTranslate, but applies a scale instead of a translation.
1113 Matrix4x4Typed
& PreScale(T aX
, T aY
, T aZ
) {
1130 template <typename NewSourceUnits
>
1131 [[nodiscard
]] Matrix4x4Typed
<NewSourceUnits
, TargetUnits
> PreScale(
1132 const ScaleFactor
<NewSourceUnits
, SourceUnits
>& aScale
) const {
1133 auto clone
= Cast
<Matrix4x4Typed
<NewSourceUnits
, TargetUnits
>>();
1134 clone
.PreScale(aScale
.scale
, aScale
.scale
, 1);
1138 template <typename NewSourceUnits
>
1139 [[nodiscard
]] Matrix4x4Typed
<NewSourceUnits
, TargetUnits
> PreScale(
1140 const BaseScaleFactors2D
<NewSourceUnits
, SourceUnits
, T
>& aScale
) const {
1141 auto clone
= Cast
<Matrix4x4Typed
<NewSourceUnits
, TargetUnits
>>();
1142 clone
.PreScale(aScale
.xScale
, aScale
.yScale
, 1);
1147 * Similar to PostTranslate, but applies a scale instead of a translation.
1149 Matrix4x4Typed
& PostScale(T aScaleX
, T aScaleY
, T aScaleZ
) {
1166 template <typename NewTargetUnits
>
1167 [[nodiscard
]] Matrix4x4Typed
<SourceUnits
, NewTargetUnits
> PostScale(
1168 const ScaleFactor
<TargetUnits
, NewTargetUnits
>& aScale
) const {
1169 auto clone
= Cast
<Matrix4x4Typed
<SourceUnits
, NewTargetUnits
>>();
1170 clone
.PostScale(aScale
.scale
, aScale
.scale
, 1);
1174 template <typename NewTargetUnits
>
1175 [[nodiscard
]] Matrix4x4Typed
<SourceUnits
, NewTargetUnits
> PostScale(
1176 const BaseScaleFactors2D
<TargetUnits
, NewTargetUnits
, T
>& aScale
) const {
1177 auto clone
= Cast
<Matrix4x4Typed
<SourceUnits
, NewTargetUnits
>>();
1178 clone
.PostScale(aScale
.xScale
, aScale
.yScale
, 1);
1182 void SkewXY(T aSkew
) { (*this)[1] += (*this)[0] * aSkew
; }
1184 void SkewXZ(T aSkew
) { (*this)[2] += (*this)[0] * aSkew
; }
1186 void SkewYZ(T aSkew
) { (*this)[2] += (*this)[1] * aSkew
; }
1188 Matrix4x4Typed
& ChangeBasis(const Point3DTyped
<UnknownUnits
, T
>& aOrigin
) {
1189 return ChangeBasis(aOrigin
.x
, aOrigin
.y
, aOrigin
.z
);
1192 Matrix4x4Typed
& ChangeBasis(T aX
, T aY
, T aZ
) {
1193 // Translate to the origin before applying this matrix
1194 PreTranslate(-aX
, -aY
, -aZ
);
1196 // Translate back into position after applying this matrix
1197 PostTranslate(aX
, aY
, aZ
);
1202 Matrix4x4Typed
& Transpose() {
1203 std::swap(_12
, _21
);
1204 std::swap(_13
, _31
);
1205 std::swap(_14
, _41
);
1207 std::swap(_23
, _32
);
1208 std::swap(_24
, _42
);
1210 std::swap(_34
, _43
);
1215 bool operator==(const Matrix4x4Typed
& o
) const {
1216 // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics
1217 return _11
== o
._11
&& _12
== o
._12
&& _13
== o
._13
&& _14
== o
._14
&&
1218 _21
== o
._21
&& _22
== o
._22
&& _23
== o
._23
&& _24
== o
._24
&&
1219 _31
== o
._31
&& _32
== o
._32
&& _33
== o
._33
&& _34
== o
._34
&&
1220 _41
== o
._41
&& _42
== o
._42
&& _43
== o
._43
&& _44
== o
._44
;
1223 bool operator!=(const Matrix4x4Typed
& o
) const { return !((*this) == o
); }
1225 Matrix4x4Typed
& operator=(const Matrix4x4Typed
& aOther
) = default;
1227 template <typename NewTargetUnits
>
1228 Matrix4x4Typed
<SourceUnits
, NewTargetUnits
, T
> operator*(
1229 const Matrix4x4Typed
<TargetUnits
, NewTargetUnits
, T
>& aMatrix
) const {
1230 Matrix4x4Typed
<SourceUnits
, NewTargetUnits
, T
> matrix
;
1232 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
+ _13
* aMatrix
._31
+
1234 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
+ _23
* aMatrix
._31
+
1236 matrix
._31
= _31
* aMatrix
._11
+ _32
* aMatrix
._21
+ _33
* aMatrix
._31
+
1238 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ _43
* aMatrix
._31
+
1240 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
+ _13
* aMatrix
._32
+
1242 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
+ _23
* aMatrix
._32
+
1244 matrix
._32
= _31
* aMatrix
._12
+ _32
* aMatrix
._22
+ _33
* aMatrix
._32
+
1246 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ _43
* aMatrix
._32
+
1248 matrix
._13
= _11
* aMatrix
._13
+ _12
* aMatrix
._23
+ _13
* aMatrix
._33
+
1250 matrix
._23
= _21
* aMatrix
._13
+ _22
* aMatrix
._23
+ _23
* aMatrix
._33
+
1252 matrix
._33
= _31
* aMatrix
._13
+ _32
* aMatrix
._23
+ _33
* aMatrix
._33
+
1254 matrix
._43
= _41
* aMatrix
._13
+ _42
* aMatrix
._23
+ _43
* aMatrix
._33
+
1256 matrix
._14
= _11
* aMatrix
._14
+ _12
* aMatrix
._24
+ _13
* aMatrix
._34
+
1258 matrix
._24
= _21
* aMatrix
._14
+ _22
* aMatrix
._24
+ _23
* aMatrix
._34
+
1260 matrix
._34
= _31
* aMatrix
._14
+ _32
* aMatrix
._24
+ _33
* aMatrix
._34
+
1262 matrix
._44
= _41
* aMatrix
._14
+ _42
* aMatrix
._24
+ _43
* aMatrix
._34
+
1268 Matrix4x4Typed
& operator*=(
1269 const Matrix4x4Typed
<TargetUnits
, TargetUnits
, T
>& aMatrix
) {
1270 *this = *this * aMatrix
;
1274 /* Returns true if the matrix is an identity matrix.
1276 bool IsIdentity() const {
1277 return _11
== 1.0f
&& _12
== 0.0f
&& _13
== 0.0f
&& _14
== 0.0f
&&
1278 _21
== 0.0f
&& _22
== 1.0f
&& _23
== 0.0f
&& _24
== 0.0f
&&
1279 _31
== 0.0f
&& _32
== 0.0f
&& _33
== 1.0f
&& _34
== 0.0f
&&
1280 _41
== 0.0f
&& _42
== 0.0f
&& _43
== 0.0f
&& _44
== 1.0f
;
1283 bool IsSingular() const { return Determinant() == 0.0; }
1285 T
Determinant() const {
1286 return _14
* _23
* _32
* _41
- _13
* _24
* _32
* _41
-
1287 _14
* _22
* _33
* _41
+ _12
* _24
* _33
* _41
+
1288 _13
* _22
* _34
* _41
- _12
* _23
* _34
* _41
-
1289 _14
* _23
* _31
* _42
+ _13
* _24
* _31
* _42
+
1290 _14
* _21
* _33
* _42
- _11
* _24
* _33
* _42
-
1291 _13
* _21
* _34
* _42
+ _11
* _23
* _34
* _42
+
1292 _14
* _22
* _31
* _43
- _12
* _24
* _31
* _43
-
1293 _14
* _21
* _32
* _43
+ _11
* _24
* _32
* _43
+
1294 _12
* _21
* _34
* _43
- _11
* _22
* _34
* _43
-
1295 _13
* _22
* _31
* _44
+ _12
* _23
* _31
* _44
+
1296 _13
* _21
* _32
* _44
- _11
* _23
* _32
* _44
-
1297 _12
* _21
* _33
* _44
+ _11
* _22
* _33
* _44
;
1300 // Invert() is not unit-correct. Prefer Inverse() where possible.
1302 T det
= Determinant();
1307 Matrix4x4Typed
<SourceUnits
, TargetUnits
, T
> result
;
1308 result
._11
= _23
* _34
* _42
- _24
* _33
* _42
+ _24
* _32
* _43
-
1309 _22
* _34
* _43
- _23
* _32
* _44
+ _22
* _33
* _44
;
1310 result
._12
= _14
* _33
* _42
- _13
* _34
* _42
- _14
* _32
* _43
+
1311 _12
* _34
* _43
+ _13
* _32
* _44
- _12
* _33
* _44
;
1312 result
._13
= _13
* _24
* _42
- _14
* _23
* _42
+ _14
* _22
* _43
-
1313 _12
* _24
* _43
- _13
* _22
* _44
+ _12
* _23
* _44
;
1314 result
._14
= _14
* _23
* _32
- _13
* _24
* _32
- _14
* _22
* _33
+
1315 _12
* _24
* _33
+ _13
* _22
* _34
- _12
* _23
* _34
;
1316 result
._21
= _24
* _33
* _41
- _23
* _34
* _41
- _24
* _31
* _43
+
1317 _21
* _34
* _43
+ _23
* _31
* _44
- _21
* _33
* _44
;
1318 result
._22
= _13
* _34
* _41
- _14
* _33
* _41
+ _14
* _31
* _43
-
1319 _11
* _34
* _43
- _13
* _31
* _44
+ _11
* _33
* _44
;
1320 result
._23
= _14
* _23
* _41
- _13
* _24
* _41
- _14
* _21
* _43
+
1321 _11
* _24
* _43
+ _13
* _21
* _44
- _11
* _23
* _44
;
1322 result
._24
= _13
* _24
* _31
- _14
* _23
* _31
+ _14
* _21
* _33
-
1323 _11
* _24
* _33
- _13
* _21
* _34
+ _11
* _23
* _34
;
1324 result
._31
= _22
* _34
* _41
- _24
* _32
* _41
+ _24
* _31
* _42
-
1325 _21
* _34
* _42
- _22
* _31
* _44
+ _21
* _32
* _44
;
1326 result
._32
= _14
* _32
* _41
- _12
* _34
* _41
- _14
* _31
* _42
+
1327 _11
* _34
* _42
+ _12
* _31
* _44
- _11
* _32
* _44
;
1328 result
._33
= _12
* _24
* _41
- _14
* _22
* _41
+ _14
* _21
* _42
-
1329 _11
* _24
* _42
- _12
* _21
* _44
+ _11
* _22
* _44
;
1330 result
._34
= _14
* _22
* _31
- _12
* _24
* _31
- _14
* _21
* _32
+
1331 _11
* _24
* _32
+ _12
* _21
* _34
- _11
* _22
* _34
;
1332 result
._41
= _23
* _32
* _41
- _22
* _33
* _41
- _23
* _31
* _42
+
1333 _21
* _33
* _42
+ _22
* _31
* _43
- _21
* _32
* _43
;
1334 result
._42
= _12
* _33
* _41
- _13
* _32
* _41
+ _13
* _31
* _42
-
1335 _11
* _33
* _42
- _12
* _31
* _43
+ _11
* _32
* _43
;
1336 result
._43
= _13
* _22
* _41
- _12
* _23
* _41
- _13
* _21
* _42
+
1337 _11
* _23
* _42
+ _12
* _21
* _43
- _11
* _22
* _43
;
1338 result
._44
= _12
* _23
* _31
- _13
* _22
* _31
+ _13
* _21
* _32
-
1339 _11
* _23
* _32
- _12
* _21
* _33
+ _11
* _22
* _33
;
1362 Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
> Inverse() const {
1363 typedef Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
> InvertedMatrix
;
1364 InvertedMatrix clone
= Cast
<InvertedMatrix
>();
1365 DebugOnly
<bool> inverted
= clone
.Invert();
1366 MOZ_ASSERT(inverted
,
1367 "Attempted to get the inverse of a non-invertible matrix");
1371 Maybe
<Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
>> MaybeInverse() const {
1372 typedef Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
> InvertedMatrix
;
1373 InvertedMatrix clone
= Cast
<InvertedMatrix
>();
1374 if (clone
.Invert()) {
1381 for (int i
= 0; i
< 4; i
++) {
1382 for (int j
= 0; j
< 4; j
++) {
1383 (*this)[i
][j
] /= (*this)[3][3];
1388 bool FuzzyEqual(const Matrix4x4Typed
& o
) const {
1389 return gfx::FuzzyEqual(_11
, o
._11
) && gfx::FuzzyEqual(_12
, o
._12
) &&
1390 gfx::FuzzyEqual(_13
, o
._13
) && gfx::FuzzyEqual(_14
, o
._14
) &&
1391 gfx::FuzzyEqual(_21
, o
._21
) && gfx::FuzzyEqual(_22
, o
._22
) &&
1392 gfx::FuzzyEqual(_23
, o
._23
) && gfx::FuzzyEqual(_24
, o
._24
) &&
1393 gfx::FuzzyEqual(_31
, o
._31
) && gfx::FuzzyEqual(_32
, o
._32
) &&
1394 gfx::FuzzyEqual(_33
, o
._33
) && gfx::FuzzyEqual(_34
, o
._34
) &&
1395 gfx::FuzzyEqual(_41
, o
._41
) && gfx::FuzzyEqual(_42
, o
._42
) &&
1396 gfx::FuzzyEqual(_43
, o
._43
) && gfx::FuzzyEqual(_44
, o
._44
);
1399 bool FuzzyEqualsMultiplicative(const Matrix4x4Typed
& o
) const {
1400 return ::mozilla::FuzzyEqualsMultiplicative(_11
, o
._11
) &&
1401 ::mozilla::FuzzyEqualsMultiplicative(_12
, o
._12
) &&
1402 ::mozilla::FuzzyEqualsMultiplicative(_13
, o
._13
) &&
1403 ::mozilla::FuzzyEqualsMultiplicative(_14
, o
._14
) &&
1404 ::mozilla::FuzzyEqualsMultiplicative(_21
, o
._21
) &&
1405 ::mozilla::FuzzyEqualsMultiplicative(_22
, o
._22
) &&
1406 ::mozilla::FuzzyEqualsMultiplicative(_23
, o
._23
) &&
1407 ::mozilla::FuzzyEqualsMultiplicative(_24
, o
._24
) &&
1408 ::mozilla::FuzzyEqualsMultiplicative(_31
, o
._31
) &&
1409 ::mozilla::FuzzyEqualsMultiplicative(_32
, o
._32
) &&
1410 ::mozilla::FuzzyEqualsMultiplicative(_33
, o
._33
) &&
1411 ::mozilla::FuzzyEqualsMultiplicative(_34
, o
._34
) &&
1412 ::mozilla::FuzzyEqualsMultiplicative(_41
, o
._41
) &&
1413 ::mozilla::FuzzyEqualsMultiplicative(_42
, o
._42
) &&
1414 ::mozilla::FuzzyEqualsMultiplicative(_43
, o
._43
) &&
1415 ::mozilla::FuzzyEqualsMultiplicative(_44
, o
._44
);
1418 bool IsBackfaceVisible() const {
1419 // Inverse()._33 < 0;
1420 T det
= Determinant();
1421 T __33
= _12
* _24
* _41
- _14
* _22
* _41
+ _14
* _21
* _42
-
1422 _11
* _24
* _42
- _12
* _21
* _44
+ _11
* _22
* _44
;
1423 return (__33
* det
) < 0;
1426 Matrix4x4Typed
& NudgeToIntegersFixedEpsilon() {
1427 NudgeToInteger(&_11
);
1428 NudgeToInteger(&_12
);
1429 NudgeToInteger(&_13
);
1430 NudgeToInteger(&_14
);
1431 NudgeToInteger(&_21
);
1432 NudgeToInteger(&_22
);
1433 NudgeToInteger(&_23
);
1434 NudgeToInteger(&_24
);
1435 NudgeToInteger(&_31
);
1436 NudgeToInteger(&_32
);
1437 NudgeToInteger(&_33
);
1438 NudgeToInteger(&_34
);
1439 static const float error
= 1e-5f
;
1440 NudgeToInteger(&_41
, error
);
1441 NudgeToInteger(&_42
, error
);
1442 NudgeToInteger(&_43
, error
);
1443 NudgeToInteger(&_44
, error
);
1447 Point4D
TransposedVector(int aIndex
) const {
1448 MOZ_ASSERT(aIndex
>= 0 && aIndex
<= 3, "Invalid matrix array index");
1449 return Point4DTyped
<UnknownUnits
, T
>(*((&_11
) + aIndex
), *((&_21
) + aIndex
),
1451 *((&_41
) + aIndex
));
1454 void SetTransposedVector(int aIndex
, Point4DTyped
<UnknownUnits
, T
>& aVector
) {
1455 MOZ_ASSERT(aIndex
>= 0 && aIndex
<= 3, "Invalid matrix array index");
1456 *((&_11
) + aIndex
) = aVector
.x
;
1457 *((&_21
) + aIndex
) = aVector
.y
;
1458 *((&_31
) + aIndex
) = aVector
.z
;
1459 *((&_41
) + aIndex
) = aVector
.w
;
1462 bool Decompose(Point3DTyped
<UnknownUnits
, T
>& translation
,
1463 BaseQuaternion
<T
>& rotation
,
1464 Point3DTyped
<UnknownUnits
, T
>& scale
) const {
1465 // Ensure matrix can be normalized
1466 if (gfx::FuzzyEqual(_44
, 0.0f
)) {
1469 Matrix4x4Typed mat
= *this;
1471 if (HasPerspectiveComponent()) {
1472 // We do not support projection matrices
1476 // Extract translation
1477 translation
.x
= mat
._41
;
1478 translation
.y
= mat
._42
;
1479 translation
.z
= mat
._43
;
1481 // Remove translation
1487 scale
.x
= sqrtf(_11
* _11
+ _21
* _21
+ _31
* _31
);
1488 scale
.y
= sqrtf(_12
* _12
+ _22
* _22
+ _32
* _32
);
1489 scale
.z
= sqrtf(_13
* _13
+ _23
* _23
+ _33
* _33
);
1492 if (gfx::FuzzyEqual(scale
.x
, 0.0f
) || gfx::FuzzyEqual(scale
.y
, 0.0f
) ||
1493 gfx::FuzzyEqual(scale
.z
, 0.0f
)) {
1494 // We do not support matrices with a zero scale component
1499 rotation
.SetFromRotationMatrix(this->ToUnknownMatrix());
1503 // Sets this matrix to a rotation matrix given by aQuat.
1504 // This quaternion *MUST* be normalized!
1505 // Implemented in Quaternion.cpp
1506 void SetRotationFromQuaternion(const BaseQuaternion
<T
>& q
) {
1507 const T x2
= q
.x
+ q
.x
, y2
= q
.y
+ q
.y
, z2
= q
.z
+ q
.z
;
1508 const T xx
= q
.x
* x2
, xy
= q
.x
* y2
, xz
= q
.x
* z2
;
1509 const T yy
= q
.y
* y2
, yz
= q
.y
* z2
, zz
= q
.z
* z2
;
1510 const T wx
= q
.w
* x2
, wy
= q
.w
* y2
, wz
= q
.w
* z2
;
1512 _11
= 1.0f
- (yy
+ zz
);
1518 _22
= 1.0f
- (xx
+ zz
);
1524 _33
= 1.0f
- (xx
+ yy
);
1527 _14
= _42
= _43
= 0.0f
;
1531 // Set all the members of the matrix to NaN
1533 _11
= UnspecifiedNaN
<T
>();
1534 _21
= UnspecifiedNaN
<T
>();
1535 _31
= UnspecifiedNaN
<T
>();
1536 _41
= UnspecifiedNaN
<T
>();
1537 _12
= UnspecifiedNaN
<T
>();
1538 _22
= UnspecifiedNaN
<T
>();
1539 _32
= UnspecifiedNaN
<T
>();
1540 _42
= UnspecifiedNaN
<T
>();
1541 _13
= UnspecifiedNaN
<T
>();
1542 _23
= UnspecifiedNaN
<T
>();
1543 _33
= UnspecifiedNaN
<T
>();
1544 _43
= UnspecifiedNaN
<T
>();
1545 _14
= UnspecifiedNaN
<T
>();
1546 _24
= UnspecifiedNaN
<T
>();
1547 _34
= UnspecifiedNaN
<T
>();
1548 _44
= UnspecifiedNaN
<T
>();
1551 // Verifies that the matrix contains no Infs or NaNs
1552 bool IsFinite() const {
1553 return std::isfinite(_11
) && std::isfinite(_12
) && std::isfinite(_13
) &&
1554 std::isfinite(_14
) && std::isfinite(_21
) && std::isfinite(_22
) &&
1555 std::isfinite(_23
) && std::isfinite(_24
) && std::isfinite(_31
) &&
1556 std::isfinite(_32
) && std::isfinite(_33
) && std::isfinite(_34
) &&
1557 std::isfinite(_41
) && std::isfinite(_42
) && std::isfinite(_43
) &&
1561 void SkewXY(double aXSkew
, double aYSkew
) {
1562 // XXX Is double precision really necessary here
1563 T tanX
= SafeTangent(aXSkew
);
1564 T tanY
= SafeTangent(aYSkew
);
1584 void RotateX(double aTheta
) {
1585 // XXX Is double precision really necessary here
1586 double cosTheta
= FlushToZero(cos(aTheta
));
1587 double sinTheta
= FlushToZero(sin(aTheta
));
1592 _21
= cosTheta
* _21
+ sinTheta
* _31
;
1593 _31
= -sinTheta
* temp
+ cosTheta
* _31
;
1596 _22
= cosTheta
* _22
+ sinTheta
* _32
;
1597 _32
= -sinTheta
* temp
+ cosTheta
* _32
;
1600 _23
= cosTheta
* _23
+ sinTheta
* _33
;
1601 _33
= -sinTheta
* temp
+ cosTheta
* _33
;
1604 _24
= cosTheta
* _24
+ sinTheta
* _34
;
1605 _34
= -sinTheta
* temp
+ cosTheta
* _34
;
1608 void RotateY(double aTheta
) {
1609 // XXX Is double precision really necessary here
1610 double cosTheta
= FlushToZero(cos(aTheta
));
1611 double sinTheta
= FlushToZero(sin(aTheta
));
1616 _11
= cosTheta
* _11
+ -sinTheta
* _31
;
1617 _31
= sinTheta
* temp
+ cosTheta
* _31
;
1620 _12
= cosTheta
* _12
+ -sinTheta
* _32
;
1621 _32
= sinTheta
* temp
+ cosTheta
* _32
;
1624 _13
= cosTheta
* _13
+ -sinTheta
* _33
;
1625 _33
= sinTheta
* temp
+ cosTheta
* _33
;
1628 _14
= cosTheta
* _14
+ -sinTheta
* _34
;
1629 _34
= sinTheta
* temp
+ cosTheta
* _34
;
1632 void RotateZ(double aTheta
) {
1633 // XXX Is double precision really necessary here
1634 double cosTheta
= FlushToZero(cos(aTheta
));
1635 double sinTheta
= FlushToZero(sin(aTheta
));
1640 _11
= cosTheta
* _11
+ sinTheta
* _21
;
1641 _21
= -sinTheta
* temp
+ cosTheta
* _21
;
1644 _12
= cosTheta
* _12
+ sinTheta
* _22
;
1645 _22
= -sinTheta
* temp
+ cosTheta
* _22
;
1648 _13
= cosTheta
* _13
+ sinTheta
* _23
;
1649 _23
= -sinTheta
* temp
+ cosTheta
* _23
;
1652 _14
= cosTheta
* _14
+ sinTheta
* _24
;
1653 _24
= -sinTheta
* temp
+ cosTheta
* _24
;
1656 // Sets this matrix to a rotation matrix about a
1657 // vector [x,y,z] by angle theta. The vector is normalized
1658 // to a unit vector.
1659 // https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
1660 void SetRotateAxisAngle(double aX
, double aY
, double aZ
, double aTheta
) {
1661 Point3DTyped
<UnknownUnits
, T
> vector(aX
, aY
, aZ
);
1662 if (!vector
.Length()) {
1665 vector
.RobustNormalize();
1667 double x
= vector
.x
;
1668 double y
= vector
.y
;
1669 double z
= vector
.z
;
1671 double cosTheta
= FlushToZero(cos(aTheta
));
1672 double sinTheta
= FlushToZero(sin(aTheta
));
1674 // sin(aTheta / 2) * cos(aTheta / 2)
1675 double sc
= sinTheta
/ 2;
1676 // pow(sin(aTheta / 2), 2)
1677 double sq
= (1 - cosTheta
) / 2;
1679 _11
= 1 - 2 * (y
* y
+ z
* z
) * sq
;
1680 _12
= 2 * (x
* y
* sq
+ z
* sc
);
1681 _13
= 2 * (x
* z
* sq
- y
* sc
);
1683 _21
= 2 * (x
* y
* sq
- z
* sc
);
1684 _22
= 1 - 2 * (x
* x
+ z
* z
) * sq
;
1685 _23
= 2 * (y
* z
* sq
+ x
* sc
);
1687 _31
= 2 * (x
* z
* sq
+ y
* sc
);
1688 _32
= 2 * (y
* z
* sq
- x
* sc
);
1689 _33
= 1 - 2 * (x
* x
+ y
* y
) * sq
;
1697 void Perspective(T aDepth
) {
1698 MOZ_ASSERT(aDepth
> 0.0f
, "Perspective must be positive!");
1699 _31
+= -1.0 / aDepth
* _41
;
1700 _32
+= -1.0 / aDepth
* _42
;
1701 _33
+= -1.0 / aDepth
* _43
;
1702 _34
+= -1.0 / aDepth
* _44
;
1705 Point3D
GetNormalVector() const {
1706 // Define a plane in transformed space as the transformations
1707 // of 3 points on the z=0 screen plane.
1708 Point3DTyped
<UnknownUnits
, T
> a
=
1709 TransformPoint(Point3DTyped
<UnknownUnits
, T
>(0, 0, 0));
1710 Point3DTyped
<UnknownUnits
, T
> b
=
1711 TransformPoint(Point3DTyped
<UnknownUnits
, T
>(0, 1, 0));
1712 Point3DTyped
<UnknownUnits
, T
> c
=
1713 TransformPoint(Point3DTyped
<UnknownUnits
, T
>(1, 0, 0));
1715 // Convert to two vectors on the surface of the plane.
1716 Point3DTyped
<UnknownUnits
, T
> ab
= b
- a
;
1717 Point3DTyped
<UnknownUnits
, T
> ac
= c
- a
;
1719 return ac
.CrossProduct(ab
);
1723 * Returns true if the matrix has any transform other
1724 * than a straight translation.
1726 bool HasNonTranslation() const {
1727 return !gfx::FuzzyEqual(_11
, 1.0) || !gfx::FuzzyEqual(_22
, 1.0) ||
1728 !gfx::FuzzyEqual(_12
, 0.0) || !gfx::FuzzyEqual(_21
, 0.0) ||
1729 !gfx::FuzzyEqual(_13
, 0.0) || !gfx::FuzzyEqual(_23
, 0.0) ||
1730 !gfx::FuzzyEqual(_31
, 0.0) || !gfx::FuzzyEqual(_32
, 0.0) ||
1731 !gfx::FuzzyEqual(_33
, 1.0);
1735 * Returns true if the matrix is anything other than a straight
1736 * translation by integers.
1738 bool HasNonIntegerTranslation() const {
1739 return HasNonTranslation() || !gfx::FuzzyEqual(_41
, floor(_41
+ 0.5)) ||
1740 !gfx::FuzzyEqual(_42
, floor(_42
+ 0.5)) ||
1741 !gfx::FuzzyEqual(_43
, floor(_43
+ 0.5));
1745 * Return true if the matrix is with perspective (w).
1747 bool HasPerspectiveComponent() const {
1748 return _14
!= 0 || _24
!= 0 || _34
!= 0 || _44
!= 1;
1751 /* Returns true if the matrix is a rectilinear transformation (i.e.
1752 * grid-aligned rectangles are transformed to grid-aligned rectangles).
1753 * This should only be called on 2D matrices.
1755 bool IsRectilinear() const {
1757 if (gfx::FuzzyEqual(_12
, 0) && gfx::FuzzyEqual(_21
, 0)) {
1759 } else if (gfx::FuzzyEqual(_22
, 0) && gfx::FuzzyEqual(_11
, 0)) {
1766 * Convert between typed and untyped matrices.
1768 using UnknownMatrix
= Matrix4x4Typed
<UnknownUnits
, UnknownUnits
, T
>;
1769 UnknownMatrix
ToUnknownMatrix() const {
1770 return UnknownMatrix
{_11
, _12
, _13
, _14
, _21
, _22
, _23
, _24
,
1771 _31
, _32
, _33
, _34
, _41
, _42
, _43
, _44
};
1773 static Matrix4x4Typed
FromUnknownMatrix(const UnknownMatrix
& aUnknown
) {
1774 return Matrix4x4Typed
{
1775 aUnknown
._11
, aUnknown
._12
, aUnknown
._13
, aUnknown
._14
,
1776 aUnknown
._21
, aUnknown
._22
, aUnknown
._23
, aUnknown
._24
,
1777 aUnknown
._31
, aUnknown
._32
, aUnknown
._33
, aUnknown
._34
,
1778 aUnknown
._41
, aUnknown
._42
, aUnknown
._43
, aUnknown
._44
};
1781 * For convenience, overload FromUnknownMatrix() for Maybe<Matrix>.
1783 static Maybe
<Matrix4x4Typed
> FromUnknownMatrix(
1784 const Maybe
<UnknownMatrix
>& aUnknown
) {
1785 if (aUnknown
.isSome()) {
1786 return Some(FromUnknownMatrix(*aUnknown
));
1792 typedef Matrix4x4Typed
<UnknownUnits
, UnknownUnits
> Matrix4x4
;
1793 typedef Matrix4x4Typed
<UnknownUnits
, UnknownUnits
, double> Matrix4x4Double
;
1818 Matrix5x4(Float a11
, Float a12
, Float a13
, Float a14
, Float a21
, Float a22
,
1819 Float a23
, Float a24
, Float a31
, Float a32
, Float a33
, Float a34
,
1820 Float a41
, Float a42
, Float a43
, Float a44
, Float a51
, Float a52
,
1821 Float a53
, Float a54
)
1843 bool operator==(const Matrix5x4
& o
) const {
1844 return _11
== o
._11
&& _12
== o
._12
&& _13
== o
._13
&& _14
== o
._14
&&
1845 _21
== o
._21
&& _22
== o
._22
&& _23
== o
._23
&& _24
== o
._24
&&
1846 _31
== o
._31
&& _32
== o
._32
&& _33
== o
._33
&& _34
== o
._34
&&
1847 _41
== o
._41
&& _42
== o
._42
&& _43
== o
._43
&& _44
== o
._44
&&
1848 _51
== o
._51
&& _52
== o
._52
&& _53
== o
._53
&& _54
== o
._54
;
1851 bool operator!=(const Matrix5x4
& aMatrix
) const {
1852 return !(*this == aMatrix
);
1855 Matrix5x4
operator*(const Matrix5x4
& aMatrix
) const {
1856 Matrix5x4 resultMatrix
;
1858 resultMatrix
._11
= this->_11
* aMatrix
._11
+ this->_12
* aMatrix
._21
+
1859 this->_13
* aMatrix
._31
+ this->_14
* aMatrix
._41
;
1860 resultMatrix
._12
= this->_11
* aMatrix
._12
+ this->_12
* aMatrix
._22
+
1861 this->_13
* aMatrix
._32
+ this->_14
* aMatrix
._42
;
1862 resultMatrix
._13
= this->_11
* aMatrix
._13
+ this->_12
* aMatrix
._23
+
1863 this->_13
* aMatrix
._33
+ this->_14
* aMatrix
._43
;
1864 resultMatrix
._14
= this->_11
* aMatrix
._14
+ this->_12
* aMatrix
._24
+
1865 this->_13
* aMatrix
._34
+ this->_14
* aMatrix
._44
;
1866 resultMatrix
._21
= this->_21
* aMatrix
._11
+ this->_22
* aMatrix
._21
+
1867 this->_23
* aMatrix
._31
+ this->_24
* aMatrix
._41
;
1868 resultMatrix
._22
= this->_21
* aMatrix
._12
+ this->_22
* aMatrix
._22
+
1869 this->_23
* aMatrix
._32
+ this->_24
* aMatrix
._42
;
1870 resultMatrix
._23
= this->_21
* aMatrix
._13
+ this->_22
* aMatrix
._23
+
1871 this->_23
* aMatrix
._33
+ this->_24
* aMatrix
._43
;
1872 resultMatrix
._24
= this->_21
* aMatrix
._14
+ this->_22
* aMatrix
._24
+
1873 this->_23
* aMatrix
._34
+ this->_24
* aMatrix
._44
;
1874 resultMatrix
._31
= this->_31
* aMatrix
._11
+ this->_32
* aMatrix
._21
+
1875 this->_33
* aMatrix
._31
+ this->_34
* aMatrix
._41
;
1876 resultMatrix
._32
= this->_31
* aMatrix
._12
+ this->_32
* aMatrix
._22
+
1877 this->_33
* aMatrix
._32
+ this->_34
* aMatrix
._42
;
1878 resultMatrix
._33
= this->_31
* aMatrix
._13
+ this->_32
* aMatrix
._23
+
1879 this->_33
* aMatrix
._33
+ this->_34
* aMatrix
._43
;
1880 resultMatrix
._34
= this->_31
* aMatrix
._14
+ this->_32
* aMatrix
._24
+
1881 this->_33
* aMatrix
._34
+ this->_34
* aMatrix
._44
;
1882 resultMatrix
._41
= this->_41
* aMatrix
._11
+ this->_42
* aMatrix
._21
+
1883 this->_43
* aMatrix
._31
+ this->_44
* aMatrix
._41
;
1884 resultMatrix
._42
= this->_41
* aMatrix
._12
+ this->_42
* aMatrix
._22
+
1885 this->_43
* aMatrix
._32
+ this->_44
* aMatrix
._42
;
1886 resultMatrix
._43
= this->_41
* aMatrix
._13
+ this->_42
* aMatrix
._23
+
1887 this->_43
* aMatrix
._33
+ this->_44
* aMatrix
._43
;
1888 resultMatrix
._44
= this->_41
* aMatrix
._14
+ this->_42
* aMatrix
._24
+
1889 this->_43
* aMatrix
._34
+ this->_44
* aMatrix
._44
;
1890 resultMatrix
._51
= this->_51
* aMatrix
._11
+ this->_52
* aMatrix
._21
+
1891 this->_53
* aMatrix
._31
+ this->_54
* aMatrix
._41
+
1893 resultMatrix
._52
= this->_51
* aMatrix
._12
+ this->_52
* aMatrix
._22
+
1894 this->_53
* aMatrix
._32
+ this->_54
* aMatrix
._42
+
1896 resultMatrix
._53
= this->_51
* aMatrix
._13
+ this->_52
* aMatrix
._23
+
1897 this->_53
* aMatrix
._33
+ this->_54
* aMatrix
._43
+
1899 resultMatrix
._54
= this->_51
* aMatrix
._14
+ this->_52
* aMatrix
._24
+
1900 this->_53
* aMatrix
._34
+ this->_54
* aMatrix
._44
+
1903 return resultMatrix
;
1906 Matrix5x4
& operator*=(const Matrix5x4
& aMatrix
) {
1907 *this = *this * aMatrix
;
1911 friend std::ostream
& operator<<(std::ostream
& aStream
,
1912 const Matrix5x4
& aMatrix
) {
1913 const Float
* f
= &aMatrix
._11
;
1914 aStream
<< "[ " << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1916 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1918 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1920 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1922 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3]
1929 Float _11
, _12
, _13
, _14
;
1930 Float _21
, _22
, _23
, _24
;
1931 Float _31
, _32
, _33
, _34
;
1932 Float _41
, _42
, _43
, _44
;
1933 Float _51
, _52
, _53
, _54
;
1935 Float components
[20];
1939 /* This Matrix class will carry one additional type field in order to
1940 * track what type of 4x4 matrix we're dealing with, it can then execute
1941 * simplified versions of certain operations when applicable.
1942 * This does not allow access to the parent class directly, as a caller
1943 * could then mutate the parent class without updating the type.
1945 template <typename SourceUnits
, typename TargetUnits
>
1946 class Matrix4x4TypedFlagged
1947 : protected Matrix4x4Typed
<SourceUnits
, TargetUnits
> {
1949 using Parent
= Matrix4x4Typed
<SourceUnits
, TargetUnits
>;
1950 using TargetPoint
= PointTyped
<TargetUnits
>;
1968 Matrix4x4TypedFlagged() : mType(MatrixType::Identity
) {}
1970 Matrix4x4TypedFlagged(Float a11
, Float a12
, Float a13
, Float a14
, Float a21
,
1971 Float a22
, Float a23
, Float a24
, Float a31
, Float a32
,
1972 Float a33
, Float a34
, Float a41
, Float a42
, Float a43
,
1974 : Parent(a11
, a12
, a13
, a14
, a21
, a22
, a23
, a24
, a31
, a32
, a33
, a34
, a41
,
1979 MOZ_IMPLICIT
Matrix4x4TypedFlagged(const Parent
& aOther
) : Parent(aOther
) {
1983 template <typename NewMatrix4x4TypedFlagged
>
1984 [[nodiscard
]] NewMatrix4x4TypedFlagged
Cast() const {
1985 return NewMatrix4x4TypedFlagged(_11
, _12
, _13
, _14
, _21
, _22
, _23
, _24
, _31
,
1986 _32
, _33
, _34
, _41
, _42
, _43
, _44
, mType
);
1990 PointTyped
<TargetUnits
, F
> TransformPoint(
1991 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
1992 if (mType
== MatrixType::Identity
) {
1996 if (mType
== MatrixType::Simple
) {
1997 return TransformPointSimple(aPoint
);
2000 return Parent::TransformPoint(aPoint
);
2004 RectTyped
<TargetUnits
, F
> TransformAndClipBounds(
2005 const RectTyped
<SourceUnits
, F
>& aRect
,
2006 const RectTyped
<TargetUnits
, F
>& aClip
) const {
2007 if (mType
== MatrixType::Identity
) {
2008 const RectTyped
<SourceUnits
, F
>& clipped
= aRect
.Intersect(aClip
);
2009 return RectTyped
<TargetUnits
, F
>(clipped
.X(), clipped
.Y(),
2010 clipped
.Width(), clipped
.Height());
2013 if (mType
== MatrixType::Simple
) {
2014 PointTyped
<UnknownUnits
, F
> p1
= TransformPointSimple(aRect
.TopLeft());
2015 PointTyped
<UnknownUnits
, F
> p2
= TransformPointSimple(aRect
.TopRight());
2016 PointTyped
<UnknownUnits
, F
> p3
= TransformPointSimple(aRect
.BottomLeft());
2017 PointTyped
<UnknownUnits
, F
> p4
=
2018 TransformPointSimple(aRect
.BottomRight());
2020 F min_x
= std::min(std::min(std::min(p1
.x
, p2
.x
), p3
.x
), p4
.x
);
2021 F max_x
= std::max(std::max(std::max(p1
.x
, p2
.x
), p3
.x
), p4
.x
);
2022 F min_y
= std::min(std::min(std::min(p1
.y
, p2
.y
), p3
.y
), p4
.y
);
2023 F max_y
= std::max(std::max(std::max(p1
.y
, p2
.y
), p3
.y
), p4
.y
);
2025 TargetPoint
topLeft(std::max(min_x
, aClip
.x
), std::max(min_y
, aClip
.y
));
2026 F width
= std::min(max_x
, aClip
.XMost()) - topLeft
.x
;
2027 F height
= std::min(max_y
, aClip
.YMost()) - topLeft
.y
;
2029 return RectTyped
<TargetUnits
, F
>(topLeft
.x
, topLeft
.y
, width
, height
);
2031 return Parent::TransformAndClipBounds(aRect
, aClip
);
2034 bool FuzzyEqual(const Parent
& o
) const { return Parent::FuzzyEqual(o
); }
2036 bool FuzzyEqual(const Matrix4x4TypedFlagged
& o
) const {
2037 if (mType
== MatrixType::Identity
&& o
.mType
== MatrixType::Identity
) {
2040 return Parent::FuzzyEqual(o
);
2043 Matrix4x4TypedFlagged
& PreTranslate(Float aX
, Float aY
, Float aZ
) {
2044 if (mType
== MatrixType::Identity
) {
2050 mType
= MatrixType::Simple
;
2053 mType
= MatrixType::Full
;
2057 Parent::PreTranslate(aX
, aY
, aZ
);
2060 mType
= MatrixType::Full
;
2066 Matrix4x4TypedFlagged
& PostTranslate(Float aX
, Float aY
, Float aZ
) {
2067 if (mType
== MatrixType::Identity
) {
2073 mType
= MatrixType::Simple
;
2076 mType
= MatrixType::Full
;
2080 Parent::PostTranslate(aX
, aY
, aZ
);
2083 mType
= MatrixType::Full
;
2089 Matrix4x4TypedFlagged
& ChangeBasis(Float aX
, Float aY
, Float aZ
) {
2090 // Translate to the origin before applying this matrix
2091 PreTranslate(-aX
, -aY
, -aZ
);
2093 // Translate back into position after applying this matrix
2094 PostTranslate(aX
, aY
, aZ
);
2099 bool IsIdentity() const { return mType
== MatrixType::Identity
; }
2102 Point4DTyped
<TargetUnits
, F
> ProjectPoint(
2103 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
2104 if (mType
== MatrixType::Identity
) {
2105 return Point4DTyped
<TargetUnits
, F
>(aPoint
.x
, aPoint
.y
, 0, 1);
2108 if (mType
== MatrixType::Simple
) {
2109 TargetPoint point
= TransformPointSimple(aPoint
);
2110 return Point4DTyped
<TargetUnits
, F
>(point
.x
, point
.y
, 0, 1);
2113 return Parent::ProjectPoint(aPoint
);
2116 Matrix4x4TypedFlagged
& ProjectTo2D() {
2117 if (mType
== MatrixType::Full
) {
2118 Parent::ProjectTo2D();
2123 bool IsSingular() const {
2124 if (mType
== MatrixType::Identity
) {
2127 return Parent::Determinant() == 0.0;
2131 if (mType
== MatrixType::Identity
) {
2135 return Parent::Invert();
2138 Matrix4x4TypedFlagged
<TargetUnits
, SourceUnits
> Inverse() const {
2139 typedef Matrix4x4TypedFlagged
<TargetUnits
, SourceUnits
> InvertedMatrix
;
2140 InvertedMatrix clone
= Cast
<InvertedMatrix
>();
2141 if (mType
== MatrixType::Identity
) {
2144 DebugOnly
<bool> inverted
= clone
.Invert();
2145 MOZ_ASSERT(inverted
,
2146 "Attempted to get the inverse of a non-invertible matrix");
2148 // Inverting a 2D Matrix should result in a 2D matrix, ergo mType doesn't
2153 template <typename NewTargetUnits
>
2155 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2156 if (mType
== MatrixType::Identity
&&
2157 aMatrix
.mType
== MatrixType::Identity
) {
2160 // Depending on the usage it may make sense to compare more flags.
2161 return Parent::operator==(aMatrix
);
2164 template <typename NewTargetUnits
>
2166 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2167 if (mType
== MatrixType::Identity
&&
2168 aMatrix
.mType
== MatrixType::Identity
) {
2171 // Depending on the usage it may make sense to compare more flags.
2172 return Parent::operator!=(aMatrix
);
2175 template <typename NewTargetUnits
>
2176 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> operator*(
2177 const Matrix4x4Typed
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2178 if (mType
== MatrixType::Identity
) {
2182 if (mType
== MatrixType::Simple
) {
2183 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> matrix
;
2184 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
;
2185 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
;
2186 matrix
._31
= aMatrix
._31
;
2187 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ aMatrix
._41
;
2188 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
;
2189 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
;
2190 matrix
._32
= aMatrix
._32
;
2191 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ aMatrix
._42
;
2192 matrix
._13
= _11
* aMatrix
._13
+ _12
* aMatrix
._23
;
2193 matrix
._23
= _21
* aMatrix
._13
+ _22
* aMatrix
._23
;
2194 matrix
._33
= aMatrix
._33
;
2195 matrix
._43
= _41
* aMatrix
._13
+ _42
* aMatrix
._23
+ aMatrix
._43
;
2196 matrix
._14
= _11
* aMatrix
._14
+ _12
* aMatrix
._24
;
2197 matrix
._24
= _21
* aMatrix
._14
+ _22
* aMatrix
._24
;
2198 matrix
._34
= aMatrix
._34
;
2199 matrix
._44
= _41
* aMatrix
._14
+ _42
* aMatrix
._24
+ aMatrix
._44
;
2204 return Parent::operator*(aMatrix
);
2207 template <typename NewTargetUnits
>
2208 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> operator*(
2209 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2210 if (mType
== MatrixType::Identity
) {
2214 if (aMatrix
.mType
== MatrixType::Identity
) {
2215 return Cast
<Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
>>();
2218 if (mType
== MatrixType::Simple
&& aMatrix
.mType
== MatrixType::Simple
) {
2219 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> matrix
;
2220 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
;
2221 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
;
2222 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ aMatrix
._41
;
2223 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
;
2224 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
;
2225 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ aMatrix
._42
;
2226 matrix
.mType
= MatrixType::Simple
;
2228 } else if (mType
== MatrixType::Simple
) {
2229 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> matrix
;
2230 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
;
2231 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
;
2232 matrix
._31
= aMatrix
._31
;
2233 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ aMatrix
._41
;
2234 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
;
2235 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
;
2236 matrix
._32
= aMatrix
._32
;
2237 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ aMatrix
._42
;
2238 matrix
._13
= _11
* aMatrix
._13
+ _12
* aMatrix
._23
;
2239 matrix
._23
= _21
* aMatrix
._13
+ _22
* aMatrix
._23
;
2240 matrix
._33
= aMatrix
._33
;
2241 matrix
._43
= _41
* aMatrix
._13
+ _42
* aMatrix
._23
+ aMatrix
._43
;
2242 matrix
._14
= _11
* aMatrix
._14
+ _12
* aMatrix
._24
;
2243 matrix
._24
= _21
* aMatrix
._14
+ _22
* aMatrix
._24
;
2244 matrix
._34
= aMatrix
._34
;
2245 matrix
._44
= _41
* aMatrix
._14
+ _42
* aMatrix
._24
+ aMatrix
._44
;
2246 matrix
.mType
= MatrixType::Full
;
2248 } else if (aMatrix
.mType
== MatrixType::Simple
) {
2249 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> matrix
;
2250 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
+ _14
* aMatrix
._41
;
2251 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
+ _24
* aMatrix
._41
;
2252 matrix
._31
= _31
* aMatrix
._11
+ _32
* aMatrix
._21
+ _34
* aMatrix
._41
;
2253 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ _44
* aMatrix
._41
;
2254 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
+ _14
* aMatrix
._42
;
2255 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
+ _24
* aMatrix
._42
;
2256 matrix
._32
= _31
* aMatrix
._12
+ _32
* aMatrix
._22
+ _34
* aMatrix
._42
;
2257 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ _44
* aMatrix
._42
;
2266 matrix
.mType
= MatrixType::Full
;
2270 return Parent::operator*(aMatrix
);
2273 bool Is2D() const { return mType
!= MatrixType::Full
; }
2275 bool CanDraw2D(Matrix
* aMatrix
= nullptr) const {
2276 if (mType
!= MatrixType::Full
) {
2287 return Parent::CanDraw2D(aMatrix
);
2290 bool Is2D(Matrix
* aMatrix
) const {
2306 RectTyped
<TargetUnits
, F
> ProjectRectBounds(
2307 const RectTyped
<SourceUnits
, F
>& aRect
,
2308 const RectTyped
<TargetUnits
, F
>& aClip
) const {
2309 return Parent::ProjectRectBounds(aRect
, aClip
);
2312 const Parent
& GetMatrix() const { return *this; }
2315 enum class MatrixType
: uint8_t {
2317 Simple
, // 2x3 Matrix
2321 Matrix4x4TypedFlagged(Float a11
, Float a12
, Float a13
, Float a14
, Float a21
,
2322 Float a22
, Float a23
, Float a24
, Float a31
, Float a32
,
2323 Float a33
, Float a34
, Float a41
, Float a42
, Float a43
,
2325 typename
Matrix4x4TypedFlagged::MatrixType aType
)
2326 : Parent(a11
, a12
, a13
, a14
, a21
, a22
, a23
, a24
, a31
, a32
, a33
, a34
, a41
,
2330 static Matrix4x4TypedFlagged
FromUnknownMatrix(
2331 const Matrix4x4Flagged
& aUnknown
) {
2332 return Matrix4x4TypedFlagged
{
2333 aUnknown
._11
, aUnknown
._12
, aUnknown
._13
, aUnknown
._14
, aUnknown
._21
,
2334 aUnknown
._22
, aUnknown
._23
, aUnknown
._24
, aUnknown
._31
, aUnknown
._32
,
2335 aUnknown
._33
, aUnknown
._34
, aUnknown
._41
, aUnknown
._42
, aUnknown
._43
,
2336 aUnknown
._44
, aUnknown
.mType
};
2338 Matrix4x4Flagged
ToUnknownMatrix() const {
2339 return Matrix4x4Flagged
{_11
, _12
, _13
, _14
, _21
, _22
, _23
, _24
, _31
,
2340 _32
, _33
, _34
, _41
, _42
, _43
, _44
, mType
};
2344 PointTyped
<TargetUnits
, F
> TransformPointSimple(
2345 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
2346 PointTyped
<SourceUnits
, F
> temp
;
2347 temp
.x
= aPoint
.x
* _11
+ aPoint
.y
* +_21
+ _41
;
2348 temp
.y
= aPoint
.x
* _12
+ aPoint
.y
* +_22
+ _42
;
2353 if (Parent::IsIdentity()) {
2354 mType
= MatrixType::Identity
;
2358 if (Parent::Is2D()) {
2359 mType
= MatrixType::Simple
;
2363 mType
= MatrixType::Full
;
2369 using Matrix4x4Flagged
= Matrix4x4TypedFlagged
<UnknownUnits
, UnknownUnits
>;
2372 } // namespace mozilla
2374 #endif /* MOZILLA_GFX_MATRIX_H_ */