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 // If you change this also change Matrix4x4TypedFlagged::TransformBounds to
993 PointTyped
<TargetUnits
, F
> quad
[4];
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
) {
1009 if (quad
[i
].x
> max_x
) {
1013 if (quad
[i
].y
< min_y
) {
1016 if (quad
[i
].y
> max_y
) {
1021 return RectTyped
<TargetUnits
, F
>(min_x
, min_y
, max_x
- min_x
,
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
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
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
;
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
1082 Matrix4x4Typed
& PostTranslate(T aX
, T aY
, T aZ
) {
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
) {
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);
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);
1149 * Similar to PostTranslate, but applies a scale instead of a translation.
1151 Matrix4x4Typed
& PostScale(T aScaleX
, T aScaleY
, T aScaleZ
) {
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);
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);
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
);
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
);
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
+
1236 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
+ _23
* aMatrix
._31
+
1238 matrix
._31
= _31
* aMatrix
._11
+ _32
* aMatrix
._21
+ _33
* aMatrix
._31
+
1240 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ _43
* aMatrix
._31
+
1242 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
+ _13
* aMatrix
._32
+
1244 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
+ _23
* aMatrix
._32
+
1246 matrix
._32
= _31
* aMatrix
._12
+ _32
* aMatrix
._22
+ _33
* aMatrix
._32
+
1248 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ _43
* aMatrix
._32
+
1250 matrix
._13
= _11
* aMatrix
._13
+ _12
* aMatrix
._23
+ _13
* aMatrix
._33
+
1252 matrix
._23
= _21
* aMatrix
._13
+ _22
* aMatrix
._23
+ _23
* aMatrix
._33
+
1254 matrix
._33
= _31
* aMatrix
._13
+ _32
* aMatrix
._23
+ _33
* aMatrix
._33
+
1256 matrix
._43
= _41
* aMatrix
._13
+ _42
* aMatrix
._23
+ _43
* aMatrix
._33
+
1258 matrix
._14
= _11
* aMatrix
._14
+ _12
* aMatrix
._24
+ _13
* aMatrix
._34
+
1260 matrix
._24
= _21
* aMatrix
._14
+ _22
* aMatrix
._24
+ _23
* aMatrix
._34
+
1262 matrix
._34
= _31
* aMatrix
._14
+ _32
* aMatrix
._24
+ _33
* aMatrix
._34
+
1264 matrix
._44
= _41
* aMatrix
._14
+ _42
* aMatrix
._24
+ _43
* aMatrix
._34
+
1270 Matrix4x4Typed
& operator*=(
1271 const Matrix4x4Typed
<TargetUnits
, TargetUnits
, T
>& aMatrix
) {
1272 *this = *this * aMatrix
;
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.
1304 T det
= Determinant();
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
;
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");
1373 Maybe
<Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
>> MaybeInverse() const {
1374 typedef Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
> InvertedMatrix
;
1375 InvertedMatrix clone
= Cast
<InvertedMatrix
>();
1376 if (clone
.Invert()) {
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
);
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
),
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
)) {
1471 Matrix4x4Typed mat
= *this;
1473 if (HasPerspectiveComponent()) {
1474 // We do not support projection matrices
1478 // Extract translation
1479 translation
.x
= mat
._41
;
1480 translation
.y
= mat
._42
;
1481 translation
.z
= mat
._43
;
1483 // Remove translation
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
);
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
1501 rotation
.SetFromRotationMatrix(this->ToUnknownMatrix());
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
);
1520 _22
= 1.0f
- (xx
+ zz
);
1526 _33
= 1.0f
- (xx
+ yy
);
1529 _14
= _42
= _43
= 0.0f
;
1533 // Set all the members of the matrix to NaN
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
) &&
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
);
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
));
1594 _21
= cosTheta
* _21
+ sinTheta
* _31
;
1595 _31
= -sinTheta
* temp
+ cosTheta
* _31
;
1598 _22
= cosTheta
* _22
+ sinTheta
* _32
;
1599 _32
= -sinTheta
* temp
+ cosTheta
* _32
;
1602 _23
= cosTheta
* _23
+ sinTheta
* _33
;
1603 _33
= -sinTheta
* temp
+ cosTheta
* _33
;
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
));
1618 _11
= cosTheta
* _11
+ -sinTheta
* _31
;
1619 _31
= sinTheta
* temp
+ cosTheta
* _31
;
1622 _12
= cosTheta
* _12
+ -sinTheta
* _32
;
1623 _32
= sinTheta
* temp
+ cosTheta
* _32
;
1626 _13
= cosTheta
* _13
+ -sinTheta
* _33
;
1627 _33
= sinTheta
* temp
+ cosTheta
* _33
;
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
));
1642 _11
= cosTheta
* _11
+ sinTheta
* _21
;
1643 _21
= -sinTheta
* temp
+ cosTheta
* _21
;
1646 _12
= cosTheta
* _12
+ sinTheta
* _22
;
1647 _22
= -sinTheta
* temp
+ cosTheta
* _22
;
1650 _13
= cosTheta
* _13
+ sinTheta
* _23
;
1651 _23
= -sinTheta
* temp
+ cosTheta
* _23
;
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()) {
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
);
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
);
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
;
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 {
1759 if (gfx::FuzzyEqual(_12
, 0) && gfx::FuzzyEqual(_21
, 0)) {
1761 } else if (gfx::FuzzyEqual(_22
, 0) && gfx::FuzzyEqual(_11
, 0)) {
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
));
1794 typedef Matrix4x4Typed
<UnknownUnits
, UnknownUnits
> Matrix4x4
;
1795 typedef Matrix4x4Typed
<UnknownUnits
, UnknownUnits
, double> Matrix4x4Double
;
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
)
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
+
1895 resultMatrix
._52
= this->_51
* aMatrix
._12
+ this->_52
* aMatrix
._22
+
1896 this->_53
* aMatrix
._32
+ this->_54
* aMatrix
._42
+
1898 resultMatrix
._53
= this->_51
* aMatrix
._13
+ this->_52
* aMatrix
._23
+
1899 this->_53
* aMatrix
._33
+ this->_54
* aMatrix
._43
+
1901 resultMatrix
._54
= this->_51
* aMatrix
._14
+ this->_52
* aMatrix
._24
+
1902 this->_53
* aMatrix
._34
+ this->_54
* aMatrix
._44
+
1905 return resultMatrix
;
1908 Matrix5x4
& operator*=(const Matrix5x4
& aMatrix
) {
1909 *this = *this * aMatrix
;
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] << ';';
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] << ';';
1924 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3]
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 {
1950 Simple
, // 2x3 Matrix
1954 template <typename SourceUnits
, typename TargetUnits
>
1955 class Matrix4x4TypedFlagged
1956 : protected Matrix4x4Typed
<SourceUnits
, TargetUnits
> {
1958 using Parent
= Matrix4x4Typed
<SourceUnits
, TargetUnits
>;
1959 using TargetPoint
= PointTyped
<TargetUnits
>;
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
,
1983 : Parent(a11
, a12
, a13
, a14
, a21
, a22
, a23
, a24
, a31
, a32
, a33
, a34
, a41
,
1988 MOZ_IMPLICIT
Matrix4x4TypedFlagged(const Parent
& aOther
) : Parent(aOther
) {
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
,
2008 static Matrix4x4TypedFlagged
Scaling(Float aScaleX
, Float aScaleY
,
2010 MatrixType matrixType
= MatrixType::Full
;
2011 if (aScaleZ
== 1.0) {
2012 if (aScaleX
== 1.0 && aScaleY
== 1.0) {
2013 matrixType
= MatrixType::Identity
;
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
);
2024 PointTyped
<TargetUnits
, F
> TransformPoint(
2025 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
2026 if (mType
== MatrixType::Identity
) {
2030 if (mType
== MatrixType::Simple
) {
2031 return TransformPointSimple(aPoint
);
2034 return Parent::TransformPoint(aPoint
);
2038 RectTyped
<TargetUnits
, F
> TransformBounds(
2039 const RectTyped
<SourceUnits
, F
>& aRect
) const {
2040 if (mType
== MatrixType::Identity
) {
2044 if (mType
== MatrixType::Simple
) {
2045 PointTyped
<TargetUnits
, F
> quad
[4];
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
) {
2061 if (quad
[i
].x
> max_x
) {
2065 if (quad
[i
].y
< min_y
) {
2068 if (quad
[i
].y
> max_y
) {
2073 return RectTyped
<TargetUnits
, F
>(min_x
, min_y
, max_x
- min_x
,
2077 return Parent::TransformBounds(aRect
);
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
) {
2117 return Parent::FuzzyEqual(o
);
2120 Matrix4x4TypedFlagged
& PreTranslate(Float aX
, Float aY
, Float aZ
) {
2121 if (mType
== MatrixType::Identity
) {
2127 mType
= MatrixType::Simple
;
2130 mType
= MatrixType::Full
;
2134 Parent::PreTranslate(aX
, aY
, aZ
);
2137 mType
= MatrixType::Full
;
2143 Matrix4x4TypedFlagged
& PostTranslate(Float aX
, Float aY
, Float aZ
) {
2144 if (mType
== MatrixType::Identity
) {
2150 mType
= MatrixType::Simple
;
2153 mType
= MatrixType::Full
;
2157 Parent::PostTranslate(aX
, aY
, aZ
);
2160 mType
= MatrixType::Full
;
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
);
2176 bool IsIdentity() const { return mType
== MatrixType::Identity
; }
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();
2200 bool IsSingular() const {
2201 if (mType
== MatrixType::Identity
) {
2204 return Parent::Determinant() == 0.0;
2208 if (mType
== MatrixType::Identity
) {
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
) {
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
2230 Maybe
<Matrix4x4TypedFlagged
<TargetUnits
, SourceUnits
>> MaybeInverse() const {
2231 typedef Matrix4x4TypedFlagged
<TargetUnits
, SourceUnits
> InvertedMatrix
;
2232 InvertedMatrix clone
= Cast
<InvertedMatrix
>();
2233 if (clone
.Invert()) {
2239 template <typename NewTargetUnits
>
2241 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2242 if (mType
== MatrixType::Identity
&&
2243 aMatrix
.mType
== MatrixType::Identity
) {
2246 // Depending on the usage it may make sense to compare more flags.
2247 return Parent::operator==(aMatrix
);
2250 template <typename NewTargetUnits
>
2252 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2253 if (mType
== MatrixType::Identity
&&
2254 aMatrix
.mType
== MatrixType::Identity
) {
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
) {
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
;
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
) {
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
;
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
;
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
;
2352 matrix
.mType
= MatrixType::Full
;
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
) {
2373 return Parent::CanDraw2D(aMatrix
);
2376 bool Is2D(Matrix
* aMatrix
) const {
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
};
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
,
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
};
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
;
2432 if (Parent::IsIdentity()) {
2433 mType
= MatrixType::Identity
;
2437 if (Parent::Is2D()) {
2438 mType
= MatrixType::Simple
;
2442 mType
= MatrixType::Full
;
2447 template <typename
, typename
>
2448 friend class Matrix4x4TypedFlagged
;
2451 using Matrix4x4Flagged
= Matrix4x4TypedFlagged
<UnknownUnits
, UnknownUnits
>;
2454 } // namespace mozilla
2456 #endif /* MOZILLA_GFX_MATRIX_H_ */