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
) +
639 * Returns true if the matrix is isomorphic to a 2D affine transformation.
642 if (_13
!= 0.0f
|| _14
!= 0.0f
|| _23
!= 0.0f
|| _24
!= 0.0f
||
643 _31
!= 0.0f
|| _32
!= 0.0f
|| _33
!= 1.0f
|| _34
!= 0.0f
||
644 _43
!= 0.0f
|| _44
!= 1.0f
) {
650 bool Is2D(BaseMatrix
<T
>* aMatrix
) const {
665 BaseMatrix
<T
> As2D() const {
666 MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform");
668 return BaseMatrix
<T
>(_11
, _12
, _21
, _22
, _41
, _42
);
671 bool CanDraw2D(BaseMatrix
<T
>* aMatrix
= nullptr) const {
672 if (_14
!= 0.0f
|| _24
!= 0.0f
|| _44
!= 1.0f
) {
686 Matrix4x4Typed
& ProjectTo2D() {
694 // Some matrices, such as those derived from perspective transforms,
695 // can modify _44 from 1, while leaving the rest of the fourth column
696 // (_14, _24) at 0. In this case, after resetting the third row and
697 // third column above, the value of _44 functions only to scale the
698 // coordinate transform divide by W. The matrix can be converted to
699 // a true 2D matrix by normalizing out the scaling effect of _44 on
700 // the remaining components ahead of time.
701 if (_14
== 0.0f
&& _24
== 0.0f
&& _44
!= 1.0f
&& _44
!= 0.0f
) {
702 T scale
= 1.0f
/ _44
;
715 Point4DTyped
<TargetUnits
, F
> ProjectPoint(
716 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
717 // Find a value for z that will transform to 0.
719 // The transformed value of z is computed as:
720 // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43;
722 // Solving for z when z' = 0 gives us:
723 F z
= -(aPoint
.x
* _13
+ aPoint
.y
* _23
+ _43
) / _33
;
725 // Compute the transformed point
726 return this->TransformPoint(
727 Point4DTyped
<SourceUnits
, F
>(aPoint
.x
, aPoint
.y
, z
, 1));
731 RectTyped
<TargetUnits
, F
> ProjectRectBounds(
732 const RectTyped
<SourceUnits
, F
>& aRect
,
733 const RectTyped
<TargetUnits
, F
>& aClip
) const {
734 // This function must never return std::numeric_limits<Float>::max() or any
735 // other arbitrary large value in place of inifinity. This often occurs
736 // when aRect is an inversed projection matrix or when aRect is transformed
737 // to be partly behind and in front of the camera (w=0 plane in homogenous
738 // coordinates) - See Bug 1035611
740 // Some call-sites will call RoundGfxRectToAppRect which clips both the
741 // extents and dimensions of the rect to be bounded by nscoord_MAX.
742 // If we return a Rect that, when converted to nscoords, has a width or
743 // height greater than nscoord_MAX, RoundGfxRectToAppRect will clip the
744 // overflow off both the min and max end of the rect after clipping the
745 // extents of the rect, resulting in a translation of the rect towards the
748 // The bounds returned by ProjectRectBounds are expected to be clipped only
749 // on the edges beyond the bounds of the coordinate system; otherwise, the
750 // clipped bounding box would be smaller than the correct one and result
751 // bugs such as incorrect culling (eg. Bug 1073056)
753 // To address this without requiring all code to work in homogenous
754 // coordinates or interpret infinite values correctly, a specialized
755 // clipping function is integrated into ProjectRectBounds.
757 // Callers should pass an aClip value that represents the extents to clip
758 // the result to, in the same coordinate system as aRect.
759 Point4DTyped
<TargetUnits
, F
> points
[4];
761 points
[0] = ProjectPoint(aRect
.TopLeft());
762 points
[1] = ProjectPoint(aRect
.TopRight());
763 points
[2] = ProjectPoint(aRect
.BottomRight());
764 points
[3] = ProjectPoint(aRect
.BottomLeft());
766 F min_x
= std::numeric_limits
<F
>::max();
767 F min_y
= std::numeric_limits
<F
>::max();
768 F max_x
= -std::numeric_limits
<F
>::max();
769 F max_y
= -std::numeric_limits
<F
>::max();
771 for (int i
= 0; i
< 4; i
++) {
772 // Only use points that exist above the w=0 plane
773 if (points
[i
].HasPositiveWCoord()) {
774 PointTyped
<TargetUnits
, F
> point2d
=
775 aClip
.ClampPoint(points
[i
].As2DPoint());
776 min_x
= std::min
<F
>(point2d
.x
, min_x
);
777 max_x
= std::max
<F
>(point2d
.x
, max_x
);
778 min_y
= std::min
<F
>(point2d
.y
, min_y
);
779 max_y
= std::max
<F
>(point2d
.y
, max_y
);
782 int next
= (i
== 3) ? 0 : i
+ 1;
783 if (points
[i
].HasPositiveWCoord() != points
[next
].HasPositiveWCoord()) {
784 // If the line between two points crosses the w=0 plane, then
785 // interpolate to find the point of intersection with the w=0 plane and
787 Point4DTyped
<TargetUnits
, F
> intercept
=
788 ComputePerspectivePlaneIntercept(points
[i
], points
[next
]);
789 // Since intercept.w will always be 0 here, we interpret x,y,z as a
790 // direction towards an infinite vanishing point.
791 if (intercept
.x
< 0.0f
) {
793 } else if (intercept
.x
> 0.0f
) {
794 max_x
= aClip
.XMost();
796 if (intercept
.y
< 0.0f
) {
798 } else if (intercept
.y
> 0.0f
) {
799 max_y
= aClip
.YMost();
804 if (max_x
< min_x
|| max_y
< min_y
) {
805 return RectTyped
<TargetUnits
, F
>(0, 0, 0, 0);
808 return RectTyped
<TargetUnits
, F
>(min_x
, min_y
, max_x
- min_x
,
813 * TransformAndClipBounds transforms aRect as a bounding box, while clipping
814 * the transformed bounds to the extents of aClip.
817 RectTyped
<TargetUnits
, F
> TransformAndClipBounds(
818 const RectTyped
<SourceUnits
, F
>& aRect
,
819 const RectTyped
<TargetUnits
, F
>& aClip
) const {
820 PointTyped
<UnknownUnits
, F
> verts
[kTransformAndClipRectMaxVerts
];
821 size_t vertCount
= TransformAndClipRect(aRect
, aClip
, verts
);
823 F min_x
= std::numeric_limits
<F
>::max();
824 F min_y
= std::numeric_limits
<F
>::max();
825 F max_x
= -std::numeric_limits
<F
>::max();
826 F max_y
= -std::numeric_limits
<F
>::max();
827 for (size_t i
= 0; i
< vertCount
; i
++) {
828 min_x
= std::min(min_x
, verts
[i
].x
.value
);
829 max_x
= std::max(max_x
, verts
[i
].x
.value
);
830 min_y
= std::min(min_y
, verts
[i
].y
.value
);
831 max_y
= std::max(max_y
, verts
[i
].y
.value
);
834 if (max_x
< min_x
|| max_y
< min_y
) {
835 return RectTyped
<TargetUnits
, F
>(0, 0, 0, 0);
838 return RectTyped
<TargetUnits
, F
>(min_x
, min_y
, max_x
- min_x
,
843 RectTyped
<TargetUnits
, F
> TransformAndClipBounds(
844 const TriangleTyped
<SourceUnits
, F
>& aTriangle
,
845 const RectTyped
<TargetUnits
, F
>& aClip
) const {
846 return TransformAndClipBounds(aTriangle
.BoundingBox(), aClip
);
850 * TransformAndClipRect projects a rectangle and clips against view frustum
851 * clipping planes in homogenous space so that its projected vertices are
852 * constrained within the 2d rectangle passed in aClip.
853 * The resulting vertices are populated in aVerts. aVerts must be
854 * pre-allocated to hold at least kTransformAndClipRectMaxVerts Points.
855 * The vertex count is returned by TransformAndClipRect. It is possible to
856 * emit fewer than 3 vertices, indicating that aRect will not be visible
860 size_t TransformAndClipRect(const RectTyped
<SourceUnits
, F
>& aRect
,
861 const RectTyped
<TargetUnits
, F
>& aClip
,
862 PointTyped
<TargetUnits
, F
>* aVerts
) const {
863 typedef Point4DTyped
<UnknownUnits
, F
> P4D
;
865 // The initial polygon is made up by the corners of aRect in homogenous
866 // space, mapped into the destination space of this transform.
867 P4D rectCorners
[] = {
868 TransformPoint(P4D(aRect
.X(), aRect
.Y(), 0, 1)),
869 TransformPoint(P4D(aRect
.XMost(), aRect
.Y(), 0, 1)),
870 TransformPoint(P4D(aRect
.XMost(), aRect
.YMost(), 0, 1)),
871 TransformPoint(P4D(aRect
.X(), aRect
.YMost(), 0, 1)),
874 // Cut off pieces of the polygon that are outside of aClip (the "view
875 // frustrum"), by consecutively intersecting the polygon with the half space
876 // induced by the clipping plane for each side of aClip.
877 // View frustum clipping planes are described as normals originating from
878 // the 0,0,0,0 origin.
879 // Each pass can increase or decrease the number of points that make up the
880 // current clipped polygon. We double buffer the set of points, alternating
881 // between polygonBufA and polygonBufB. Duplicated points in the polygons
882 // are kept around until all clipping is done. The loop at the end filters
883 // out any consecutive duplicates.
884 P4D polygonBufA
[kTransformAndClipRectMaxVerts
];
885 P4D polygonBufB
[kTransformAndClipRectMaxVerts
];
887 Span
<P4D
> polygon(rectCorners
);
888 polygon
= IntersectPolygon
<F
>(polygon
, P4D(1.0, 0.0, 0.0, -aClip
.X()),
890 polygon
= IntersectPolygon
<F
>(polygon
, P4D(-1.0, 0.0, 0.0, aClip
.XMost()),
892 polygon
= IntersectPolygon
<F
>(polygon
, P4D(0.0, 1.0, 0.0, -aClip
.Y()),
894 polygon
= IntersectPolygon
<F
>(polygon
, P4D(0.0, -1.0, 0.0, aClip
.YMost()),
897 size_t vertCount
= 0;
898 for (const auto& srcPoint
: polygon
) {
899 PointTyped
<TargetUnits
, F
> p
;
900 if (srcPoint
.w
== 0.0) {
901 // If a point lies on the intersection of the clipping planes at
902 // (0,0,0,0), we must avoid a division by zero w component.
903 p
= PointTyped
<TargetUnits
, F
>(0.0, 0.0);
905 p
= srcPoint
.As2DPoint();
907 // Emit only unique points
908 if (vertCount
== 0 || p
!= aVerts
[vertCount
- 1]) {
909 aVerts
[vertCount
++] = p
;
916 static const int kTransformAndClipRectMaxVerts
= 32;
918 static Matrix4x4Typed
From2D(const BaseMatrix
<T
>& aMatrix
) {
919 Matrix4x4Typed matrix
;
920 matrix
._11
= aMatrix
._11
;
921 matrix
._12
= aMatrix
._12
;
922 matrix
._21
= aMatrix
._21
;
923 matrix
._22
= aMatrix
._22
;
924 matrix
._41
= aMatrix
._31
;
925 matrix
._42
= aMatrix
._32
;
929 bool Is2DIntegerTranslation() const {
930 return Is2D() && As2D().IsIntegerTranslation();
933 TargetPoint4D
TransposeTransform4D(const SourcePoint4D
& aPoint
) const {
934 Float x
= aPoint
.x
* _11
+ aPoint
.y
* _12
+ aPoint
.z
* _13
+ aPoint
.w
* _14
;
935 Float y
= aPoint
.x
* _21
+ aPoint
.y
* _22
+ aPoint
.z
* _23
+ aPoint
.w
* _24
;
936 Float z
= aPoint
.x
* _31
+ aPoint
.y
* _32
+ aPoint
.z
* _33
+ aPoint
.w
* _34
;
937 Float w
= aPoint
.x
* _41
+ aPoint
.y
* _42
+ aPoint
.z
* _43
+ aPoint
.w
* _44
;
939 return TargetPoint4D(x
, y
, z
, w
);
943 Point4DTyped
<TargetUnits
, F
> TransformPoint(
944 const Point4DTyped
<SourceUnits
, F
>& aPoint
) const {
945 Point4DTyped
<TargetUnits
, F
> retPoint
;
948 aPoint
.x
* _11
+ aPoint
.y
* _21
+ aPoint
.z
* _31
+ aPoint
.w
* _41
;
950 aPoint
.x
* _12
+ aPoint
.y
* _22
+ aPoint
.z
* _32
+ aPoint
.w
* _42
;
952 aPoint
.x
* _13
+ aPoint
.y
* _23
+ aPoint
.z
* _33
+ aPoint
.w
* _43
;
954 aPoint
.x
* _14
+ aPoint
.y
* _24
+ aPoint
.z
* _34
+ aPoint
.w
* _44
;
960 Point3DTyped
<TargetUnits
, F
> TransformPoint(
961 const Point3DTyped
<SourceUnits
, F
>& aPoint
) const {
962 Point3DTyped
<TargetUnits
, F
> result
;
963 result
.x
= aPoint
.x
* _11
+ aPoint
.y
* _21
+ aPoint
.z
* _31
+ _41
;
964 result
.y
= aPoint
.x
* _12
+ aPoint
.y
* _22
+ aPoint
.z
* _32
+ _42
;
965 result
.z
= aPoint
.x
* _13
+ aPoint
.y
* _23
+ aPoint
.z
* _33
+ _43
;
967 result
/= (aPoint
.x
* _14
+ aPoint
.y
* _24
+ aPoint
.z
* _34
+ _44
);
973 PointTyped
<TargetUnits
, F
> TransformPoint(
974 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
975 Point4DTyped
<SourceUnits
, F
> temp(aPoint
.x
, aPoint
.y
, 0, 1);
976 return TransformPoint(temp
).As2DPoint();
980 GFX2D_API RectTyped
<TargetUnits
, F
> TransformBounds(
981 const RectTyped
<SourceUnits
, F
>& aRect
) const {
982 PointTyped
<TargetUnits
, F
> quad
[4];
986 quad
[0] = TransformPoint(aRect
.TopLeft());
987 quad
[1] = TransformPoint(aRect
.TopRight());
988 quad
[2] = TransformPoint(aRect
.BottomLeft());
989 quad
[3] = TransformPoint(aRect
.BottomRight());
991 min_x
= max_x
= quad
[0].x
;
992 min_y
= max_y
= quad
[0].y
;
994 for (int i
= 1; i
< 4; i
++) {
995 if (quad
[i
].x
< min_x
) {
998 if (quad
[i
].x
> max_x
) {
1002 if (quad
[i
].y
< min_y
) {
1005 if (quad
[i
].y
> max_y
) {
1010 return RectTyped
<TargetUnits
, F
>(min_x
, min_y
, max_x
- min_x
,
1014 static Matrix4x4Typed
Translation(T aX
, T aY
, T aZ
) {
1015 return Matrix4x4Typed(1.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
,
1016 0.0f
, 1.0f
, 0.0f
, aX
, aY
, aZ
, 1.0f
);
1019 static Matrix4x4Typed
Translation(const TargetPoint3D
& aP
) {
1020 return Translation(aP
.x
, aP
.y
, aP
.z
);
1023 static Matrix4x4Typed
Translation(const TargetPoint
& aP
) {
1024 return Translation(aP
.x
, aP
.y
, 0);
1028 * Apply a translation to this matrix.
1030 * The "Pre" in this method's name means that the translation is applied
1031 * -before- this matrix's existing transformation. That is, any vector that
1032 * is multiplied by the resulting matrix will first be translated, then be
1033 * transformed by the original transform.
1035 * Calling this method will result in this matrix having the same value as
1038 * Matrix4x4::Translation(x, y) * this
1040 * (Note that in performance critical code multiplying by the result of a
1041 * Translation()/Scaling() call is not recommended since that results in a
1042 * full matrix multiply involving 64 floating-point multiplications. Calling
1043 * this method would be preferred since it only involves 12 floating-point
1046 Matrix4x4Typed
& PreTranslate(T aX
, T aY
, T aZ
) {
1047 _41
+= aX
* _11
+ aY
* _21
+ aZ
* _31
;
1048 _42
+= aX
* _12
+ aY
* _22
+ aZ
* _32
;
1049 _43
+= aX
* _13
+ aY
* _23
+ aZ
* _33
;
1050 _44
+= aX
* _14
+ aY
* _24
+ aZ
* _34
;
1055 Matrix4x4Typed
& PreTranslate(const Point3DTyped
<UnknownUnits
, T
>& aPoint
) {
1056 return PreTranslate(aPoint
.x
, aPoint
.y
, aPoint
.z
);
1060 * Similar to PreTranslate, but the translation is applied -after- this
1061 * matrix's existing transformation instead of before it.
1063 * This method is generally less used than PreTranslate since typically code
1064 * wants to adjust an existing user space to device space matrix to create a
1065 * transform to device space from a -new- user space (translated from the
1066 * previous user space). In that case consumers will need to use the Pre*
1067 * variants of the matrix methods rather than using the Post* methods, since
1068 * the Post* methods add a transform to the device space end of the
1071 Matrix4x4Typed
& PostTranslate(T aX
, T aY
, T aZ
) {
1088 Matrix4x4Typed
& PostTranslate(const TargetPoint3D
& aPoint
) {
1089 return PostTranslate(aPoint
.x
, aPoint
.y
, aPoint
.z
);
1092 Matrix4x4Typed
& PostTranslate(const TargetPoint
& aPoint
) {
1093 return PostTranslate(aPoint
.x
, aPoint
.y
, 0);
1096 static Matrix4x4Typed
Scaling(T aScaleX
, T aScaleY
, T aScaleZ
) {
1097 return Matrix4x4Typed(aScaleX
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, aScaleY
, 0.0f
, 0.0f
,
1098 0.0f
, 0.0f
, aScaleZ
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 1.0f
);
1102 * Similar to PreTranslate, but applies a scale instead of a translation.
1104 Matrix4x4Typed
& PreScale(T aX
, T aY
, T aZ
) {
1122 * Similar to PostTranslate, but applies a scale instead of a translation.
1124 Matrix4x4Typed
& PostScale(T aScaleX
, T aScaleY
, T aScaleZ
) {
1141 void SkewXY(T aSkew
) { (*this)[1] += (*this)[0] * aSkew
; }
1143 void SkewXZ(T aSkew
) { (*this)[2] += (*this)[0] * aSkew
; }
1145 void SkewYZ(T aSkew
) { (*this)[2] += (*this)[1] * aSkew
; }
1147 Matrix4x4Typed
& ChangeBasis(const Point3DTyped
<UnknownUnits
, T
>& aOrigin
) {
1148 return ChangeBasis(aOrigin
.x
, aOrigin
.y
, aOrigin
.z
);
1151 Matrix4x4Typed
& ChangeBasis(T aX
, T aY
, T aZ
) {
1152 // Translate to the origin before applying this matrix
1153 PreTranslate(-aX
, -aY
, -aZ
);
1155 // Translate back into position after applying this matrix
1156 PostTranslate(aX
, aY
, aZ
);
1161 Matrix4x4Typed
& Transpose() {
1162 std::swap(_12
, _21
);
1163 std::swap(_13
, _31
);
1164 std::swap(_14
, _41
);
1166 std::swap(_23
, _32
);
1167 std::swap(_24
, _42
);
1169 std::swap(_34
, _43
);
1174 bool operator==(const Matrix4x4Typed
& o
) const {
1175 // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics
1176 return _11
== o
._11
&& _12
== o
._12
&& _13
== o
._13
&& _14
== o
._14
&&
1177 _21
== o
._21
&& _22
== o
._22
&& _23
== o
._23
&& _24
== o
._24
&&
1178 _31
== o
._31
&& _32
== o
._32
&& _33
== o
._33
&& _34
== o
._34
&&
1179 _41
== o
._41
&& _42
== o
._42
&& _43
== o
._43
&& _44
== o
._44
;
1182 bool operator!=(const Matrix4x4Typed
& o
) const { return !((*this) == o
); }
1184 Matrix4x4Typed
& operator=(const Matrix4x4Typed
& aOther
) = default;
1186 template <typename NewTargetUnits
>
1187 Matrix4x4Typed
<SourceUnits
, NewTargetUnits
, T
> operator*(
1188 const Matrix4x4Typed
<TargetUnits
, NewTargetUnits
, T
>& aMatrix
) const {
1189 Matrix4x4Typed
<SourceUnits
, NewTargetUnits
, T
> matrix
;
1191 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
+ _13
* aMatrix
._31
+
1193 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
+ _23
* aMatrix
._31
+
1195 matrix
._31
= _31
* aMatrix
._11
+ _32
* aMatrix
._21
+ _33
* aMatrix
._31
+
1197 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ _43
* aMatrix
._31
+
1199 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
+ _13
* aMatrix
._32
+
1201 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
+ _23
* aMatrix
._32
+
1203 matrix
._32
= _31
* aMatrix
._12
+ _32
* aMatrix
._22
+ _33
* aMatrix
._32
+
1205 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ _43
* aMatrix
._32
+
1207 matrix
._13
= _11
* aMatrix
._13
+ _12
* aMatrix
._23
+ _13
* aMatrix
._33
+
1209 matrix
._23
= _21
* aMatrix
._13
+ _22
* aMatrix
._23
+ _23
* aMatrix
._33
+
1211 matrix
._33
= _31
* aMatrix
._13
+ _32
* aMatrix
._23
+ _33
* aMatrix
._33
+
1213 matrix
._43
= _41
* aMatrix
._13
+ _42
* aMatrix
._23
+ _43
* aMatrix
._33
+
1215 matrix
._14
= _11
* aMatrix
._14
+ _12
* aMatrix
._24
+ _13
* aMatrix
._34
+
1217 matrix
._24
= _21
* aMatrix
._14
+ _22
* aMatrix
._24
+ _23
* aMatrix
._34
+
1219 matrix
._34
= _31
* aMatrix
._14
+ _32
* aMatrix
._24
+ _33
* aMatrix
._34
+
1221 matrix
._44
= _41
* aMatrix
._14
+ _42
* aMatrix
._24
+ _43
* aMatrix
._34
+
1227 Matrix4x4Typed
& operator*=(
1228 const Matrix4x4Typed
<TargetUnits
, TargetUnits
, T
>& aMatrix
) {
1229 *this = *this * aMatrix
;
1233 /* Returns true if the matrix is an identity matrix.
1235 bool IsIdentity() const {
1236 return _11
== 1.0f
&& _12
== 0.0f
&& _13
== 0.0f
&& _14
== 0.0f
&&
1237 _21
== 0.0f
&& _22
== 1.0f
&& _23
== 0.0f
&& _24
== 0.0f
&&
1238 _31
== 0.0f
&& _32
== 0.0f
&& _33
== 1.0f
&& _34
== 0.0f
&&
1239 _41
== 0.0f
&& _42
== 0.0f
&& _43
== 0.0f
&& _44
== 1.0f
;
1242 bool IsSingular() const { return Determinant() == 0.0; }
1244 T
Determinant() const {
1245 return _14
* _23
* _32
* _41
- _13
* _24
* _32
* _41
-
1246 _14
* _22
* _33
* _41
+ _12
* _24
* _33
* _41
+
1247 _13
* _22
* _34
* _41
- _12
* _23
* _34
* _41
-
1248 _14
* _23
* _31
* _42
+ _13
* _24
* _31
* _42
+
1249 _14
* _21
* _33
* _42
- _11
* _24
* _33
* _42
-
1250 _13
* _21
* _34
* _42
+ _11
* _23
* _34
* _42
+
1251 _14
* _22
* _31
* _43
- _12
* _24
* _31
* _43
-
1252 _14
* _21
* _32
* _43
+ _11
* _24
* _32
* _43
+
1253 _12
* _21
* _34
* _43
- _11
* _22
* _34
* _43
-
1254 _13
* _22
* _31
* _44
+ _12
* _23
* _31
* _44
+
1255 _13
* _21
* _32
* _44
- _11
* _23
* _32
* _44
-
1256 _12
* _21
* _33
* _44
+ _11
* _22
* _33
* _44
;
1259 // Invert() is not unit-correct. Prefer Inverse() where possible.
1261 T det
= Determinant();
1266 Matrix4x4Typed
<SourceUnits
, TargetUnits
, T
> result
;
1267 result
._11
= _23
* _34
* _42
- _24
* _33
* _42
+ _24
* _32
* _43
-
1268 _22
* _34
* _43
- _23
* _32
* _44
+ _22
* _33
* _44
;
1269 result
._12
= _14
* _33
* _42
- _13
* _34
* _42
- _14
* _32
* _43
+
1270 _12
* _34
* _43
+ _13
* _32
* _44
- _12
* _33
* _44
;
1271 result
._13
= _13
* _24
* _42
- _14
* _23
* _42
+ _14
* _22
* _43
-
1272 _12
* _24
* _43
- _13
* _22
* _44
+ _12
* _23
* _44
;
1273 result
._14
= _14
* _23
* _32
- _13
* _24
* _32
- _14
* _22
* _33
+
1274 _12
* _24
* _33
+ _13
* _22
* _34
- _12
* _23
* _34
;
1275 result
._21
= _24
* _33
* _41
- _23
* _34
* _41
- _24
* _31
* _43
+
1276 _21
* _34
* _43
+ _23
* _31
* _44
- _21
* _33
* _44
;
1277 result
._22
= _13
* _34
* _41
- _14
* _33
* _41
+ _14
* _31
* _43
-
1278 _11
* _34
* _43
- _13
* _31
* _44
+ _11
* _33
* _44
;
1279 result
._23
= _14
* _23
* _41
- _13
* _24
* _41
- _14
* _21
* _43
+
1280 _11
* _24
* _43
+ _13
* _21
* _44
- _11
* _23
* _44
;
1281 result
._24
= _13
* _24
* _31
- _14
* _23
* _31
+ _14
* _21
* _33
-
1282 _11
* _24
* _33
- _13
* _21
* _34
+ _11
* _23
* _34
;
1283 result
._31
= _22
* _34
* _41
- _24
* _32
* _41
+ _24
* _31
* _42
-
1284 _21
* _34
* _42
- _22
* _31
* _44
+ _21
* _32
* _44
;
1285 result
._32
= _14
* _32
* _41
- _12
* _34
* _41
- _14
* _31
* _42
+
1286 _11
* _34
* _42
+ _12
* _31
* _44
- _11
* _32
* _44
;
1287 result
._33
= _12
* _24
* _41
- _14
* _22
* _41
+ _14
* _21
* _42
-
1288 _11
* _24
* _42
- _12
* _21
* _44
+ _11
* _22
* _44
;
1289 result
._34
= _14
* _22
* _31
- _12
* _24
* _31
- _14
* _21
* _32
+
1290 _11
* _24
* _32
+ _12
* _21
* _34
- _11
* _22
* _34
;
1291 result
._41
= _23
* _32
* _41
- _22
* _33
* _41
- _23
* _31
* _42
+
1292 _21
* _33
* _42
+ _22
* _31
* _43
- _21
* _32
* _43
;
1293 result
._42
= _12
* _33
* _41
- _13
* _32
* _41
+ _13
* _31
* _42
-
1294 _11
* _33
* _42
- _12
* _31
* _43
+ _11
* _32
* _43
;
1295 result
._43
= _13
* _22
* _41
- _12
* _23
* _41
- _13
* _21
* _42
+
1296 _11
* _23
* _42
+ _12
* _21
* _43
- _11
* _22
* _43
;
1297 result
._44
= _12
* _23
* _31
- _13
* _22
* _31
+ _13
* _21
* _32
-
1298 _11
* _23
* _32
- _12
* _21
* _33
+ _11
* _22
* _33
;
1321 Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
> Inverse() const {
1322 typedef Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
> InvertedMatrix
;
1323 InvertedMatrix clone
= InvertedMatrix::FromUnknownMatrix(ToUnknownMatrix());
1324 DebugOnly
<bool> inverted
= clone
.Invert();
1325 MOZ_ASSERT(inverted
,
1326 "Attempted to get the inverse of a non-invertible matrix");
1330 Maybe
<Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
>> MaybeInverse() const {
1331 typedef Matrix4x4Typed
<TargetUnits
, SourceUnits
, T
> InvertedMatrix
;
1332 InvertedMatrix clone
= InvertedMatrix::FromUnknownMatrix(ToUnknownMatrix());
1333 if (clone
.Invert()) {
1340 for (int i
= 0; i
< 4; i
++) {
1341 for (int j
= 0; j
< 4; j
++) {
1342 (*this)[i
][j
] /= (*this)[3][3];
1347 bool FuzzyEqual(const Matrix4x4Typed
& o
) const {
1348 return gfx::FuzzyEqual(_11
, o
._11
) && gfx::FuzzyEqual(_12
, o
._12
) &&
1349 gfx::FuzzyEqual(_13
, o
._13
) && gfx::FuzzyEqual(_14
, o
._14
) &&
1350 gfx::FuzzyEqual(_21
, o
._21
) && gfx::FuzzyEqual(_22
, o
._22
) &&
1351 gfx::FuzzyEqual(_23
, o
._23
) && gfx::FuzzyEqual(_24
, o
._24
) &&
1352 gfx::FuzzyEqual(_31
, o
._31
) && gfx::FuzzyEqual(_32
, o
._32
) &&
1353 gfx::FuzzyEqual(_33
, o
._33
) && gfx::FuzzyEqual(_34
, o
._34
) &&
1354 gfx::FuzzyEqual(_41
, o
._41
) && gfx::FuzzyEqual(_42
, o
._42
) &&
1355 gfx::FuzzyEqual(_43
, o
._43
) && gfx::FuzzyEqual(_44
, o
._44
);
1358 bool FuzzyEqualsMultiplicative(const Matrix4x4Typed
& o
) const {
1359 return ::mozilla::FuzzyEqualsMultiplicative(_11
, o
._11
) &&
1360 ::mozilla::FuzzyEqualsMultiplicative(_12
, o
._12
) &&
1361 ::mozilla::FuzzyEqualsMultiplicative(_13
, o
._13
) &&
1362 ::mozilla::FuzzyEqualsMultiplicative(_14
, o
._14
) &&
1363 ::mozilla::FuzzyEqualsMultiplicative(_21
, o
._21
) &&
1364 ::mozilla::FuzzyEqualsMultiplicative(_22
, o
._22
) &&
1365 ::mozilla::FuzzyEqualsMultiplicative(_23
, o
._23
) &&
1366 ::mozilla::FuzzyEqualsMultiplicative(_24
, o
._24
) &&
1367 ::mozilla::FuzzyEqualsMultiplicative(_31
, o
._31
) &&
1368 ::mozilla::FuzzyEqualsMultiplicative(_32
, o
._32
) &&
1369 ::mozilla::FuzzyEqualsMultiplicative(_33
, o
._33
) &&
1370 ::mozilla::FuzzyEqualsMultiplicative(_34
, o
._34
) &&
1371 ::mozilla::FuzzyEqualsMultiplicative(_41
, o
._41
) &&
1372 ::mozilla::FuzzyEqualsMultiplicative(_42
, o
._42
) &&
1373 ::mozilla::FuzzyEqualsMultiplicative(_43
, o
._43
) &&
1374 ::mozilla::FuzzyEqualsMultiplicative(_44
, o
._44
);
1377 bool IsBackfaceVisible() const {
1378 // Inverse()._33 < 0;
1379 T det
= Determinant();
1380 T __33
= _12
* _24
* _41
- _14
* _22
* _41
+ _14
* _21
* _42
-
1381 _11
* _24
* _42
- _12
* _21
* _44
+ _11
* _22
* _44
;
1382 return (__33
* det
) < 0;
1385 Matrix4x4Typed
& NudgeToIntegersFixedEpsilon() {
1386 NudgeToInteger(&_11
);
1387 NudgeToInteger(&_12
);
1388 NudgeToInteger(&_13
);
1389 NudgeToInteger(&_14
);
1390 NudgeToInteger(&_21
);
1391 NudgeToInteger(&_22
);
1392 NudgeToInteger(&_23
);
1393 NudgeToInteger(&_24
);
1394 NudgeToInteger(&_31
);
1395 NudgeToInteger(&_32
);
1396 NudgeToInteger(&_33
);
1397 NudgeToInteger(&_34
);
1398 static const float error
= 1e-5f
;
1399 NudgeToInteger(&_41
, error
);
1400 NudgeToInteger(&_42
, error
);
1401 NudgeToInteger(&_43
, error
);
1402 NudgeToInteger(&_44
, error
);
1406 Point4D
TransposedVector(int aIndex
) const {
1407 MOZ_ASSERT(aIndex
>= 0 && aIndex
<= 3, "Invalid matrix array index");
1408 return Point4DTyped
<UnknownUnits
, T
>(*((&_11
) + aIndex
), *((&_21
) + aIndex
),
1410 *((&_41
) + aIndex
));
1413 void SetTransposedVector(int aIndex
, Point4DTyped
<UnknownUnits
, T
>& aVector
) {
1414 MOZ_ASSERT(aIndex
>= 0 && aIndex
<= 3, "Invalid matrix array index");
1415 *((&_11
) + aIndex
) = aVector
.x
;
1416 *((&_21
) + aIndex
) = aVector
.y
;
1417 *((&_31
) + aIndex
) = aVector
.z
;
1418 *((&_41
) + aIndex
) = aVector
.w
;
1421 bool Decompose(Point3DTyped
<UnknownUnits
, T
>& translation
,
1422 BaseQuaternion
<T
>& rotation
,
1423 Point3DTyped
<UnknownUnits
, T
>& scale
) const {
1424 // Ensure matrix can be normalized
1425 if (gfx::FuzzyEqual(_44
, 0.0f
)) {
1428 Matrix4x4Typed mat
= *this;
1430 if (HasPerspectiveComponent()) {
1431 // We do not support projection matrices
1435 // Extract translation
1436 translation
.x
= mat
._41
;
1437 translation
.y
= mat
._42
;
1438 translation
.z
= mat
._43
;
1440 // Remove translation
1446 scale
.x
= sqrtf(_11
* _11
+ _21
* _21
+ _31
* _31
);
1447 scale
.y
= sqrtf(_12
* _12
+ _22
* _22
+ _32
* _32
);
1448 scale
.z
= sqrtf(_13
* _13
+ _23
* _23
+ _33
* _33
);
1451 if (gfx::FuzzyEqual(scale
.x
, 0.0f
) || gfx::FuzzyEqual(scale
.y
, 0.0f
) ||
1452 gfx::FuzzyEqual(scale
.z
, 0.0f
)) {
1453 // We do not support matrices with a zero scale component
1458 rotation
.SetFromRotationMatrix(this->ToUnknownMatrix());
1462 // Sets this matrix to a rotation matrix given by aQuat.
1463 // This quaternion *MUST* be normalized!
1464 // Implemented in Quaternion.cpp
1465 void SetRotationFromQuaternion(const BaseQuaternion
<T
>& q
) {
1466 const T x2
= q
.x
+ q
.x
, y2
= q
.y
+ q
.y
, z2
= q
.z
+ q
.z
;
1467 const T xx
= q
.x
* x2
, xy
= q
.x
* y2
, xz
= q
.x
* z2
;
1468 const T yy
= q
.y
* y2
, yz
= q
.y
* z2
, zz
= q
.z
* z2
;
1469 const T wx
= q
.w
* x2
, wy
= q
.w
* y2
, wz
= q
.w
* z2
;
1471 _11
= 1.0f
- (yy
+ zz
);
1477 _22
= 1.0f
- (xx
+ zz
);
1483 _33
= 1.0f
- (xx
+ yy
);
1486 _14
= _42
= _43
= 0.0f
;
1490 // Set all the members of the matrix to NaN
1492 _11
= UnspecifiedNaN
<T
>();
1493 _21
= UnspecifiedNaN
<T
>();
1494 _31
= UnspecifiedNaN
<T
>();
1495 _41
= UnspecifiedNaN
<T
>();
1496 _12
= UnspecifiedNaN
<T
>();
1497 _22
= UnspecifiedNaN
<T
>();
1498 _32
= UnspecifiedNaN
<T
>();
1499 _42
= UnspecifiedNaN
<T
>();
1500 _13
= UnspecifiedNaN
<T
>();
1501 _23
= UnspecifiedNaN
<T
>();
1502 _33
= UnspecifiedNaN
<T
>();
1503 _43
= UnspecifiedNaN
<T
>();
1504 _14
= UnspecifiedNaN
<T
>();
1505 _24
= UnspecifiedNaN
<T
>();
1506 _34
= UnspecifiedNaN
<T
>();
1507 _44
= UnspecifiedNaN
<T
>();
1510 // Verifies that the matrix contains no Infs or NaNs
1511 bool IsFinite() const {
1512 return std::isfinite(_11
) && std::isfinite(_12
) && std::isfinite(_13
) &&
1513 std::isfinite(_14
) && std::isfinite(_21
) && std::isfinite(_22
) &&
1514 std::isfinite(_23
) && std::isfinite(_24
) && std::isfinite(_31
) &&
1515 std::isfinite(_32
) && std::isfinite(_33
) && std::isfinite(_34
) &&
1516 std::isfinite(_41
) && std::isfinite(_42
) && std::isfinite(_43
) &&
1520 void SkewXY(double aXSkew
, double aYSkew
) {
1521 // XXX Is double precision really necessary here
1522 T tanX
= SafeTangent(aXSkew
);
1523 T tanY
= SafeTangent(aYSkew
);
1543 void RotateX(double aTheta
) {
1544 // XXX Is double precision really necessary here
1545 double cosTheta
= FlushToZero(cos(aTheta
));
1546 double sinTheta
= FlushToZero(sin(aTheta
));
1551 _21
= cosTheta
* _21
+ sinTheta
* _31
;
1552 _31
= -sinTheta
* temp
+ cosTheta
* _31
;
1555 _22
= cosTheta
* _22
+ sinTheta
* _32
;
1556 _32
= -sinTheta
* temp
+ cosTheta
* _32
;
1559 _23
= cosTheta
* _23
+ sinTheta
* _33
;
1560 _33
= -sinTheta
* temp
+ cosTheta
* _33
;
1563 _24
= cosTheta
* _24
+ sinTheta
* _34
;
1564 _34
= -sinTheta
* temp
+ cosTheta
* _34
;
1567 void RotateY(double aTheta
) {
1568 // XXX Is double precision really necessary here
1569 double cosTheta
= FlushToZero(cos(aTheta
));
1570 double sinTheta
= FlushToZero(sin(aTheta
));
1575 _11
= cosTheta
* _11
+ -sinTheta
* _31
;
1576 _31
= sinTheta
* temp
+ cosTheta
* _31
;
1579 _12
= cosTheta
* _12
+ -sinTheta
* _32
;
1580 _32
= sinTheta
* temp
+ cosTheta
* _32
;
1583 _13
= cosTheta
* _13
+ -sinTheta
* _33
;
1584 _33
= sinTheta
* temp
+ cosTheta
* _33
;
1587 _14
= cosTheta
* _14
+ -sinTheta
* _34
;
1588 _34
= sinTheta
* temp
+ cosTheta
* _34
;
1591 void RotateZ(double aTheta
) {
1592 // XXX Is double precision really necessary here
1593 double cosTheta
= FlushToZero(cos(aTheta
));
1594 double sinTheta
= FlushToZero(sin(aTheta
));
1599 _11
= cosTheta
* _11
+ sinTheta
* _21
;
1600 _21
= -sinTheta
* temp
+ cosTheta
* _21
;
1603 _12
= cosTheta
* _12
+ sinTheta
* _22
;
1604 _22
= -sinTheta
* temp
+ cosTheta
* _22
;
1607 _13
= cosTheta
* _13
+ sinTheta
* _23
;
1608 _23
= -sinTheta
* temp
+ cosTheta
* _23
;
1611 _14
= cosTheta
* _14
+ sinTheta
* _24
;
1612 _24
= -sinTheta
* temp
+ cosTheta
* _24
;
1615 // Sets this matrix to a rotation matrix about a
1616 // vector [x,y,z] by angle theta. The vector is normalized
1617 // to a unit vector.
1618 // https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
1619 void SetRotateAxisAngle(double aX
, double aY
, double aZ
, double aTheta
) {
1620 Point3DTyped
<UnknownUnits
, T
> vector(aX
, aY
, aZ
);
1621 if (!vector
.Length()) {
1624 vector
.RobustNormalize();
1626 double x
= vector
.x
;
1627 double y
= vector
.y
;
1628 double z
= vector
.z
;
1630 double cosTheta
= FlushToZero(cos(aTheta
));
1631 double sinTheta
= FlushToZero(sin(aTheta
));
1633 // sin(aTheta / 2) * cos(aTheta / 2)
1634 double sc
= sinTheta
/ 2;
1635 // pow(sin(aTheta / 2), 2)
1636 double sq
= (1 - cosTheta
) / 2;
1638 _11
= 1 - 2 * (y
* y
+ z
* z
) * sq
;
1639 _12
= 2 * (x
* y
* sq
+ z
* sc
);
1640 _13
= 2 * (x
* z
* sq
- y
* sc
);
1642 _21
= 2 * (x
* y
* sq
- z
* sc
);
1643 _22
= 1 - 2 * (x
* x
+ z
* z
) * sq
;
1644 _23
= 2 * (y
* z
* sq
+ x
* sc
);
1646 _31
= 2 * (x
* z
* sq
+ y
* sc
);
1647 _32
= 2 * (y
* z
* sq
- x
* sc
);
1648 _33
= 1 - 2 * (x
* x
+ y
* y
) * sq
;
1656 void Perspective(T aDepth
) {
1657 MOZ_ASSERT(aDepth
> 0.0f
, "Perspective must be positive!");
1658 _31
+= -1.0 / aDepth
* _41
;
1659 _32
+= -1.0 / aDepth
* _42
;
1660 _33
+= -1.0 / aDepth
* _43
;
1661 _34
+= -1.0 / aDepth
* _44
;
1664 Point3D
GetNormalVector() const {
1665 // Define a plane in transformed space as the transformations
1666 // of 3 points on the z=0 screen plane.
1667 Point3DTyped
<UnknownUnits
, T
> a
=
1668 TransformPoint(Point3DTyped
<UnknownUnits
, T
>(0, 0, 0));
1669 Point3DTyped
<UnknownUnits
, T
> b
=
1670 TransformPoint(Point3DTyped
<UnknownUnits
, T
>(0, 1, 0));
1671 Point3DTyped
<UnknownUnits
, T
> c
=
1672 TransformPoint(Point3DTyped
<UnknownUnits
, T
>(1, 0, 0));
1674 // Convert to two vectors on the surface of the plane.
1675 Point3DTyped
<UnknownUnits
, T
> ab
= b
- a
;
1676 Point3DTyped
<UnknownUnits
, T
> ac
= c
- a
;
1678 return ac
.CrossProduct(ab
);
1682 * Returns true if the matrix has any transform other
1683 * than a straight translation.
1685 bool HasNonTranslation() const {
1686 return !gfx::FuzzyEqual(_11
, 1.0) || !gfx::FuzzyEqual(_22
, 1.0) ||
1687 !gfx::FuzzyEqual(_12
, 0.0) || !gfx::FuzzyEqual(_21
, 0.0) ||
1688 !gfx::FuzzyEqual(_13
, 0.0) || !gfx::FuzzyEqual(_23
, 0.0) ||
1689 !gfx::FuzzyEqual(_31
, 0.0) || !gfx::FuzzyEqual(_32
, 0.0) ||
1690 !gfx::FuzzyEqual(_33
, 1.0);
1694 * Returns true if the matrix is anything other than a straight
1695 * translation by integers.
1697 bool HasNonIntegerTranslation() const {
1698 return HasNonTranslation() || !gfx::FuzzyEqual(_41
, floor(_41
+ 0.5)) ||
1699 !gfx::FuzzyEqual(_42
, floor(_42
+ 0.5)) ||
1700 !gfx::FuzzyEqual(_43
, floor(_43
+ 0.5));
1704 * Return true if the matrix is with perspective (w).
1706 bool HasPerspectiveComponent() const {
1707 return _14
!= 0 || _24
!= 0 || _34
!= 0 || _44
!= 1;
1710 /* Returns true if the matrix is a rectilinear transformation (i.e.
1711 * grid-aligned rectangles are transformed to grid-aligned rectangles).
1712 * This should only be called on 2D matrices.
1714 bool IsRectilinear() const {
1716 if (gfx::FuzzyEqual(_12
, 0) && gfx::FuzzyEqual(_21
, 0)) {
1718 } else if (gfx::FuzzyEqual(_22
, 0) && gfx::FuzzyEqual(_11
, 0)) {
1725 * Convert between typed and untyped matrices.
1727 using UnknownMatrix
= Matrix4x4Typed
<UnknownUnits
, UnknownUnits
, T
>;
1728 UnknownMatrix
ToUnknownMatrix() const {
1729 return UnknownMatrix
{_11
, _12
, _13
, _14
, _21
, _22
, _23
, _24
,
1730 _31
, _32
, _33
, _34
, _41
, _42
, _43
, _44
};
1732 static Matrix4x4Typed
FromUnknownMatrix(const UnknownMatrix
& aUnknown
) {
1733 return Matrix4x4Typed
{
1734 aUnknown
._11
, aUnknown
._12
, aUnknown
._13
, aUnknown
._14
,
1735 aUnknown
._21
, aUnknown
._22
, aUnknown
._23
, aUnknown
._24
,
1736 aUnknown
._31
, aUnknown
._32
, aUnknown
._33
, aUnknown
._34
,
1737 aUnknown
._41
, aUnknown
._42
, aUnknown
._43
, aUnknown
._44
};
1740 * For convenience, overload FromUnknownMatrix() for Maybe<Matrix>.
1742 static Maybe
<Matrix4x4Typed
> FromUnknownMatrix(
1743 const Maybe
<UnknownMatrix
>& aUnknown
) {
1744 if (aUnknown
.isSome()) {
1745 return Some(FromUnknownMatrix(*aUnknown
));
1751 typedef Matrix4x4Typed
<UnknownUnits
, UnknownUnits
> Matrix4x4
;
1752 typedef Matrix4x4Typed
<UnknownUnits
, UnknownUnits
, double> Matrix4x4Double
;
1777 Matrix5x4(Float a11
, Float a12
, Float a13
, Float a14
, Float a21
, Float a22
,
1778 Float a23
, Float a24
, Float a31
, Float a32
, Float a33
, Float a34
,
1779 Float a41
, Float a42
, Float a43
, Float a44
, Float a51
, Float a52
,
1780 Float a53
, Float a54
)
1802 bool operator==(const Matrix5x4
& o
) const {
1803 return _11
== o
._11
&& _12
== o
._12
&& _13
== o
._13
&& _14
== o
._14
&&
1804 _21
== o
._21
&& _22
== o
._22
&& _23
== o
._23
&& _24
== o
._24
&&
1805 _31
== o
._31
&& _32
== o
._32
&& _33
== o
._33
&& _34
== o
._34
&&
1806 _41
== o
._41
&& _42
== o
._42
&& _43
== o
._43
&& _44
== o
._44
&&
1807 _51
== o
._51
&& _52
== o
._52
&& _53
== o
._53
&& _54
== o
._54
;
1810 bool operator!=(const Matrix5x4
& aMatrix
) const {
1811 return !(*this == aMatrix
);
1814 Matrix5x4
operator*(const Matrix5x4
& aMatrix
) const {
1815 Matrix5x4 resultMatrix
;
1817 resultMatrix
._11
= this->_11
* aMatrix
._11
+ this->_12
* aMatrix
._21
+
1818 this->_13
* aMatrix
._31
+ this->_14
* aMatrix
._41
;
1819 resultMatrix
._12
= this->_11
* aMatrix
._12
+ this->_12
* aMatrix
._22
+
1820 this->_13
* aMatrix
._32
+ this->_14
* aMatrix
._42
;
1821 resultMatrix
._13
= this->_11
* aMatrix
._13
+ this->_12
* aMatrix
._23
+
1822 this->_13
* aMatrix
._33
+ this->_14
* aMatrix
._43
;
1823 resultMatrix
._14
= this->_11
* aMatrix
._14
+ this->_12
* aMatrix
._24
+
1824 this->_13
* aMatrix
._34
+ this->_14
* aMatrix
._44
;
1825 resultMatrix
._21
= this->_21
* aMatrix
._11
+ this->_22
* aMatrix
._21
+
1826 this->_23
* aMatrix
._31
+ this->_24
* aMatrix
._41
;
1827 resultMatrix
._22
= this->_21
* aMatrix
._12
+ this->_22
* aMatrix
._22
+
1828 this->_23
* aMatrix
._32
+ this->_24
* aMatrix
._42
;
1829 resultMatrix
._23
= this->_21
* aMatrix
._13
+ this->_22
* aMatrix
._23
+
1830 this->_23
* aMatrix
._33
+ this->_24
* aMatrix
._43
;
1831 resultMatrix
._24
= this->_21
* aMatrix
._14
+ this->_22
* aMatrix
._24
+
1832 this->_23
* aMatrix
._34
+ this->_24
* aMatrix
._44
;
1833 resultMatrix
._31
= this->_31
* aMatrix
._11
+ this->_32
* aMatrix
._21
+
1834 this->_33
* aMatrix
._31
+ this->_34
* aMatrix
._41
;
1835 resultMatrix
._32
= this->_31
* aMatrix
._12
+ this->_32
* aMatrix
._22
+
1836 this->_33
* aMatrix
._32
+ this->_34
* aMatrix
._42
;
1837 resultMatrix
._33
= this->_31
* aMatrix
._13
+ this->_32
* aMatrix
._23
+
1838 this->_33
* aMatrix
._33
+ this->_34
* aMatrix
._43
;
1839 resultMatrix
._34
= this->_31
* aMatrix
._14
+ this->_32
* aMatrix
._24
+
1840 this->_33
* aMatrix
._34
+ this->_34
* aMatrix
._44
;
1841 resultMatrix
._41
= this->_41
* aMatrix
._11
+ this->_42
* aMatrix
._21
+
1842 this->_43
* aMatrix
._31
+ this->_44
* aMatrix
._41
;
1843 resultMatrix
._42
= this->_41
* aMatrix
._12
+ this->_42
* aMatrix
._22
+
1844 this->_43
* aMatrix
._32
+ this->_44
* aMatrix
._42
;
1845 resultMatrix
._43
= this->_41
* aMatrix
._13
+ this->_42
* aMatrix
._23
+
1846 this->_43
* aMatrix
._33
+ this->_44
* aMatrix
._43
;
1847 resultMatrix
._44
= this->_41
* aMatrix
._14
+ this->_42
* aMatrix
._24
+
1848 this->_43
* aMatrix
._34
+ this->_44
* aMatrix
._44
;
1849 resultMatrix
._51
= this->_51
* aMatrix
._11
+ this->_52
* aMatrix
._21
+
1850 this->_53
* aMatrix
._31
+ this->_54
* aMatrix
._41
+
1852 resultMatrix
._52
= this->_51
* aMatrix
._12
+ this->_52
* aMatrix
._22
+
1853 this->_53
* aMatrix
._32
+ this->_54
* aMatrix
._42
+
1855 resultMatrix
._53
= this->_51
* aMatrix
._13
+ this->_52
* aMatrix
._23
+
1856 this->_53
* aMatrix
._33
+ this->_54
* aMatrix
._43
+
1858 resultMatrix
._54
= this->_51
* aMatrix
._14
+ this->_52
* aMatrix
._24
+
1859 this->_53
* aMatrix
._34
+ this->_54
* aMatrix
._44
+
1862 return resultMatrix
;
1865 Matrix5x4
& operator*=(const Matrix5x4
& aMatrix
) {
1866 *this = *this * aMatrix
;
1870 friend std::ostream
& operator<<(std::ostream
& aStream
,
1871 const Matrix5x4
& aMatrix
) {
1872 const Float
* f
= &aMatrix
._11
;
1873 aStream
<< "[ " << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1875 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1877 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1879 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3] << ';';
1881 aStream
<< ' ' << f
[0] << ' ' << f
[1] << ' ' << f
[2] << ' ' << f
[3]
1888 Float _11
, _12
, _13
, _14
;
1889 Float _21
, _22
, _23
, _24
;
1890 Float _31
, _32
, _33
, _34
;
1891 Float _41
, _42
, _43
, _44
;
1892 Float _51
, _52
, _53
, _54
;
1894 Float components
[20];
1898 /* This Matrix class will carry one additional type field in order to
1899 * track what type of 4x4 matrix we're dealing with, it can then execute
1900 * simplified versions of certain operations when applicable.
1901 * This does not allow access to the parent class directly, as a caller
1902 * could then mutate the parent class without updating the type.
1904 template <typename SourceUnits
, typename TargetUnits
>
1905 class Matrix4x4TypedFlagged
1906 : protected Matrix4x4Typed
<SourceUnits
, TargetUnits
> {
1908 using Parent
= Matrix4x4Typed
<SourceUnits
, TargetUnits
>;
1909 using TargetPoint
= PointTyped
<TargetUnits
>;
1927 Matrix4x4TypedFlagged() : mType(MatrixType::Identity
) {}
1929 Matrix4x4TypedFlagged(Float a11
, Float a12
, Float a13
, Float a14
, Float a21
,
1930 Float a22
, Float a23
, Float a24
, Float a31
, Float a32
,
1931 Float a33
, Float a34
, Float a41
, Float a42
, Float a43
,
1933 : Parent(a11
, a12
, a13
, a14
, a21
, a22
, a23
, a24
, a31
, a32
, a33
, a34
, a41
,
1938 MOZ_IMPLICIT
Matrix4x4TypedFlagged(const Parent
& aOther
) : Parent(aOther
) {
1943 PointTyped
<TargetUnits
, F
> TransformPoint(
1944 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
1945 if (mType
== MatrixType::Identity
) {
1949 if (mType
== MatrixType::Simple
) {
1950 return TransformPointSimple(aPoint
);
1953 return Parent::TransformPoint(aPoint
);
1957 RectTyped
<TargetUnits
, F
> TransformAndClipBounds(
1958 const RectTyped
<SourceUnits
, F
>& aRect
,
1959 const RectTyped
<TargetUnits
, F
>& aClip
) const {
1960 if (mType
== MatrixType::Identity
) {
1961 const RectTyped
<SourceUnits
, F
>& clipped
= aRect
.Intersect(aClip
);
1962 return RectTyped
<TargetUnits
, F
>(clipped
.X(), clipped
.Y(),
1963 clipped
.Width(), clipped
.Height());
1966 if (mType
== MatrixType::Simple
) {
1967 PointTyped
<UnknownUnits
, F
> p1
= TransformPointSimple(aRect
.TopLeft());
1968 PointTyped
<UnknownUnits
, F
> p2
= TransformPointSimple(aRect
.TopRight());
1969 PointTyped
<UnknownUnits
, F
> p3
= TransformPointSimple(aRect
.BottomLeft());
1970 PointTyped
<UnknownUnits
, F
> p4
=
1971 TransformPointSimple(aRect
.BottomRight());
1973 F min_x
= std::min(std::min(std::min(p1
.x
, p2
.x
), p3
.x
), p4
.x
);
1974 F max_x
= std::max(std::max(std::max(p1
.x
, p2
.x
), p3
.x
), p4
.x
);
1975 F min_y
= std::min(std::min(std::min(p1
.y
, p2
.y
), p3
.y
), p4
.y
);
1976 F max_y
= std::max(std::max(std::max(p1
.y
, p2
.y
), p3
.y
), p4
.y
);
1978 TargetPoint
topLeft(std::max(min_x
, aClip
.x
), std::max(min_y
, aClip
.y
));
1979 F width
= std::min(max_x
, aClip
.XMost()) - topLeft
.x
;
1980 F height
= std::min(max_y
, aClip
.YMost()) - topLeft
.y
;
1982 return RectTyped
<TargetUnits
, F
>(topLeft
.x
, topLeft
.y
, width
, height
);
1984 return Parent::TransformAndClipBounds(aRect
, aClip
);
1987 bool FuzzyEqual(const Parent
& o
) const { return Parent::FuzzyEqual(o
); }
1989 bool FuzzyEqual(const Matrix4x4TypedFlagged
& o
) const {
1990 if (mType
== MatrixType::Identity
&& o
.mType
== MatrixType::Identity
) {
1993 return Parent::FuzzyEqual(o
);
1996 Matrix4x4TypedFlagged
& PreTranslate(Float aX
, Float aY
, Float aZ
) {
1997 if (mType
== MatrixType::Identity
) {
2003 mType
= MatrixType::Simple
;
2006 mType
= MatrixType::Full
;
2010 Parent::PreTranslate(aX
, aY
, aZ
);
2013 mType
= MatrixType::Full
;
2019 Matrix4x4TypedFlagged
& PostTranslate(Float aX
, Float aY
, Float aZ
) {
2020 if (mType
== MatrixType::Identity
) {
2026 mType
= MatrixType::Simple
;
2029 mType
= MatrixType::Full
;
2033 Parent::PostTranslate(aX
, aY
, aZ
);
2036 mType
= MatrixType::Full
;
2042 Matrix4x4TypedFlagged
& ChangeBasis(Float aX
, Float aY
, Float aZ
) {
2043 // Translate to the origin before applying this matrix
2044 PreTranslate(-aX
, -aY
, -aZ
);
2046 // Translate back into position after applying this matrix
2047 PostTranslate(aX
, aY
, aZ
);
2052 bool IsIdentity() const { return mType
== MatrixType::Identity
; }
2055 Point4DTyped
<TargetUnits
, F
> ProjectPoint(
2056 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
2057 if (mType
== MatrixType::Identity
) {
2058 return Point4DTyped
<TargetUnits
, F
>(aPoint
.x
, aPoint
.y
, 0, 1);
2061 if (mType
== MatrixType::Simple
) {
2062 TargetPoint point
= TransformPointSimple(aPoint
);
2063 return Point4DTyped
<TargetUnits
, F
>(point
.x
, point
.y
, 0, 1);
2066 return Parent::ProjectPoint(aPoint
);
2069 Matrix4x4TypedFlagged
& ProjectTo2D() {
2070 if (mType
== MatrixType::Full
) {
2071 Parent::ProjectTo2D();
2076 bool IsSingular() const {
2077 if (mType
== MatrixType::Identity
) {
2080 return Parent::Determinant() == 0.0;
2084 if (mType
== MatrixType::Identity
) {
2088 return Parent::Invert();
2091 Matrix4x4TypedFlagged
<TargetUnits
, SourceUnits
> Inverse() const {
2092 typedef Matrix4x4TypedFlagged
<TargetUnits
, SourceUnits
> InvertedMatrix
;
2093 InvertedMatrix clone
= InvertedMatrix::FromUnknownMatrix(ToUnknownMatrix());
2094 if (mType
== MatrixType::Identity
) {
2097 DebugOnly
<bool> inverted
= clone
.Invert();
2098 MOZ_ASSERT(inverted
,
2099 "Attempted to get the inverse of a non-invertible matrix");
2101 // Inverting a 2D Matrix should result in a 2D matrix, ergo mType doesn't
2106 template <typename NewTargetUnits
>
2108 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2109 if (mType
== MatrixType::Identity
&&
2110 aMatrix
.mType
== MatrixType::Identity
) {
2113 // Depending on the usage it may make sense to compare more flags.
2114 return Parent::operator==(aMatrix
);
2117 template <typename NewTargetUnits
>
2119 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2120 if (mType
== MatrixType::Identity
&&
2121 aMatrix
.mType
== MatrixType::Identity
) {
2124 // Depending on the usage it may make sense to compare more flags.
2125 return Parent::operator!=(aMatrix
);
2128 template <typename NewTargetUnits
>
2129 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> operator*(
2130 const Matrix4x4Typed
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2131 if (mType
== MatrixType::Identity
) {
2135 if (mType
== MatrixType::Simple
) {
2136 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> matrix
;
2137 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
;
2138 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
;
2139 matrix
._31
= aMatrix
._31
;
2140 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ aMatrix
._41
;
2141 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
;
2142 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
;
2143 matrix
._32
= aMatrix
._32
;
2144 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ aMatrix
._42
;
2145 matrix
._13
= _11
* aMatrix
._13
+ _12
* aMatrix
._23
;
2146 matrix
._23
= _21
* aMatrix
._13
+ _22
* aMatrix
._23
;
2147 matrix
._33
= aMatrix
._33
;
2148 matrix
._43
= _41
* aMatrix
._13
+ _42
* aMatrix
._23
+ aMatrix
._43
;
2149 matrix
._14
= _11
* aMatrix
._14
+ _12
* aMatrix
._24
;
2150 matrix
._24
= _21
* aMatrix
._14
+ _22
* aMatrix
._24
;
2151 matrix
._34
= aMatrix
._34
;
2152 matrix
._44
= _41
* aMatrix
._14
+ _42
* aMatrix
._24
+ aMatrix
._44
;
2157 return Parent::operator*(aMatrix
);
2160 template <typename NewTargetUnits
>
2161 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> operator*(
2162 const Matrix4x4TypedFlagged
<TargetUnits
, NewTargetUnits
>& aMatrix
) const {
2163 if (mType
== MatrixType::Identity
) {
2167 if (aMatrix
.mType
== MatrixType::Identity
) {
2168 return Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
>::
2169 FromUnknownMatrix(this->ToUnknownMatrix());
2172 if (mType
== MatrixType::Simple
&& aMatrix
.mType
== MatrixType::Simple
) {
2173 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> matrix
;
2174 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
;
2175 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
;
2176 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ aMatrix
._41
;
2177 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
;
2178 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
;
2179 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ aMatrix
._42
;
2180 matrix
.mType
= MatrixType::Simple
;
2182 } else 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
;
2200 matrix
.mType
= MatrixType::Full
;
2202 } else if (aMatrix
.mType
== MatrixType::Simple
) {
2203 Matrix4x4TypedFlagged
<SourceUnits
, NewTargetUnits
> matrix
;
2204 matrix
._11
= _11
* aMatrix
._11
+ _12
* aMatrix
._21
+ _14
* aMatrix
._41
;
2205 matrix
._21
= _21
* aMatrix
._11
+ _22
* aMatrix
._21
+ _24
* aMatrix
._41
;
2206 matrix
._31
= _31
* aMatrix
._11
+ _32
* aMatrix
._21
+ _34
* aMatrix
._41
;
2207 matrix
._41
= _41
* aMatrix
._11
+ _42
* aMatrix
._21
+ _44
* aMatrix
._41
;
2208 matrix
._12
= _11
* aMatrix
._12
+ _12
* aMatrix
._22
+ _14
* aMatrix
._42
;
2209 matrix
._22
= _21
* aMatrix
._12
+ _22
* aMatrix
._22
+ _24
* aMatrix
._42
;
2210 matrix
._32
= _31
* aMatrix
._12
+ _32
* aMatrix
._22
+ _34
* aMatrix
._42
;
2211 matrix
._42
= _41
* aMatrix
._12
+ _42
* aMatrix
._22
+ _44
* aMatrix
._42
;
2220 matrix
.mType
= MatrixType::Full
;
2224 return Parent::operator*(aMatrix
);
2227 bool Is2D() const { return mType
!= MatrixType::Full
; }
2229 bool CanDraw2D(Matrix
* aMatrix
= nullptr) const {
2230 if (mType
!= MatrixType::Full
) {
2241 return Parent::CanDraw2D(aMatrix
);
2244 bool Is2D(Matrix
* aMatrix
) const {
2260 RectTyped
<TargetUnits
, F
> ProjectRectBounds(
2261 const RectTyped
<SourceUnits
, F
>& aRect
,
2262 const RectTyped
<TargetUnits
, F
>& aClip
) const {
2263 return Parent::ProjectRectBounds(aRect
, aClip
);
2266 const Parent
& GetMatrix() const { return *this; }
2269 enum class MatrixType
: uint8_t {
2271 Simple
, // 2x3 Matrix
2275 Matrix4x4TypedFlagged(Float a11
, Float a12
, Float a13
, Float a14
, Float a21
,
2276 Float a22
, Float a23
, Float a24
, Float a31
, Float a32
,
2277 Float a33
, Float a34
, Float a41
, Float a42
, Float a43
,
2279 typename
Matrix4x4TypedFlagged::MatrixType aType
)
2280 : Parent(a11
, a12
, a13
, a14
, a21
, a22
, a23
, a24
, a31
, a32
, a33
, a34
, a41
,
2284 static Matrix4x4TypedFlagged
FromUnknownMatrix(
2285 const Matrix4x4Flagged
& aUnknown
) {
2286 return Matrix4x4TypedFlagged
{
2287 aUnknown
._11
, aUnknown
._12
, aUnknown
._13
, aUnknown
._14
, aUnknown
._21
,
2288 aUnknown
._22
, aUnknown
._23
, aUnknown
._24
, aUnknown
._31
, aUnknown
._32
,
2289 aUnknown
._33
, aUnknown
._34
, aUnknown
._41
, aUnknown
._42
, aUnknown
._43
,
2290 aUnknown
._44
, aUnknown
.mType
};
2292 Matrix4x4Flagged
ToUnknownMatrix() const {
2293 return Matrix4x4Flagged
{_11
, _12
, _13
, _14
, _21
, _22
, _23
, _24
, _31
,
2294 _32
, _33
, _34
, _41
, _42
, _43
, _44
, mType
};
2298 PointTyped
<TargetUnits
, F
> TransformPointSimple(
2299 const PointTyped
<SourceUnits
, F
>& aPoint
) const {
2300 PointTyped
<SourceUnits
, F
> temp
;
2301 temp
.x
= aPoint
.x
* _11
+ aPoint
.y
* +_21
+ _41
;
2302 temp
.y
= aPoint
.x
* _12
+ aPoint
.y
* +_22
+ _42
;
2307 if (Parent::IsIdentity()) {
2308 mType
= MatrixType::Identity
;
2312 if (Parent::Is2D()) {
2313 mType
= MatrixType::Simple
;
2317 mType
= MatrixType::Full
;
2323 using Matrix4x4Flagged
= Matrix4x4TypedFlagged
<UnknownUnits
, UnknownUnits
>;
2326 } // namespace mozilla
2328 #endif /* MOZILLA_GFX_MATRIX_H_ */