no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / gfx / 2d / Matrix.h
blob85de653a54485781d6c98c721d26e2413b180a02
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef MOZILLA_GFX_MATRIX_H_
8 #define MOZILLA_GFX_MATRIX_H_
10 #include "Types.h"
11 #include "Triangle.h"
12 #include "Rect.h"
13 #include "Point.h"
14 #include "Quaternion.h"
15 #include <iosfwd>
16 #include <math.h>
17 #include "mozilla/Attributes.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/FloatingPoint.h"
20 #include "mozilla/gfx/ScaleFactors2D.h"
21 #include "mozilla/Span.h"
23 namespace mozilla {
24 namespace gfx {
26 static inline bool FuzzyEqual(Float aV1, Float aV2) {
27 // XXX - Check if fabs does the smart thing and just negates the sign bit.
28 return fabs(aV2 - aV1) < 1e-6;
31 template <typename F>
32 Span<Point4DTyped<UnknownUnits, F>> IntersectPolygon(
33 Span<Point4DTyped<UnknownUnits, F>> aPoints,
34 const Point4DTyped<UnknownUnits, F>& aPlaneNormal,
35 Span<Point4DTyped<UnknownUnits, F>> aDestBuffer);
37 template <class T>
38 using BaseMatrixScales = BaseScaleFactors2D<UnknownUnits, UnknownUnits, T>;
40 using MatrixScales = BaseMatrixScales<float>;
41 using MatrixScalesDouble = BaseMatrixScales<double>;
43 template <class T>
44 class BaseMatrix {
45 // Alias that maps to either Point or PointDouble depending on whether T is a
46 // float or a double.
47 typedef PointTyped<UnknownUnits, T> MatrixPoint;
48 // Same for size and rect
49 typedef SizeTyped<UnknownUnits, T> MatrixSize;
50 typedef RectTyped<UnknownUnits, T> MatrixRect;
52 public:
53 BaseMatrix() : _11(1.0f), _12(0), _21(0), _22(1.0f), _31(0), _32(0) {}
54 BaseMatrix(T a11, T a12, T a21, T a22, T a31, T a32)
55 : _11(a11), _12(a12), _21(a21), _22(a22), _31(a31), _32(a32) {}
56 union {
57 struct {
58 T _11, _12;
59 T _21, _22;
60 T _31, _32;
62 T components[6];
65 template <class T2>
66 explicit BaseMatrix(const BaseMatrix<T2>& aOther)
67 : _11(aOther._11),
68 _12(aOther._12),
69 _21(aOther._21),
70 _22(aOther._22),
71 _31(aOther._31),
72 _32(aOther._32) {}
74 MOZ_ALWAYS_INLINE BaseMatrix Copy() const { return BaseMatrix<T>(*this); }
76 friend std::ostream& operator<<(std::ostream& aStream,
77 const BaseMatrix& aMatrix) {
78 if (aMatrix.IsIdentity()) {
79 return aStream << "[ I ]";
81 return aStream << "[ " << aMatrix._11 << " " << aMatrix._12 << "; "
82 << aMatrix._21 << " " << aMatrix._22 << "; " << aMatrix._31
83 << " " << aMatrix._32 << "; ]";
86 MatrixPoint TransformPoint(const MatrixPoint& aPoint) const {
87 MatrixPoint retPoint;
89 retPoint.x = aPoint.x * _11 + aPoint.y * _21 + _31;
90 retPoint.y = aPoint.x * _12 + aPoint.y * _22 + _32;
92 return retPoint;
95 MatrixSize TransformSize(const MatrixSize& aSize) const {
96 MatrixSize retSize;
98 retSize.width = aSize.width * _11 + aSize.height * _21;
99 retSize.height = aSize.width * _12 + aSize.height * _22;
101 return retSize;
105 * In most cases you probably want to use TransformBounds. This function
106 * just transforms the top-left and size separately and constructs a rect
107 * from those results.
109 MatrixRect TransformRect(const MatrixRect& aRect) const {
110 return MatrixRect(TransformPoint(aRect.TopLeft()),
111 TransformSize(aRect.Size()));
114 GFX2D_API MatrixRect TransformBounds(const MatrixRect& aRect) const {
115 int i;
116 MatrixPoint quad[4];
117 T min_x, max_x;
118 T min_y, max_y;
120 quad[0] = TransformPoint(aRect.TopLeft());
121 quad[1] = TransformPoint(aRect.TopRight());
122 quad[2] = TransformPoint(aRect.BottomLeft());
123 quad[3] = TransformPoint(aRect.BottomRight());
125 min_x = max_x = quad[0].x;
126 min_y = max_y = quad[0].y;
128 for (i = 1; i < 4; i++) {
129 if (quad[i].x < min_x) min_x = quad[i].x;
130 if (quad[i].x > max_x) max_x = quad[i].x;
132 if (quad[i].y < min_y) min_y = quad[i].y;
133 if (quad[i].y > max_y) max_y = quad[i].y;
136 return MatrixRect(min_x, min_y, max_x - min_x, max_y - min_y);
139 static BaseMatrix<T> Translation(T aX, T aY) {
140 return BaseMatrix<T>(1.0f, 0.0f, 0.0f, 1.0f, aX, aY);
143 static BaseMatrix<T> Translation(MatrixPoint aPoint) {
144 return Translation(aPoint.x, aPoint.y);
148 * Apply a translation to this matrix.
150 * The "Pre" in this method's name means that the translation is applied
151 * -before- this matrix's existing transformation. That is, any vector that
152 * is multiplied by the resulting matrix will first be translated, then be
153 * transformed by the original transform.
155 * Calling this method will result in this matrix having the same value as
156 * the result of:
158 * BaseMatrix<T>::Translation(x, y) * this
160 * (Note that in performance critical code multiplying by the result of a
161 * Translation()/Scaling() call is not recommended since that results in a
162 * full matrix multiply involving 12 floating-point multiplications. Calling
163 * this method would be preferred since it only involves four floating-point
164 * multiplications.)
166 BaseMatrix<T>& PreTranslate(T aX, T aY) {
167 _31 += _11 * aX + _21 * aY;
168 _32 += _12 * aX + _22 * aY;
170 return *this;
173 BaseMatrix<T>& PreTranslate(const MatrixPoint& aPoint) {
174 return PreTranslate(aPoint.x, aPoint.y);
178 * Similar to PreTranslate, but the translation is applied -after- this
179 * matrix's existing transformation instead of before it.
181 * This method is generally less used than PreTranslate since typically code
182 * want to adjust an existing user space to device space matrix to create a
183 * transform to device space from a -new- user space (translated from the
184 * previous user space). In that case consumers will need to use the Pre*
185 * variants of the matrix methods rather than using the Post* methods, since
186 * the Post* methods add a transform to the device space end of the
187 * transformation.
189 BaseMatrix<T>& PostTranslate(T aX, T aY) {
190 _31 += aX;
191 _32 += aY;
192 return *this;
195 BaseMatrix<T>& PostTranslate(const MatrixPoint& aPoint) {
196 return PostTranslate(aPoint.x, aPoint.y);
199 static BaseMatrix<T> Scaling(T aScaleX, T aScaleY) {
200 return BaseMatrix<T>(aScaleX, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f);
203 static BaseMatrix<T> Scaling(const BaseMatrixScales<T>& scale) {
204 return Scaling(scale.xScale, scale.yScale);
208 * Similar to PreTranslate, but applies a scale instead of a translation.
210 BaseMatrix<T>& PreScale(T aX, T aY) {
211 _11 *= aX;
212 _12 *= aX;
213 _21 *= aY;
214 _22 *= aY;
216 return *this;
219 BaseMatrix<T>& PreScale(const BaseMatrixScales<T>& scale) {
220 return PreScale(scale.xScale, scale.yScale);
224 * Similar to PostTranslate, but applies a scale instead of a translation.
226 BaseMatrix<T>& PostScale(T aScaleX, T aScaleY) {
227 _11 *= aScaleX;
228 _12 *= aScaleY;
229 _21 *= aScaleX;
230 _22 *= aScaleY;
231 _31 *= aScaleX;
232 _32 *= aScaleY;
234 return *this;
237 GFX2D_API static BaseMatrix<T> Rotation(T aAngle);
240 * Similar to PreTranslate, but applies a rotation instead of a translation.
242 BaseMatrix<T>& PreRotate(T aAngle) {
243 return *this = BaseMatrix<T>::Rotation(aAngle) * *this;
246 bool Invert() {
247 // Compute co-factors.
248 T A = _22;
249 T B = -_21;
250 T C = _21 * _32 - _22 * _31;
251 T D = -_12;
252 T E = _11;
253 T F = _31 * _12 - _11 * _32;
255 T det = Determinant();
257 if (!det) {
258 return false;
261 T inv_det = 1 / det;
263 _11 = inv_det * A;
264 _12 = inv_det * D;
265 _21 = inv_det * B;
266 _22 = inv_det * E;
267 _31 = inv_det * C;
268 _32 = inv_det * F;
270 return true;
273 BaseMatrix<T> Inverse() const {
274 BaseMatrix<T> clone = *this;
275 DebugOnly<bool> inverted = clone.Invert();
276 MOZ_ASSERT(inverted,
277 "Attempted to get the inverse of a non-invertible matrix");
278 return clone;
281 T Determinant() const { return _11 * _22 - _12 * _21; }
283 BaseMatrix<T> operator*(const BaseMatrix<T>& aMatrix) const {
284 BaseMatrix<T> resultMatrix;
286 resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21;
287 resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22;
288 resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21;
289 resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22;
290 resultMatrix._31 =
291 this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._31;
292 resultMatrix._32 =
293 this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._32;
295 return resultMatrix;
298 BaseMatrix<T>& operator*=(const BaseMatrix<T>& aMatrix) {
299 *this = *this * aMatrix;
300 return *this;
304 * Multiplies *this with aMatrix and returns the result.
306 Matrix4x4 operator*(const Matrix4x4& aMatrix) const;
309 * Multiplies in the opposite order to operator=*.
311 BaseMatrix<T>& PreMultiply(const BaseMatrix<T>& aMatrix) {
312 *this = aMatrix * *this;
313 return *this;
317 * Please explicitly use either FuzzyEquals or ExactlyEquals.
319 bool operator==(const BaseMatrix<T>& other) const = delete;
320 bool operator!=(const BaseMatrix<T>& other) const = delete;
322 /* Returns true if the other matrix is fuzzy-equal to this matrix.
323 * Note that this isn't a cheap comparison!
325 bool FuzzyEquals(const BaseMatrix<T>& o) const {
326 return FuzzyEqual(_11, o._11) && FuzzyEqual(_12, o._12) &&
327 FuzzyEqual(_21, o._21) && FuzzyEqual(_22, o._22) &&
328 FuzzyEqual(_31, o._31) && FuzzyEqual(_32, o._32);
331 bool ExactlyEquals(const BaseMatrix<T>& o) const {
332 return _11 == o._11 && _12 == o._12 && _21 == o._21 && _22 == o._22 &&
333 _31 == o._31 && _32 == o._32;
336 /* Verifies that the matrix contains no Infs or NaNs. */
337 bool IsFinite() const {
338 return std::isfinite(_11) && std::isfinite(_12) && std::isfinite(_21) &&
339 std::isfinite(_22) && std::isfinite(_31) && std::isfinite(_32);
342 /* Returns true if the matrix is a rectilinear transformation (i.e.
343 * grid-aligned rectangles are transformed to grid-aligned rectangles)
345 bool IsRectilinear() const {
346 if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) {
347 return true;
348 } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) {
349 return true;
352 return false;
356 * Returns true if the matrix is anything other than a straight
357 * translation by integers.
359 bool HasNonIntegerTranslation() const {
360 return HasNonTranslation() || !FuzzyEqual(_31, floor(_31 + 0.5f)) ||
361 !FuzzyEqual(_32, floor(_32 + 0.5f));
365 * Returns true if the matrix only has an integer translation.
367 bool HasOnlyIntegerTranslation() const { return !HasNonIntegerTranslation(); }
370 * Returns true if the matrix has any transform other
371 * than a straight translation.
373 bool HasNonTranslation() const {
374 return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) ||
375 !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0);
379 * Returns true if the matrix has any transform other
380 * than a translation or a -1 y scale (y axis flip)
382 bool HasNonTranslationOrFlip() const {
383 return !FuzzyEqual(_11, 1.0) ||
384 (!FuzzyEqual(_22, 1.0) && !FuzzyEqual(_22, -1.0)) ||
385 !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
388 /* Returns true if the matrix is an identity matrix.
390 bool IsIdentity() const {
391 return _11 == 1.0f && _12 == 0.0f && _21 == 0.0f && _22 == 1.0f &&
392 _31 == 0.0f && _32 == 0.0f;
395 /* Returns true if the matrix is singular.
397 bool IsSingular() const {
398 T det = Determinant();
399 return !std::isfinite(det) || det == 0;
402 GFX2D_API BaseMatrix<T>& NudgeToIntegers() {
403 NudgeToInteger(&_11);
404 NudgeToInteger(&_12);
405 NudgeToInteger(&_21);
406 NudgeToInteger(&_22);
407 NudgeToInteger(&_31);
408 NudgeToInteger(&_32);
409 return *this;
412 bool IsTranslation() const {
413 return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) &&
414 FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f);
417 static bool FuzzyIsInteger(T aValue) {
418 return FuzzyEqual(aValue, floorf(aValue + 0.5f));
421 bool IsIntegerTranslation() const {
422 return IsTranslation() && FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
425 bool IsAllIntegers() const {
426 return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) && FuzzyIsInteger(_21) &&
427 FuzzyIsInteger(_22) && FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
430 MatrixPoint GetTranslation() const { return MatrixPoint(_31, _32); }
433 * Returns true if matrix is multiple of 90 degrees rotation with flipping,
434 * scaling and translation.
436 bool PreservesAxisAlignedRectangles() const {
437 return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0)) ||
438 (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0)));
442 * Returns true if the matrix has any transform other
443 * than a translation or scale; this is, if there is
444 * rotation.
446 bool HasNonAxisAlignedTransform() const {
447 return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
451 * Returns true if the matrix has negative scaling (i.e. flip).
453 bool HasNegativeScaling() const { return (_11 < 0.0) || (_22 < 0.0); }
456 * Computes the scale factors of this matrix; that is,
457 * the amounts each basis vector is scaled by.
459 BaseMatrixScales<T> ScaleFactors() const {
460 T det = Determinant();
462 if (det == 0.0) {
463 return BaseMatrixScales<T>(0.0, 0.0);
466 MatrixSize sz = MatrixSize(1.0, 0.0);
467 sz = TransformSize(sz);
469 T major = sqrt(sz.width * sz.width + sz.height * sz.height);
470 T minor = 0.0;
472 // ignore mirroring
473 if (det < 0.0) {
474 det = -det;
477 if (major) {
478 minor = det / major;
481 return BaseMatrixScales<T>(major, minor);
485 * Returns true if the matrix preserves distances, i.e. a rigid transformation
486 * that doesn't change size or shape). Such a matrix has uniform unit scaling
487 * and an orthogonal basis.
489 bool PreservesDistance() const {
490 return FuzzyEqual(_11 * _11 + _12 * _12, 1.0) &&
491 FuzzyEqual(_21 * _21 + _22 * _22, 1.0) &&
492 FuzzyEqual(_11 * _21 + _12 * _22, 0.0);
496 typedef BaseMatrix<Float> Matrix;
497 typedef BaseMatrix<Double> MatrixDouble;
499 // Helper functions used by Matrix4x4Typed defined in Matrix.cpp
500 double SafeTangent(double aTheta);
501 double FlushToZero(double aVal);
503 template <class Units, class F>
504 Point4DTyped<Units, F> ComputePerspectivePlaneIntercept(
505 const Point4DTyped<Units, F>& aFirst,
506 const Point4DTyped<Units, F>& aSecond) {
507 // This function will always return a point with a w value of 0.
508 // The X, Y, and Z components will point towards an infinite vanishing
509 // point.
511 // We want to interpolate aFirst and aSecond to find the point intersecting
512 // with the w=0 plane.
514 // Since we know what we want the w component to be, we can rearrange the
515 // interpolation equation and solve for t.
516 float t = -aFirst.w / (aSecond.w - aFirst.w);
518 // Use t to find the remainder of the components
519 return aFirst + (aSecond - aFirst) * t;
522 template <class SourceUnits, class TargetUnits, class T>
523 class Matrix4x4Typed {
524 public:
525 typedef PointTyped<SourceUnits, T> SourcePoint;
526 typedef PointTyped<TargetUnits, T> TargetPoint;
527 typedef Point3DTyped<SourceUnits, T> SourcePoint3D;
528 typedef Point3DTyped<TargetUnits, T> TargetPoint3D;
529 typedef Point4DTyped<SourceUnits, T> SourcePoint4D;
530 typedef Point4DTyped<TargetUnits, T> TargetPoint4D;
531 typedef RectTyped<SourceUnits, T> SourceRect;
532 typedef RectTyped<TargetUnits, T> TargetRect;
534 Matrix4x4Typed()
535 : _11(1.0f),
536 _12(0.0f),
537 _13(0.0f),
538 _14(0.0f),
539 _21(0.0f),
540 _22(1.0f),
541 _23(0.0f),
542 _24(0.0f),
543 _31(0.0f),
544 _32(0.0f),
545 _33(1.0f),
546 _34(0.0f),
547 _41(0.0f),
548 _42(0.0f),
549 _43(0.0f),
550 _44(1.0f) {}
552 Matrix4x4Typed(T a11, T a12, T a13, T a14, T a21, T a22, T a23, T a24, T a31,
553 T a32, T a33, T a34, T a41, T a42, T a43, T a44)
554 : _11(a11),
555 _12(a12),
556 _13(a13),
557 _14(a14),
558 _21(a21),
559 _22(a22),
560 _23(a23),
561 _24(a24),
562 _31(a31),
563 _32(a32),
564 _33(a33),
565 _34(a34),
566 _41(a41),
567 _42(a42),
568 _43(a43),
569 _44(a44) {}
571 explicit Matrix4x4Typed(const T aArray[16]) {
572 memcpy(components, aArray, sizeof(components));
575 Matrix4x4Typed(const Matrix4x4Typed& aOther) {
576 memcpy(components, aOther.components, sizeof(components));
579 template <class T2>
580 explicit Matrix4x4Typed(
581 const Matrix4x4Typed<SourceUnits, TargetUnits, T2>& aOther)
582 : _11(aOther._11),
583 _12(aOther._12),
584 _13(aOther._13),
585 _14(aOther._14),
586 _21(aOther._21),
587 _22(aOther._22),
588 _23(aOther._23),
589 _24(aOther._24),
590 _31(aOther._31),
591 _32(aOther._32),
592 _33(aOther._33),
593 _34(aOther._34),
594 _41(aOther._41),
595 _42(aOther._42),
596 _43(aOther._43),
597 _44(aOther._44) {}
599 union {
600 struct {
601 T _11, _12, _13, _14;
602 T _21, _22, _23, _24;
603 T _31, _32, _33, _34;
604 T _41, _42, _43, _44;
606 T components[16];
609 friend std::ostream& operator<<(std::ostream& aStream,
610 const Matrix4x4Typed& aMatrix) {
611 if (aMatrix.Is2D()) {
612 BaseMatrix<T> matrix = aMatrix.As2D();
613 return aStream << matrix;
615 const T* f = &aMatrix._11;
616 aStream << "[ " << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
617 f += 4;
618 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
619 f += 4;
620 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
621 f += 4;
622 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3]
623 << "; ]";
624 return aStream;
627 Point4DTyped<UnknownUnits, T>& operator[](int aIndex) {
628 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
629 return *reinterpret_cast<Point4DTyped<UnknownUnits, T>*>((&_11) +
630 4 * aIndex);
632 const Point4DTyped<UnknownUnits, T>& operator[](int aIndex) const {
633 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
634 return *reinterpret_cast<const Point4DTyped<UnknownUnits, T>*>((&_11) +
635 4 * aIndex);
638 // External code should avoid calling this, and instead use
639 // ViewAs() from UnitTransforms.h, which requires providing
640 // a justification.
641 template <typename NewMatrix4x4Typed>
642 [[nodiscard]] NewMatrix4x4Typed Cast() const {
643 return NewMatrix4x4Typed(_11, _12, _13, _14, _21, _22, _23, _24, _31, _32,
644 _33, _34, _41, _42, _43, _44);
648 * Returns true if the matrix is isomorphic to a 2D affine transformation.
650 bool Is2D() const {
651 if (_13 != 0.0f || _14 != 0.0f || _23 != 0.0f || _24 != 0.0f ||
652 _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f ||
653 _43 != 0.0f || _44 != 1.0f) {
654 return false;
656 return true;
659 bool Is2D(BaseMatrix<T>* aMatrix) const {
660 if (!Is2D()) {
661 return false;
663 if (aMatrix) {
664 aMatrix->_11 = _11;
665 aMatrix->_12 = _12;
666 aMatrix->_21 = _21;
667 aMatrix->_22 = _22;
668 aMatrix->_31 = _41;
669 aMatrix->_32 = _42;
671 return true;
674 BaseMatrix<T> As2D() const {
675 MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform");
677 return BaseMatrix<T>(_11, _12, _21, _22, _41, _42);
680 bool CanDraw2D(BaseMatrix<T>* aMatrix = nullptr) const {
681 if (_14 != 0.0f || _24 != 0.0f || _44 != 1.0f) {
682 return false;
684 if (aMatrix) {
685 aMatrix->_11 = _11;
686 aMatrix->_12 = _12;
687 aMatrix->_21 = _21;
688 aMatrix->_22 = _22;
689 aMatrix->_31 = _41;
690 aMatrix->_32 = _42;
692 return true;
695 Matrix4x4Typed& ProjectTo2D() {
696 _31 = 0.0f;
697 _32 = 0.0f;
698 _13 = 0.0f;
699 _23 = 0.0f;
700 _33 = 1.0f;
701 _43 = 0.0f;
702 _34 = 0.0f;
703 // Some matrices, such as those derived from perspective transforms,
704 // can modify _44 from 1, while leaving the rest of the fourth column
705 // (_14, _24) at 0. In this case, after resetting the third row and
706 // third column above, the value of _44 functions only to scale the
707 // coordinate transform divide by W. The matrix can be converted to
708 // a true 2D matrix by normalizing out the scaling effect of _44 on
709 // the remaining components ahead of time.
710 if (_14 == 0.0f && _24 == 0.0f && _44 != 1.0f && _44 != 0.0f) {
711 T scale = 1.0f / _44;
712 _11 *= scale;
713 _12 *= scale;
714 _21 *= scale;
715 _22 *= scale;
716 _41 *= scale;
717 _42 *= scale;
718 _44 = 1.0f;
720 return *this;
723 template <class F>
724 Point4DTyped<TargetUnits, F> ProjectPoint(
725 const PointTyped<SourceUnits, F>& aPoint) const {
726 // Find a value for z that will transform to 0.
728 // The transformed value of z is computed as:
729 // z' = aPoint.x * _13 + aPoint.y * _23 + z * _33 + _43;
731 // Solving for z when z' = 0 gives us:
732 F z = -(aPoint.x * _13 + aPoint.y * _23 + _43) / _33;
734 // Compute the transformed point
735 return this->TransformPoint(
736 Point4DTyped<SourceUnits, F>(aPoint.x, aPoint.y, z, 1));
739 template <class F>
740 RectTyped<TargetUnits, F> ProjectRectBounds(
741 const RectTyped<SourceUnits, F>& aRect,
742 const RectTyped<TargetUnits, F>& aClip) const {
743 // This function must never return std::numeric_limits<Float>::max() or any
744 // other arbitrary large value in place of inifinity. This often occurs
745 // when aRect is an inversed projection matrix or when aRect is transformed
746 // to be partly behind and in front of the camera (w=0 plane in homogenous
747 // coordinates) - See Bug 1035611
749 // Some call-sites will call RoundGfxRectToAppRect which clips both the
750 // extents and dimensions of the rect to be bounded by nscoord_MAX.
751 // If we return a Rect that, when converted to nscoords, has a width or
752 // height greater than nscoord_MAX, RoundGfxRectToAppRect will clip the
753 // overflow off both the min and max end of the rect after clipping the
754 // extents of the rect, resulting in a translation of the rect towards the
755 // infinite end.
757 // The bounds returned by ProjectRectBounds are expected to be clipped only
758 // on the edges beyond the bounds of the coordinate system; otherwise, the
759 // clipped bounding box would be smaller than the correct one and result
760 // bugs such as incorrect culling (eg. Bug 1073056)
762 // To address this without requiring all code to work in homogenous
763 // coordinates or interpret infinite values correctly, a specialized
764 // clipping function is integrated into ProjectRectBounds.
766 // Callers should pass an aClip value that represents the extents to clip
767 // the result to, in the same coordinate system as aRect.
768 Point4DTyped<TargetUnits, F> points[4];
770 points[0] = ProjectPoint(aRect.TopLeft());
771 points[1] = ProjectPoint(aRect.TopRight());
772 points[2] = ProjectPoint(aRect.BottomRight());
773 points[3] = ProjectPoint(aRect.BottomLeft());
775 F min_x = std::numeric_limits<F>::max();
776 F min_y = std::numeric_limits<F>::max();
777 F max_x = -std::numeric_limits<F>::max();
778 F max_y = -std::numeric_limits<F>::max();
780 for (int i = 0; i < 4; i++) {
781 // Only use points that exist above the w=0 plane
782 if (points[i].HasPositiveWCoord()) {
783 PointTyped<TargetUnits, F> point2d =
784 aClip.ClampPoint(points[i].As2DPoint());
785 min_x = std::min<F>(point2d.x, min_x);
786 max_x = std::max<F>(point2d.x, max_x);
787 min_y = std::min<F>(point2d.y, min_y);
788 max_y = std::max<F>(point2d.y, max_y);
791 int next = (i == 3) ? 0 : i + 1;
792 if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) {
793 // If the line between two points crosses the w=0 plane, then
794 // interpolate to find the point of intersection with the w=0 plane and
795 // use that instead.
796 Point4DTyped<TargetUnits, F> intercept =
797 ComputePerspectivePlaneIntercept(points[i], points[next]);
798 // Since intercept.w will always be 0 here, we interpret x,y,z as a
799 // direction towards an infinite vanishing point.
800 if (intercept.x < 0.0f) {
801 min_x = aClip.X();
802 } else if (intercept.x > 0.0f) {
803 max_x = aClip.XMost();
805 if (intercept.y < 0.0f) {
806 min_y = aClip.Y();
807 } else if (intercept.y > 0.0f) {
808 max_y = aClip.YMost();
813 if (max_x < min_x || max_y < min_y) {
814 return RectTyped<TargetUnits, F>(0, 0, 0, 0);
817 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
818 max_y - min_y);
822 * TransformAndClipBounds transforms aRect as a bounding box, while clipping
823 * the transformed bounds to the extents of aClip.
825 template <class F>
826 RectTyped<TargetUnits, F> TransformAndClipBounds(
827 const RectTyped<SourceUnits, F>& aRect,
828 const RectTyped<TargetUnits, F>& aClip) const {
829 PointTyped<UnknownUnits, F> verts[kTransformAndClipRectMaxVerts];
830 size_t vertCount = TransformAndClipRect(aRect, aClip, verts);
832 F min_x = std::numeric_limits<F>::max();
833 F min_y = std::numeric_limits<F>::max();
834 F max_x = -std::numeric_limits<F>::max();
835 F max_y = -std::numeric_limits<F>::max();
836 for (size_t i = 0; i < vertCount; i++) {
837 min_x = std::min(min_x, verts[i].x.value);
838 max_x = std::max(max_x, verts[i].x.value);
839 min_y = std::min(min_y, verts[i].y.value);
840 max_y = std::max(max_y, verts[i].y.value);
843 if (max_x < min_x || max_y < min_y) {
844 return RectTyped<TargetUnits, F>(0, 0, 0, 0);
847 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
848 max_y - min_y);
851 template <class F>
852 RectTyped<TargetUnits, F> TransformAndClipBounds(
853 const TriangleTyped<SourceUnits, F>& aTriangle,
854 const RectTyped<TargetUnits, F>& aClip) const {
855 return TransformAndClipBounds(aTriangle.BoundingBox(), aClip);
859 * TransformAndClipRect projects a rectangle and clips against view frustum
860 * clipping planes in homogenous space so that its projected vertices are
861 * constrained within the 2d rectangle passed in aClip.
862 * The resulting vertices are populated in aVerts. aVerts must be
863 * pre-allocated to hold at least kTransformAndClipRectMaxVerts Points.
864 * The vertex count is returned by TransformAndClipRect. It is possible to
865 * emit fewer than 3 vertices, indicating that aRect will not be visible
866 * within aClip.
868 template <class F>
869 size_t TransformAndClipRect(const RectTyped<SourceUnits, F>& aRect,
870 const RectTyped<TargetUnits, F>& aClip,
871 PointTyped<TargetUnits, F>* aVerts) const {
872 typedef Point4DTyped<UnknownUnits, F> P4D;
874 // The initial polygon is made up by the corners of aRect in homogenous
875 // space, mapped into the destination space of this transform.
876 P4D rectCorners[] = {
877 TransformPoint(P4D(aRect.X(), aRect.Y(), 0, 1)),
878 TransformPoint(P4D(aRect.XMost(), aRect.Y(), 0, 1)),
879 TransformPoint(P4D(aRect.XMost(), aRect.YMost(), 0, 1)),
880 TransformPoint(P4D(aRect.X(), aRect.YMost(), 0, 1)),
883 // Cut off pieces of the polygon that are outside of aClip (the "view
884 // frustrum"), by consecutively intersecting the polygon with the half space
885 // induced by the clipping plane for each side of aClip.
886 // View frustum clipping planes are described as normals originating from
887 // the 0,0,0,0 origin.
888 // Each pass can increase or decrease the number of points that make up the
889 // current clipped polygon. We double buffer the set of points, alternating
890 // between polygonBufA and polygonBufB. Duplicated points in the polygons
891 // are kept around until all clipping is done. The loop at the end filters
892 // out any consecutive duplicates.
893 P4D polygonBufA[kTransformAndClipRectMaxVerts];
894 P4D polygonBufB[kTransformAndClipRectMaxVerts];
896 Span<P4D> polygon(rectCorners);
897 polygon = IntersectPolygon<F>(polygon, P4D(1.0, 0.0, 0.0, -aClip.X()),
898 polygonBufA);
899 polygon = IntersectPolygon<F>(polygon, P4D(-1.0, 0.0, 0.0, aClip.XMost()),
900 polygonBufB);
901 polygon = IntersectPolygon<F>(polygon, P4D(0.0, 1.0, 0.0, -aClip.Y()),
902 polygonBufA);
903 polygon = IntersectPolygon<F>(polygon, P4D(0.0, -1.0, 0.0, aClip.YMost()),
904 polygonBufB);
906 size_t vertCount = 0;
907 for (const auto& srcPoint : polygon) {
908 PointTyped<TargetUnits, F> p;
909 if (srcPoint.w == 0.0) {
910 // If a point lies on the intersection of the clipping planes at
911 // (0,0,0,0), we must avoid a division by zero w component.
912 p = PointTyped<TargetUnits, F>(0.0, 0.0);
913 } else {
914 p = srcPoint.As2DPoint();
916 // Emit only unique points
917 if (vertCount == 0 || p != aVerts[vertCount - 1]) {
918 aVerts[vertCount++] = p;
922 return vertCount;
925 static const int kTransformAndClipRectMaxVerts = 32;
927 static Matrix4x4Typed From2D(const BaseMatrix<T>& aMatrix) {
928 Matrix4x4Typed matrix;
929 matrix._11 = aMatrix._11;
930 matrix._12 = aMatrix._12;
931 matrix._21 = aMatrix._21;
932 matrix._22 = aMatrix._22;
933 matrix._41 = aMatrix._31;
934 matrix._42 = aMatrix._32;
935 return matrix;
938 bool Is2DIntegerTranslation() const {
939 return Is2D() && As2D().IsIntegerTranslation();
942 TargetPoint4D TransposeTransform4D(const SourcePoint4D& aPoint) const {
943 Float x = aPoint.x * _11 + aPoint.y * _12 + aPoint.z * _13 + aPoint.w * _14;
944 Float y = aPoint.x * _21 + aPoint.y * _22 + aPoint.z * _23 + aPoint.w * _24;
945 Float z = aPoint.x * _31 + aPoint.y * _32 + aPoint.z * _33 + aPoint.w * _34;
946 Float w = aPoint.x * _41 + aPoint.y * _42 + aPoint.z * _43 + aPoint.w * _44;
948 return TargetPoint4D(x, y, z, w);
951 template <class F>
952 Point4DTyped<TargetUnits, F> TransformPoint(
953 const Point4DTyped<SourceUnits, F>& aPoint) const {
954 Point4DTyped<TargetUnits, F> retPoint;
956 retPoint.x =
957 aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + aPoint.w * _41;
958 retPoint.y =
959 aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + aPoint.w * _42;
960 retPoint.z =
961 aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + aPoint.w * _43;
962 retPoint.w =
963 aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + aPoint.w * _44;
965 return retPoint;
968 template <class F>
969 Point3DTyped<TargetUnits, F> TransformPoint(
970 const Point3DTyped<SourceUnits, F>& aPoint) const {
971 Point3DTyped<TargetUnits, F> result;
972 result.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41;
973 result.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42;
974 result.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43;
976 result /= (aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44);
978 return result;
981 template <class F>
982 PointTyped<TargetUnits, F> TransformPoint(
983 const PointTyped<SourceUnits, F>& aPoint) const {
984 Point4DTyped<SourceUnits, F> temp(aPoint.x, aPoint.y, 0, 1);
985 return TransformPoint(temp).As2DPoint();
988 template <class F>
989 GFX2D_API RectTyped<TargetUnits, F> TransformBounds(
990 const RectTyped<SourceUnits, F>& aRect) const {
991 PointTyped<TargetUnits, F> quad[4];
992 F min_x, max_x;
993 F min_y, max_y;
995 quad[0] = TransformPoint(aRect.TopLeft());
996 quad[1] = TransformPoint(aRect.TopRight());
997 quad[2] = TransformPoint(aRect.BottomLeft());
998 quad[3] = TransformPoint(aRect.BottomRight());
1000 min_x = max_x = quad[0].x;
1001 min_y = max_y = quad[0].y;
1003 for (int i = 1; i < 4; i++) {
1004 if (quad[i].x < min_x) {
1005 min_x = quad[i].x;
1007 if (quad[i].x > max_x) {
1008 max_x = quad[i].x;
1011 if (quad[i].y < min_y) {
1012 min_y = quad[i].y;
1014 if (quad[i].y > max_y) {
1015 max_y = quad[i].y;
1019 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
1020 max_y - min_y);
1023 static Matrix4x4Typed Translation(T aX, T aY, T aZ) {
1024 return Matrix4x4Typed(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1025 0.0f, 1.0f, 0.0f, aX, aY, aZ, 1.0f);
1028 static Matrix4x4Typed Translation(const TargetPoint3D& aP) {
1029 return Translation(aP.x, aP.y, aP.z);
1032 static Matrix4x4Typed Translation(const TargetPoint& aP) {
1033 return Translation(aP.x, aP.y, 0);
1037 * Apply a translation to this matrix.
1039 * The "Pre" in this method's name means that the translation is applied
1040 * -before- this matrix's existing transformation. That is, any vector that
1041 * is multiplied by the resulting matrix will first be translated, then be
1042 * transformed by the original transform.
1044 * Calling this method will result in this matrix having the same value as
1045 * the result of:
1047 * Matrix4x4::Translation(x, y) * this
1049 * (Note that in performance critical code multiplying by the result of a
1050 * Translation()/Scaling() call is not recommended since that results in a
1051 * full matrix multiply involving 64 floating-point multiplications. Calling
1052 * this method would be preferred since it only involves 12 floating-point
1053 * multiplications.)
1055 Matrix4x4Typed& PreTranslate(T aX, T aY, T aZ) {
1056 _41 += aX * _11 + aY * _21 + aZ * _31;
1057 _42 += aX * _12 + aY * _22 + aZ * _32;
1058 _43 += aX * _13 + aY * _23 + aZ * _33;
1059 _44 += aX * _14 + aY * _24 + aZ * _34;
1061 return *this;
1064 Matrix4x4Typed& PreTranslate(const Point3DTyped<UnknownUnits, T>& aPoint) {
1065 return PreTranslate(aPoint.x, aPoint.y, aPoint.z);
1069 * Similar to PreTranslate, but the translation is applied -after- this
1070 * matrix's existing transformation instead of before it.
1072 * This method is generally less used than PreTranslate since typically code
1073 * wants to adjust an existing user space to device space matrix to create a
1074 * transform to device space from a -new- user space (translated from the
1075 * previous user space). In that case consumers will need to use the Pre*
1076 * variants of the matrix methods rather than using the Post* methods, since
1077 * the Post* methods add a transform to the device space end of the
1078 * transformation.
1080 Matrix4x4Typed& PostTranslate(T aX, T aY, T aZ) {
1081 _11 += _14 * aX;
1082 _21 += _24 * aX;
1083 _31 += _34 * aX;
1084 _41 += _44 * aX;
1085 _12 += _14 * aY;
1086 _22 += _24 * aY;
1087 _32 += _34 * aY;
1088 _42 += _44 * aY;
1089 _13 += _14 * aZ;
1090 _23 += _24 * aZ;
1091 _33 += _34 * aZ;
1092 _43 += _44 * aZ;
1094 return *this;
1097 Matrix4x4Typed& PostTranslate(const TargetPoint3D& aPoint) {
1098 return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
1101 Matrix4x4Typed& PostTranslate(const TargetPoint& aPoint) {
1102 return PostTranslate(aPoint.x, aPoint.y, 0);
1105 static Matrix4x4Typed Scaling(T aScaleX, T aScaleY, T aScaleZ) {
1106 return Matrix4x4Typed(aScaleX, 0.0f, 0.0f, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f,
1107 0.0f, 0.0f, aScaleZ, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
1111 * Similar to PreTranslate, but applies a scale instead of a translation.
1113 Matrix4x4Typed& PreScale(T aX, T aY, T aZ) {
1114 _11 *= aX;
1115 _12 *= aX;
1116 _13 *= aX;
1117 _14 *= aX;
1118 _21 *= aY;
1119 _22 *= aY;
1120 _23 *= aY;
1121 _24 *= aY;
1122 _31 *= aZ;
1123 _32 *= aZ;
1124 _33 *= aZ;
1125 _34 *= aZ;
1127 return *this;
1130 template <typename NewSourceUnits>
1131 [[nodiscard]] Matrix4x4Typed<NewSourceUnits, TargetUnits> PreScale(
1132 const ScaleFactor<NewSourceUnits, SourceUnits>& aScale) const {
1133 auto clone = Cast<Matrix4x4Typed<NewSourceUnits, TargetUnits>>();
1134 clone.PreScale(aScale.scale, aScale.scale, 1);
1135 return clone;
1138 template <typename NewSourceUnits>
1139 [[nodiscard]] Matrix4x4Typed<NewSourceUnits, TargetUnits> PreScale(
1140 const BaseScaleFactors2D<NewSourceUnits, SourceUnits, T>& aScale) const {
1141 auto clone = Cast<Matrix4x4Typed<NewSourceUnits, TargetUnits>>();
1142 clone.PreScale(aScale.xScale, aScale.yScale, 1);
1143 return clone;
1147 * Similar to PostTranslate, but applies a scale instead of a translation.
1149 Matrix4x4Typed& PostScale(T aScaleX, T aScaleY, T aScaleZ) {
1150 _11 *= aScaleX;
1151 _21 *= aScaleX;
1152 _31 *= aScaleX;
1153 _41 *= aScaleX;
1154 _12 *= aScaleY;
1155 _22 *= aScaleY;
1156 _32 *= aScaleY;
1157 _42 *= aScaleY;
1158 _13 *= aScaleZ;
1159 _23 *= aScaleZ;
1160 _33 *= aScaleZ;
1161 _43 *= aScaleZ;
1163 return *this;
1166 template <typename NewTargetUnits>
1167 [[nodiscard]] Matrix4x4Typed<SourceUnits, NewTargetUnits> PostScale(
1168 const ScaleFactor<TargetUnits, NewTargetUnits>& aScale) const {
1169 auto clone = Cast<Matrix4x4Typed<SourceUnits, NewTargetUnits>>();
1170 clone.PostScale(aScale.scale, aScale.scale, 1);
1171 return clone;
1174 template <typename NewTargetUnits>
1175 [[nodiscard]] Matrix4x4Typed<SourceUnits, NewTargetUnits> PostScale(
1176 const BaseScaleFactors2D<TargetUnits, NewTargetUnits, T>& aScale) const {
1177 auto clone = Cast<Matrix4x4Typed<SourceUnits, NewTargetUnits>>();
1178 clone.PostScale(aScale.xScale, aScale.yScale, 1);
1179 return clone;
1182 void SkewXY(T aSkew) { (*this)[1] += (*this)[0] * aSkew; }
1184 void SkewXZ(T aSkew) { (*this)[2] += (*this)[0] * aSkew; }
1186 void SkewYZ(T aSkew) { (*this)[2] += (*this)[1] * aSkew; }
1188 Matrix4x4Typed& ChangeBasis(const Point3DTyped<UnknownUnits, T>& aOrigin) {
1189 return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z);
1192 Matrix4x4Typed& ChangeBasis(T aX, T aY, T aZ) {
1193 // Translate to the origin before applying this matrix
1194 PreTranslate(-aX, -aY, -aZ);
1196 // Translate back into position after applying this matrix
1197 PostTranslate(aX, aY, aZ);
1199 return *this;
1202 Matrix4x4Typed& Transpose() {
1203 std::swap(_12, _21);
1204 std::swap(_13, _31);
1205 std::swap(_14, _41);
1207 std::swap(_23, _32);
1208 std::swap(_24, _42);
1210 std::swap(_34, _43);
1212 return *this;
1215 bool operator==(const Matrix4x4Typed& o) const {
1216 // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics
1217 return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 &&
1218 _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 &&
1219 _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 &&
1220 _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44;
1223 bool operator!=(const Matrix4x4Typed& o) const { return !((*this) == o); }
1225 Matrix4x4Typed& operator=(const Matrix4x4Typed& aOther) = default;
1227 template <typename NewTargetUnits>
1228 Matrix4x4Typed<SourceUnits, NewTargetUnits, T> operator*(
1229 const Matrix4x4Typed<TargetUnits, NewTargetUnits, T>& aMatrix) const {
1230 Matrix4x4Typed<SourceUnits, NewTargetUnits, T> matrix;
1232 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 +
1233 _14 * aMatrix._41;
1234 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 +
1235 _24 * aMatrix._41;
1236 matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 +
1237 _34 * aMatrix._41;
1238 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 +
1239 _44 * aMatrix._41;
1240 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 +
1241 _14 * aMatrix._42;
1242 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 +
1243 _24 * aMatrix._42;
1244 matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 +
1245 _34 * aMatrix._42;
1246 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 +
1247 _44 * aMatrix._42;
1248 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 +
1249 _14 * aMatrix._43;
1250 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 +
1251 _24 * aMatrix._43;
1252 matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 +
1253 _34 * aMatrix._43;
1254 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 +
1255 _44 * aMatrix._43;
1256 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 +
1257 _14 * aMatrix._44;
1258 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 +
1259 _24 * aMatrix._44;
1260 matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 +
1261 _34 * aMatrix._44;
1262 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 +
1263 _44 * aMatrix._44;
1265 return matrix;
1268 Matrix4x4Typed& operator*=(
1269 const Matrix4x4Typed<TargetUnits, TargetUnits, T>& aMatrix) {
1270 *this = *this * aMatrix;
1271 return *this;
1274 /* Returns true if the matrix is an identity matrix.
1276 bool IsIdentity() const {
1277 return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f &&
1278 _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f &&
1279 _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f &&
1280 _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f;
1283 bool IsSingular() const { return Determinant() == 0.0; }
1285 T Determinant() const {
1286 return _14 * _23 * _32 * _41 - _13 * _24 * _32 * _41 -
1287 _14 * _22 * _33 * _41 + _12 * _24 * _33 * _41 +
1288 _13 * _22 * _34 * _41 - _12 * _23 * _34 * _41 -
1289 _14 * _23 * _31 * _42 + _13 * _24 * _31 * _42 +
1290 _14 * _21 * _33 * _42 - _11 * _24 * _33 * _42 -
1291 _13 * _21 * _34 * _42 + _11 * _23 * _34 * _42 +
1292 _14 * _22 * _31 * _43 - _12 * _24 * _31 * _43 -
1293 _14 * _21 * _32 * _43 + _11 * _24 * _32 * _43 +
1294 _12 * _21 * _34 * _43 - _11 * _22 * _34 * _43 -
1295 _13 * _22 * _31 * _44 + _12 * _23 * _31 * _44 +
1296 _13 * _21 * _32 * _44 - _11 * _23 * _32 * _44 -
1297 _12 * _21 * _33 * _44 + _11 * _22 * _33 * _44;
1300 // Invert() is not unit-correct. Prefer Inverse() where possible.
1301 bool Invert() {
1302 T det = Determinant();
1303 if (!det) {
1304 return false;
1307 Matrix4x4Typed<SourceUnits, TargetUnits, T> result;
1308 result._11 = _23 * _34 * _42 - _24 * _33 * _42 + _24 * _32 * _43 -
1309 _22 * _34 * _43 - _23 * _32 * _44 + _22 * _33 * _44;
1310 result._12 = _14 * _33 * _42 - _13 * _34 * _42 - _14 * _32 * _43 +
1311 _12 * _34 * _43 + _13 * _32 * _44 - _12 * _33 * _44;
1312 result._13 = _13 * _24 * _42 - _14 * _23 * _42 + _14 * _22 * _43 -
1313 _12 * _24 * _43 - _13 * _22 * _44 + _12 * _23 * _44;
1314 result._14 = _14 * _23 * _32 - _13 * _24 * _32 - _14 * _22 * _33 +
1315 _12 * _24 * _33 + _13 * _22 * _34 - _12 * _23 * _34;
1316 result._21 = _24 * _33 * _41 - _23 * _34 * _41 - _24 * _31 * _43 +
1317 _21 * _34 * _43 + _23 * _31 * _44 - _21 * _33 * _44;
1318 result._22 = _13 * _34 * _41 - _14 * _33 * _41 + _14 * _31 * _43 -
1319 _11 * _34 * _43 - _13 * _31 * _44 + _11 * _33 * _44;
1320 result._23 = _14 * _23 * _41 - _13 * _24 * _41 - _14 * _21 * _43 +
1321 _11 * _24 * _43 + _13 * _21 * _44 - _11 * _23 * _44;
1322 result._24 = _13 * _24 * _31 - _14 * _23 * _31 + _14 * _21 * _33 -
1323 _11 * _24 * _33 - _13 * _21 * _34 + _11 * _23 * _34;
1324 result._31 = _22 * _34 * _41 - _24 * _32 * _41 + _24 * _31 * _42 -
1325 _21 * _34 * _42 - _22 * _31 * _44 + _21 * _32 * _44;
1326 result._32 = _14 * _32 * _41 - _12 * _34 * _41 - _14 * _31 * _42 +
1327 _11 * _34 * _42 + _12 * _31 * _44 - _11 * _32 * _44;
1328 result._33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 -
1329 _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44;
1330 result._34 = _14 * _22 * _31 - _12 * _24 * _31 - _14 * _21 * _32 +
1331 _11 * _24 * _32 + _12 * _21 * _34 - _11 * _22 * _34;
1332 result._41 = _23 * _32 * _41 - _22 * _33 * _41 - _23 * _31 * _42 +
1333 _21 * _33 * _42 + _22 * _31 * _43 - _21 * _32 * _43;
1334 result._42 = _12 * _33 * _41 - _13 * _32 * _41 + _13 * _31 * _42 -
1335 _11 * _33 * _42 - _12 * _31 * _43 + _11 * _32 * _43;
1336 result._43 = _13 * _22 * _41 - _12 * _23 * _41 - _13 * _21 * _42 +
1337 _11 * _23 * _42 + _12 * _21 * _43 - _11 * _22 * _43;
1338 result._44 = _12 * _23 * _31 - _13 * _22 * _31 + _13 * _21 * _32 -
1339 _11 * _23 * _32 - _12 * _21 * _33 + _11 * _22 * _33;
1341 result._11 /= det;
1342 result._12 /= det;
1343 result._13 /= det;
1344 result._14 /= det;
1345 result._21 /= det;
1346 result._22 /= det;
1347 result._23 /= det;
1348 result._24 /= det;
1349 result._31 /= det;
1350 result._32 /= det;
1351 result._33 /= det;
1352 result._34 /= det;
1353 result._41 /= det;
1354 result._42 /= det;
1355 result._43 /= det;
1356 result._44 /= det;
1357 *this = result;
1359 return true;
1362 Matrix4x4Typed<TargetUnits, SourceUnits, T> Inverse() const {
1363 typedef Matrix4x4Typed<TargetUnits, SourceUnits, T> InvertedMatrix;
1364 InvertedMatrix clone = Cast<InvertedMatrix>();
1365 DebugOnly<bool> inverted = clone.Invert();
1366 MOZ_ASSERT(inverted,
1367 "Attempted to get the inverse of a non-invertible matrix");
1368 return clone;
1371 Maybe<Matrix4x4Typed<TargetUnits, SourceUnits, T>> MaybeInverse() const {
1372 typedef Matrix4x4Typed<TargetUnits, SourceUnits, T> InvertedMatrix;
1373 InvertedMatrix clone = Cast<InvertedMatrix>();
1374 if (clone.Invert()) {
1375 return Some(clone);
1377 return Nothing();
1380 void Normalize() {
1381 for (int i = 0; i < 4; i++) {
1382 for (int j = 0; j < 4; j++) {
1383 (*this)[i][j] /= (*this)[3][3];
1388 bool FuzzyEqual(const Matrix4x4Typed& o) const {
1389 return gfx::FuzzyEqual(_11, o._11) && gfx::FuzzyEqual(_12, o._12) &&
1390 gfx::FuzzyEqual(_13, o._13) && gfx::FuzzyEqual(_14, o._14) &&
1391 gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) &&
1392 gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) &&
1393 gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) &&
1394 gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) &&
1395 gfx::FuzzyEqual(_41, o._41) && gfx::FuzzyEqual(_42, o._42) &&
1396 gfx::FuzzyEqual(_43, o._43) && gfx::FuzzyEqual(_44, o._44);
1399 bool FuzzyEqualsMultiplicative(const Matrix4x4Typed& o) const {
1400 return ::mozilla::FuzzyEqualsMultiplicative(_11, o._11) &&
1401 ::mozilla::FuzzyEqualsMultiplicative(_12, o._12) &&
1402 ::mozilla::FuzzyEqualsMultiplicative(_13, o._13) &&
1403 ::mozilla::FuzzyEqualsMultiplicative(_14, o._14) &&
1404 ::mozilla::FuzzyEqualsMultiplicative(_21, o._21) &&
1405 ::mozilla::FuzzyEqualsMultiplicative(_22, o._22) &&
1406 ::mozilla::FuzzyEqualsMultiplicative(_23, o._23) &&
1407 ::mozilla::FuzzyEqualsMultiplicative(_24, o._24) &&
1408 ::mozilla::FuzzyEqualsMultiplicative(_31, o._31) &&
1409 ::mozilla::FuzzyEqualsMultiplicative(_32, o._32) &&
1410 ::mozilla::FuzzyEqualsMultiplicative(_33, o._33) &&
1411 ::mozilla::FuzzyEqualsMultiplicative(_34, o._34) &&
1412 ::mozilla::FuzzyEqualsMultiplicative(_41, o._41) &&
1413 ::mozilla::FuzzyEqualsMultiplicative(_42, o._42) &&
1414 ::mozilla::FuzzyEqualsMultiplicative(_43, o._43) &&
1415 ::mozilla::FuzzyEqualsMultiplicative(_44, o._44);
1418 bool IsBackfaceVisible() const {
1419 // Inverse()._33 < 0;
1420 T det = Determinant();
1421 T __33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 -
1422 _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44;
1423 return (__33 * det) < 0;
1426 Matrix4x4Typed& NudgeToIntegersFixedEpsilon() {
1427 NudgeToInteger(&_11);
1428 NudgeToInteger(&_12);
1429 NudgeToInteger(&_13);
1430 NudgeToInteger(&_14);
1431 NudgeToInteger(&_21);
1432 NudgeToInteger(&_22);
1433 NudgeToInteger(&_23);
1434 NudgeToInteger(&_24);
1435 NudgeToInteger(&_31);
1436 NudgeToInteger(&_32);
1437 NudgeToInteger(&_33);
1438 NudgeToInteger(&_34);
1439 static const float error = 1e-5f;
1440 NudgeToInteger(&_41, error);
1441 NudgeToInteger(&_42, error);
1442 NudgeToInteger(&_43, error);
1443 NudgeToInteger(&_44, error);
1444 return *this;
1447 Point4D TransposedVector(int aIndex) const {
1448 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
1449 return Point4DTyped<UnknownUnits, T>(*((&_11) + aIndex), *((&_21) + aIndex),
1450 *((&_31) + aIndex),
1451 *((&_41) + aIndex));
1454 void SetTransposedVector(int aIndex, Point4DTyped<UnknownUnits, T>& aVector) {
1455 MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
1456 *((&_11) + aIndex) = aVector.x;
1457 *((&_21) + aIndex) = aVector.y;
1458 *((&_31) + aIndex) = aVector.z;
1459 *((&_41) + aIndex) = aVector.w;
1462 bool Decompose(Point3DTyped<UnknownUnits, T>& translation,
1463 BaseQuaternion<T>& rotation,
1464 Point3DTyped<UnknownUnits, T>& scale) const {
1465 // Ensure matrix can be normalized
1466 if (gfx::FuzzyEqual(_44, 0.0f)) {
1467 return false;
1469 Matrix4x4Typed mat = *this;
1470 mat.Normalize();
1471 if (HasPerspectiveComponent()) {
1472 // We do not support projection matrices
1473 return false;
1476 // Extract translation
1477 translation.x = mat._41;
1478 translation.y = mat._42;
1479 translation.z = mat._43;
1481 // Remove translation
1482 mat._41 = 0.0f;
1483 mat._42 = 0.0f;
1484 mat._43 = 0.0f;
1486 // Extract scale
1487 scale.x = sqrtf(_11 * _11 + _21 * _21 + _31 * _31);
1488 scale.y = sqrtf(_12 * _12 + _22 * _22 + _32 * _32);
1489 scale.z = sqrtf(_13 * _13 + _23 * _23 + _33 * _33);
1491 // Remove scale
1492 if (gfx::FuzzyEqual(scale.x, 0.0f) || gfx::FuzzyEqual(scale.y, 0.0f) ||
1493 gfx::FuzzyEqual(scale.z, 0.0f)) {
1494 // We do not support matrices with a zero scale component
1495 return false;
1498 // Extract rotation
1499 rotation.SetFromRotationMatrix(this->ToUnknownMatrix());
1500 return true;
1503 // Sets this matrix to a rotation matrix given by aQuat.
1504 // This quaternion *MUST* be normalized!
1505 // Implemented in Quaternion.cpp
1506 void SetRotationFromQuaternion(const BaseQuaternion<T>& q) {
1507 const T x2 = q.x + q.x, y2 = q.y + q.y, z2 = q.z + q.z;
1508 const T xx = q.x * x2, xy = q.x * y2, xz = q.x * z2;
1509 const T yy = q.y * y2, yz = q.y * z2, zz = q.z * z2;
1510 const T wx = q.w * x2, wy = q.w * y2, wz = q.w * z2;
1512 _11 = 1.0f - (yy + zz);
1513 _21 = xy - wz;
1514 _31 = xz + wy;
1515 _41 = 0.0f;
1517 _12 = xy + wz;
1518 _22 = 1.0f - (xx + zz);
1519 _32 = yz - wx;
1520 _42 = 0.0f;
1522 _13 = xz - wy;
1523 _23 = yz + wx;
1524 _33 = 1.0f - (xx + yy);
1525 _43 = 0.0f;
1527 _14 = _42 = _43 = 0.0f;
1528 _44 = 1.0f;
1531 // Set all the members of the matrix to NaN
1532 void SetNAN() {
1533 _11 = UnspecifiedNaN<T>();
1534 _21 = UnspecifiedNaN<T>();
1535 _31 = UnspecifiedNaN<T>();
1536 _41 = UnspecifiedNaN<T>();
1537 _12 = UnspecifiedNaN<T>();
1538 _22 = UnspecifiedNaN<T>();
1539 _32 = UnspecifiedNaN<T>();
1540 _42 = UnspecifiedNaN<T>();
1541 _13 = UnspecifiedNaN<T>();
1542 _23 = UnspecifiedNaN<T>();
1543 _33 = UnspecifiedNaN<T>();
1544 _43 = UnspecifiedNaN<T>();
1545 _14 = UnspecifiedNaN<T>();
1546 _24 = UnspecifiedNaN<T>();
1547 _34 = UnspecifiedNaN<T>();
1548 _44 = UnspecifiedNaN<T>();
1551 // Verifies that the matrix contains no Infs or NaNs
1552 bool IsFinite() const {
1553 return std::isfinite(_11) && std::isfinite(_12) && std::isfinite(_13) &&
1554 std::isfinite(_14) && std::isfinite(_21) && std::isfinite(_22) &&
1555 std::isfinite(_23) && std::isfinite(_24) && std::isfinite(_31) &&
1556 std::isfinite(_32) && std::isfinite(_33) && std::isfinite(_34) &&
1557 std::isfinite(_41) && std::isfinite(_42) && std::isfinite(_43) &&
1558 std::isfinite(_44);
1561 void SkewXY(double aXSkew, double aYSkew) {
1562 // XXX Is double precision really necessary here
1563 T tanX = SafeTangent(aXSkew);
1564 T tanY = SafeTangent(aYSkew);
1565 T temp;
1567 temp = _11;
1568 _11 += tanY * _21;
1569 _21 += tanX * temp;
1571 temp = _12;
1572 _12 += tanY * _22;
1573 _22 += tanX * temp;
1575 temp = _13;
1576 _13 += tanY * _23;
1577 _23 += tanX * temp;
1579 temp = _14;
1580 _14 += tanY * _24;
1581 _24 += tanX * temp;
1584 void RotateX(double aTheta) {
1585 // XXX Is double precision really necessary here
1586 double cosTheta = FlushToZero(cos(aTheta));
1587 double sinTheta = FlushToZero(sin(aTheta));
1589 T temp;
1591 temp = _21;
1592 _21 = cosTheta * _21 + sinTheta * _31;
1593 _31 = -sinTheta * temp + cosTheta * _31;
1595 temp = _22;
1596 _22 = cosTheta * _22 + sinTheta * _32;
1597 _32 = -sinTheta * temp + cosTheta * _32;
1599 temp = _23;
1600 _23 = cosTheta * _23 + sinTheta * _33;
1601 _33 = -sinTheta * temp + cosTheta * _33;
1603 temp = _24;
1604 _24 = cosTheta * _24 + sinTheta * _34;
1605 _34 = -sinTheta * temp + cosTheta * _34;
1608 void RotateY(double aTheta) {
1609 // XXX Is double precision really necessary here
1610 double cosTheta = FlushToZero(cos(aTheta));
1611 double sinTheta = FlushToZero(sin(aTheta));
1613 T temp;
1615 temp = _11;
1616 _11 = cosTheta * _11 + -sinTheta * _31;
1617 _31 = sinTheta * temp + cosTheta * _31;
1619 temp = _12;
1620 _12 = cosTheta * _12 + -sinTheta * _32;
1621 _32 = sinTheta * temp + cosTheta * _32;
1623 temp = _13;
1624 _13 = cosTheta * _13 + -sinTheta * _33;
1625 _33 = sinTheta * temp + cosTheta * _33;
1627 temp = _14;
1628 _14 = cosTheta * _14 + -sinTheta * _34;
1629 _34 = sinTheta * temp + cosTheta * _34;
1632 void RotateZ(double aTheta) {
1633 // XXX Is double precision really necessary here
1634 double cosTheta = FlushToZero(cos(aTheta));
1635 double sinTheta = FlushToZero(sin(aTheta));
1637 T temp;
1639 temp = _11;
1640 _11 = cosTheta * _11 + sinTheta * _21;
1641 _21 = -sinTheta * temp + cosTheta * _21;
1643 temp = _12;
1644 _12 = cosTheta * _12 + sinTheta * _22;
1645 _22 = -sinTheta * temp + cosTheta * _22;
1647 temp = _13;
1648 _13 = cosTheta * _13 + sinTheta * _23;
1649 _23 = -sinTheta * temp + cosTheta * _23;
1651 temp = _14;
1652 _14 = cosTheta * _14 + sinTheta * _24;
1653 _24 = -sinTheta * temp + cosTheta * _24;
1656 // Sets this matrix to a rotation matrix about a
1657 // vector [x,y,z] by angle theta. The vector is normalized
1658 // to a unit vector.
1659 // https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
1660 void SetRotateAxisAngle(double aX, double aY, double aZ, double aTheta) {
1661 Point3DTyped<UnknownUnits, T> vector(aX, aY, aZ);
1662 if (!vector.Length()) {
1663 return;
1665 vector.RobustNormalize();
1667 double x = vector.x;
1668 double y = vector.y;
1669 double z = vector.z;
1671 double cosTheta = FlushToZero(cos(aTheta));
1672 double sinTheta = FlushToZero(sin(aTheta));
1674 // sin(aTheta / 2) * cos(aTheta / 2)
1675 double sc = sinTheta / 2;
1676 // pow(sin(aTheta / 2), 2)
1677 double sq = (1 - cosTheta) / 2;
1679 _11 = 1 - 2 * (y * y + z * z) * sq;
1680 _12 = 2 * (x * y * sq + z * sc);
1681 _13 = 2 * (x * z * sq - y * sc);
1682 _14 = 0.0f;
1683 _21 = 2 * (x * y * sq - z * sc);
1684 _22 = 1 - 2 * (x * x + z * z) * sq;
1685 _23 = 2 * (y * z * sq + x * sc);
1686 _24 = 0.0f;
1687 _31 = 2 * (x * z * sq + y * sc);
1688 _32 = 2 * (y * z * sq - x * sc);
1689 _33 = 1 - 2 * (x * x + y * y) * sq;
1690 _34 = 0.0f;
1691 _41 = 0.0f;
1692 _42 = 0.0f;
1693 _43 = 0.0f;
1694 _44 = 1.0f;
1697 void Perspective(T aDepth) {
1698 MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!");
1699 _31 += -1.0 / aDepth * _41;
1700 _32 += -1.0 / aDepth * _42;
1701 _33 += -1.0 / aDepth * _43;
1702 _34 += -1.0 / aDepth * _44;
1705 Point3D GetNormalVector() const {
1706 // Define a plane in transformed space as the transformations
1707 // of 3 points on the z=0 screen plane.
1708 Point3DTyped<UnknownUnits, T> a =
1709 TransformPoint(Point3DTyped<UnknownUnits, T>(0, 0, 0));
1710 Point3DTyped<UnknownUnits, T> b =
1711 TransformPoint(Point3DTyped<UnknownUnits, T>(0, 1, 0));
1712 Point3DTyped<UnknownUnits, T> c =
1713 TransformPoint(Point3DTyped<UnknownUnits, T>(1, 0, 0));
1715 // Convert to two vectors on the surface of the plane.
1716 Point3DTyped<UnknownUnits, T> ab = b - a;
1717 Point3DTyped<UnknownUnits, T> ac = c - a;
1719 return ac.CrossProduct(ab);
1723 * Returns true if the matrix has any transform other
1724 * than a straight translation.
1726 bool HasNonTranslation() const {
1727 return !gfx::FuzzyEqual(_11, 1.0) || !gfx::FuzzyEqual(_22, 1.0) ||
1728 !gfx::FuzzyEqual(_12, 0.0) || !gfx::FuzzyEqual(_21, 0.0) ||
1729 !gfx::FuzzyEqual(_13, 0.0) || !gfx::FuzzyEqual(_23, 0.0) ||
1730 !gfx::FuzzyEqual(_31, 0.0) || !gfx::FuzzyEqual(_32, 0.0) ||
1731 !gfx::FuzzyEqual(_33, 1.0);
1735 * Returns true if the matrix is anything other than a straight
1736 * translation by integers.
1738 bool HasNonIntegerTranslation() const {
1739 return HasNonTranslation() || !gfx::FuzzyEqual(_41, floor(_41 + 0.5)) ||
1740 !gfx::FuzzyEqual(_42, floor(_42 + 0.5)) ||
1741 !gfx::FuzzyEqual(_43, floor(_43 + 0.5));
1745 * Return true if the matrix is with perspective (w).
1747 bool HasPerspectiveComponent() const {
1748 return _14 != 0 || _24 != 0 || _34 != 0 || _44 != 1;
1751 /* Returns true if the matrix is a rectilinear transformation (i.e.
1752 * grid-aligned rectangles are transformed to grid-aligned rectangles).
1753 * This should only be called on 2D matrices.
1755 bool IsRectilinear() const {
1756 MOZ_ASSERT(Is2D());
1757 if (gfx::FuzzyEqual(_12, 0) && gfx::FuzzyEqual(_21, 0)) {
1758 return true;
1759 } else if (gfx::FuzzyEqual(_22, 0) && gfx::FuzzyEqual(_11, 0)) {
1760 return true;
1762 return false;
1766 * Convert between typed and untyped matrices.
1768 using UnknownMatrix = Matrix4x4Typed<UnknownUnits, UnknownUnits, T>;
1769 UnknownMatrix ToUnknownMatrix() const {
1770 return UnknownMatrix{_11, _12, _13, _14, _21, _22, _23, _24,
1771 _31, _32, _33, _34, _41, _42, _43, _44};
1773 static Matrix4x4Typed FromUnknownMatrix(const UnknownMatrix& aUnknown) {
1774 return Matrix4x4Typed{
1775 aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14,
1776 aUnknown._21, aUnknown._22, aUnknown._23, aUnknown._24,
1777 aUnknown._31, aUnknown._32, aUnknown._33, aUnknown._34,
1778 aUnknown._41, aUnknown._42, aUnknown._43, aUnknown._44};
1781 * For convenience, overload FromUnknownMatrix() for Maybe<Matrix>.
1783 static Maybe<Matrix4x4Typed> FromUnknownMatrix(
1784 const Maybe<UnknownMatrix>& aUnknown) {
1785 if (aUnknown.isSome()) {
1786 return Some(FromUnknownMatrix(*aUnknown));
1788 return Nothing();
1792 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
1793 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits, double> Matrix4x4Double;
1795 class Matrix5x4 {
1796 public:
1797 Matrix5x4()
1798 : _11(1.0f),
1799 _12(0),
1800 _13(0),
1801 _14(0),
1802 _21(0),
1803 _22(1.0f),
1804 _23(0),
1805 _24(0),
1806 _31(0),
1807 _32(0),
1808 _33(1.0f),
1809 _34(0),
1810 _41(0),
1811 _42(0),
1812 _43(0),
1813 _44(1.0f),
1814 _51(0),
1815 _52(0),
1816 _53(0),
1817 _54(0) {}
1818 Matrix5x4(Float a11, Float a12, Float a13, Float a14, Float a21, Float a22,
1819 Float a23, Float a24, Float a31, Float a32, Float a33, Float a34,
1820 Float a41, Float a42, Float a43, Float a44, Float a51, Float a52,
1821 Float a53, Float a54)
1822 : _11(a11),
1823 _12(a12),
1824 _13(a13),
1825 _14(a14),
1826 _21(a21),
1827 _22(a22),
1828 _23(a23),
1829 _24(a24),
1830 _31(a31),
1831 _32(a32),
1832 _33(a33),
1833 _34(a34),
1834 _41(a41),
1835 _42(a42),
1836 _43(a43),
1837 _44(a44),
1838 _51(a51),
1839 _52(a52),
1840 _53(a53),
1841 _54(a54) {}
1843 bool operator==(const Matrix5x4& o) const {
1844 return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 &&
1845 _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 &&
1846 _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 &&
1847 _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44 &&
1848 _51 == o._51 && _52 == o._52 && _53 == o._53 && _54 == o._54;
1851 bool operator!=(const Matrix5x4& aMatrix) const {
1852 return !(*this == aMatrix);
1855 Matrix5x4 operator*(const Matrix5x4& aMatrix) const {
1856 Matrix5x4 resultMatrix;
1858 resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21 +
1859 this->_13 * aMatrix._31 + this->_14 * aMatrix._41;
1860 resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22 +
1861 this->_13 * aMatrix._32 + this->_14 * aMatrix._42;
1862 resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23 +
1863 this->_13 * aMatrix._33 + this->_14 * aMatrix._43;
1864 resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24 +
1865 this->_13 * aMatrix._34 + this->_14 * aMatrix._44;
1866 resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21 +
1867 this->_23 * aMatrix._31 + this->_24 * aMatrix._41;
1868 resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22 +
1869 this->_23 * aMatrix._32 + this->_24 * aMatrix._42;
1870 resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23 +
1871 this->_23 * aMatrix._33 + this->_24 * aMatrix._43;
1872 resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24 +
1873 this->_23 * aMatrix._34 + this->_24 * aMatrix._44;
1874 resultMatrix._31 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 +
1875 this->_33 * aMatrix._31 + this->_34 * aMatrix._41;
1876 resultMatrix._32 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 +
1877 this->_33 * aMatrix._32 + this->_34 * aMatrix._42;
1878 resultMatrix._33 = this->_31 * aMatrix._13 + this->_32 * aMatrix._23 +
1879 this->_33 * aMatrix._33 + this->_34 * aMatrix._43;
1880 resultMatrix._34 = this->_31 * aMatrix._14 + this->_32 * aMatrix._24 +
1881 this->_33 * aMatrix._34 + this->_34 * aMatrix._44;
1882 resultMatrix._41 = this->_41 * aMatrix._11 + this->_42 * aMatrix._21 +
1883 this->_43 * aMatrix._31 + this->_44 * aMatrix._41;
1884 resultMatrix._42 = this->_41 * aMatrix._12 + this->_42 * aMatrix._22 +
1885 this->_43 * aMatrix._32 + this->_44 * aMatrix._42;
1886 resultMatrix._43 = this->_41 * aMatrix._13 + this->_42 * aMatrix._23 +
1887 this->_43 * aMatrix._33 + this->_44 * aMatrix._43;
1888 resultMatrix._44 = this->_41 * aMatrix._14 + this->_42 * aMatrix._24 +
1889 this->_43 * aMatrix._34 + this->_44 * aMatrix._44;
1890 resultMatrix._51 = this->_51 * aMatrix._11 + this->_52 * aMatrix._21 +
1891 this->_53 * aMatrix._31 + this->_54 * aMatrix._41 +
1892 aMatrix._51;
1893 resultMatrix._52 = this->_51 * aMatrix._12 + this->_52 * aMatrix._22 +
1894 this->_53 * aMatrix._32 + this->_54 * aMatrix._42 +
1895 aMatrix._52;
1896 resultMatrix._53 = this->_51 * aMatrix._13 + this->_52 * aMatrix._23 +
1897 this->_53 * aMatrix._33 + this->_54 * aMatrix._43 +
1898 aMatrix._53;
1899 resultMatrix._54 = this->_51 * aMatrix._14 + this->_52 * aMatrix._24 +
1900 this->_53 * aMatrix._34 + this->_54 * aMatrix._44 +
1901 aMatrix._54;
1903 return resultMatrix;
1906 Matrix5x4& operator*=(const Matrix5x4& aMatrix) {
1907 *this = *this * aMatrix;
1908 return *this;
1911 friend std::ostream& operator<<(std::ostream& aStream,
1912 const Matrix5x4& aMatrix) {
1913 const Float* f = &aMatrix._11;
1914 aStream << "[ " << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
1915 f += 4;
1916 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
1917 f += 4;
1918 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
1919 f += 4;
1920 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
1921 f += 4;
1922 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3]
1923 << "; ]";
1924 return aStream;
1927 union {
1928 struct {
1929 Float _11, _12, _13, _14;
1930 Float _21, _22, _23, _24;
1931 Float _31, _32, _33, _34;
1932 Float _41, _42, _43, _44;
1933 Float _51, _52, _53, _54;
1935 Float components[20];
1939 /* This Matrix class will carry one additional type field in order to
1940 * track what type of 4x4 matrix we're dealing with, it can then execute
1941 * simplified versions of certain operations when applicable.
1942 * This does not allow access to the parent class directly, as a caller
1943 * could then mutate the parent class without updating the type.
1945 template <typename SourceUnits, typename TargetUnits>
1946 class Matrix4x4TypedFlagged
1947 : protected Matrix4x4Typed<SourceUnits, TargetUnits> {
1948 public:
1949 using Parent = Matrix4x4Typed<SourceUnits, TargetUnits>;
1950 using TargetPoint = PointTyped<TargetUnits>;
1951 using Parent::_11;
1952 using Parent::_12;
1953 using Parent::_13;
1954 using Parent::_14;
1955 using Parent::_21;
1956 using Parent::_22;
1957 using Parent::_23;
1958 using Parent::_24;
1959 using Parent::_31;
1960 using Parent::_32;
1961 using Parent::_33;
1962 using Parent::_34;
1963 using Parent::_41;
1964 using Parent::_42;
1965 using Parent::_43;
1966 using Parent::_44;
1968 Matrix4x4TypedFlagged() : mType(MatrixType::Identity) {}
1970 Matrix4x4TypedFlagged(Float a11, Float a12, Float a13, Float a14, Float a21,
1971 Float a22, Float a23, Float a24, Float a31, Float a32,
1972 Float a33, Float a34, Float a41, Float a42, Float a43,
1973 Float a44)
1974 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41,
1975 a42, a43, a44) {
1976 Analyze();
1979 MOZ_IMPLICIT Matrix4x4TypedFlagged(const Parent& aOther) : Parent(aOther) {
1980 Analyze();
1983 template <typename NewMatrix4x4TypedFlagged>
1984 [[nodiscard]] NewMatrix4x4TypedFlagged Cast() const {
1985 return NewMatrix4x4TypedFlagged(_11, _12, _13, _14, _21, _22, _23, _24, _31,
1986 _32, _33, _34, _41, _42, _43, _44, mType);
1989 template <class F>
1990 PointTyped<TargetUnits, F> TransformPoint(
1991 const PointTyped<SourceUnits, F>& aPoint) const {
1992 if (mType == MatrixType::Identity) {
1993 return aPoint;
1996 if (mType == MatrixType::Simple) {
1997 return TransformPointSimple(aPoint);
2000 return Parent::TransformPoint(aPoint);
2003 template <class F>
2004 RectTyped<TargetUnits, F> TransformAndClipBounds(
2005 const RectTyped<SourceUnits, F>& aRect,
2006 const RectTyped<TargetUnits, F>& aClip) const {
2007 if (mType == MatrixType::Identity) {
2008 const RectTyped<SourceUnits, F>& clipped = aRect.Intersect(aClip);
2009 return RectTyped<TargetUnits, F>(clipped.X(), clipped.Y(),
2010 clipped.Width(), clipped.Height());
2013 if (mType == MatrixType::Simple) {
2014 PointTyped<UnknownUnits, F> p1 = TransformPointSimple(aRect.TopLeft());
2015 PointTyped<UnknownUnits, F> p2 = TransformPointSimple(aRect.TopRight());
2016 PointTyped<UnknownUnits, F> p3 = TransformPointSimple(aRect.BottomLeft());
2017 PointTyped<UnknownUnits, F> p4 =
2018 TransformPointSimple(aRect.BottomRight());
2020 F min_x = std::min(std::min(std::min(p1.x, p2.x), p3.x), p4.x);
2021 F max_x = std::max(std::max(std::max(p1.x, p2.x), p3.x), p4.x);
2022 F min_y = std::min(std::min(std::min(p1.y, p2.y), p3.y), p4.y);
2023 F max_y = std::max(std::max(std::max(p1.y, p2.y), p3.y), p4.y);
2025 TargetPoint topLeft(std::max(min_x, aClip.x), std::max(min_y, aClip.y));
2026 F width = std::min(max_x, aClip.XMost()) - topLeft.x;
2027 F height = std::min(max_y, aClip.YMost()) - topLeft.y;
2029 return RectTyped<TargetUnits, F>(topLeft.x, topLeft.y, width, height);
2031 return Parent::TransformAndClipBounds(aRect, aClip);
2034 bool FuzzyEqual(const Parent& o) const { return Parent::FuzzyEqual(o); }
2036 bool FuzzyEqual(const Matrix4x4TypedFlagged& o) const {
2037 if (mType == MatrixType::Identity && o.mType == MatrixType::Identity) {
2038 return true;
2040 return Parent::FuzzyEqual(o);
2043 Matrix4x4TypedFlagged& PreTranslate(Float aX, Float aY, Float aZ) {
2044 if (mType == MatrixType::Identity) {
2045 _41 = aX;
2046 _42 = aY;
2047 _43 = aZ;
2049 if (!aZ) {
2050 mType = MatrixType::Simple;
2051 return *this;
2053 mType = MatrixType::Full;
2054 return *this;
2057 Parent::PreTranslate(aX, aY, aZ);
2059 if (aZ != 0) {
2060 mType = MatrixType::Full;
2063 return *this;
2066 Matrix4x4TypedFlagged& PostTranslate(Float aX, Float aY, Float aZ) {
2067 if (mType == MatrixType::Identity) {
2068 _41 = aX;
2069 _42 = aY;
2070 _43 = aZ;
2072 if (!aZ) {
2073 mType = MatrixType::Simple;
2074 return *this;
2076 mType = MatrixType::Full;
2077 return *this;
2080 Parent::PostTranslate(aX, aY, aZ);
2082 if (aZ != 0) {
2083 mType = MatrixType::Full;
2086 return *this;
2089 Matrix4x4TypedFlagged& ChangeBasis(Float aX, Float aY, Float aZ) {
2090 // Translate to the origin before applying this matrix
2091 PreTranslate(-aX, -aY, -aZ);
2093 // Translate back into position after applying this matrix
2094 PostTranslate(aX, aY, aZ);
2096 return *this;
2099 bool IsIdentity() const { return mType == MatrixType::Identity; }
2101 template <class F>
2102 Point4DTyped<TargetUnits, F> ProjectPoint(
2103 const PointTyped<SourceUnits, F>& aPoint) const {
2104 if (mType == MatrixType::Identity) {
2105 return Point4DTyped<TargetUnits, F>(aPoint.x, aPoint.y, 0, 1);
2108 if (mType == MatrixType::Simple) {
2109 TargetPoint point = TransformPointSimple(aPoint);
2110 return Point4DTyped<TargetUnits, F>(point.x, point.y, 0, 1);
2113 return Parent::ProjectPoint(aPoint);
2116 Matrix4x4TypedFlagged& ProjectTo2D() {
2117 if (mType == MatrixType::Full) {
2118 Parent::ProjectTo2D();
2120 return *this;
2123 bool IsSingular() const {
2124 if (mType == MatrixType::Identity) {
2125 return false;
2127 return Parent::Determinant() == 0.0;
2130 bool Invert() {
2131 if (mType == MatrixType::Identity) {
2132 return true;
2135 return Parent::Invert();
2138 Matrix4x4TypedFlagged<TargetUnits, SourceUnits> Inverse() const {
2139 typedef Matrix4x4TypedFlagged<TargetUnits, SourceUnits> InvertedMatrix;
2140 InvertedMatrix clone = Cast<InvertedMatrix>();
2141 if (mType == MatrixType::Identity) {
2142 return clone;
2144 DebugOnly<bool> inverted = clone.Invert();
2145 MOZ_ASSERT(inverted,
2146 "Attempted to get the inverse of a non-invertible matrix");
2148 // Inverting a 2D Matrix should result in a 2D matrix, ergo mType doesn't
2149 // change.
2150 return clone;
2153 template <typename NewTargetUnits>
2154 bool operator==(
2155 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2156 if (mType == MatrixType::Identity &&
2157 aMatrix.mType == MatrixType::Identity) {
2158 return true;
2160 // Depending on the usage it may make sense to compare more flags.
2161 return Parent::operator==(aMatrix);
2164 template <typename NewTargetUnits>
2165 bool operator!=(
2166 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2167 if (mType == MatrixType::Identity &&
2168 aMatrix.mType == MatrixType::Identity) {
2169 return false;
2171 // Depending on the usage it may make sense to compare more flags.
2172 return Parent::operator!=(aMatrix);
2175 template <typename NewTargetUnits>
2176 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> operator*(
2177 const Matrix4x4Typed<TargetUnits, NewTargetUnits>& aMatrix) const {
2178 if (mType == MatrixType::Identity) {
2179 return aMatrix;
2182 if (mType == MatrixType::Simple) {
2183 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2184 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
2185 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
2186 matrix._31 = aMatrix._31;
2187 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41;
2188 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
2189 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
2190 matrix._32 = aMatrix._32;
2191 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42;
2192 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23;
2193 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23;
2194 matrix._33 = aMatrix._33;
2195 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + aMatrix._43;
2196 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24;
2197 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24;
2198 matrix._34 = aMatrix._34;
2199 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + aMatrix._44;
2200 matrix.Analyze();
2201 return matrix;
2204 return Parent::operator*(aMatrix);
2207 template <typename NewTargetUnits>
2208 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> operator*(
2209 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2210 if (mType == MatrixType::Identity) {
2211 return aMatrix;
2214 if (aMatrix.mType == MatrixType::Identity) {
2215 return Cast<Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits>>();
2218 if (mType == MatrixType::Simple && aMatrix.mType == MatrixType::Simple) {
2219 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2220 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
2221 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
2222 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41;
2223 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
2224 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
2225 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42;
2226 matrix.mType = MatrixType::Simple;
2227 return matrix;
2228 } else if (mType == MatrixType::Simple) {
2229 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2230 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
2231 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
2232 matrix._31 = aMatrix._31;
2233 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41;
2234 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
2235 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
2236 matrix._32 = aMatrix._32;
2237 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42;
2238 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23;
2239 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23;
2240 matrix._33 = aMatrix._33;
2241 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + aMatrix._43;
2242 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24;
2243 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24;
2244 matrix._34 = aMatrix._34;
2245 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + aMatrix._44;
2246 matrix.mType = MatrixType::Full;
2247 return matrix;
2248 } else if (aMatrix.mType == MatrixType::Simple) {
2249 Matrix4x4TypedFlagged<SourceUnits, NewTargetUnits> matrix;
2250 matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _14 * aMatrix._41;
2251 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _24 * aMatrix._41;
2252 matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _34 * aMatrix._41;
2253 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _44 * aMatrix._41;
2254 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _14 * aMatrix._42;
2255 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _24 * aMatrix._42;
2256 matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _34 * aMatrix._42;
2257 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _44 * aMatrix._42;
2258 matrix._13 = _13;
2259 matrix._23 = _23;
2260 matrix._33 = _33;
2261 matrix._43 = _43;
2262 matrix._14 = _14;
2263 matrix._24 = _24;
2264 matrix._34 = _34;
2265 matrix._44 = _44;
2266 matrix.mType = MatrixType::Full;
2267 return matrix;
2270 return Parent::operator*(aMatrix);
2273 bool Is2D() const { return mType != MatrixType::Full; }
2275 bool CanDraw2D(Matrix* aMatrix = nullptr) const {
2276 if (mType != MatrixType::Full) {
2277 if (aMatrix) {
2278 aMatrix->_11 = _11;
2279 aMatrix->_12 = _12;
2280 aMatrix->_21 = _21;
2281 aMatrix->_22 = _22;
2282 aMatrix->_31 = _41;
2283 aMatrix->_32 = _42;
2285 return true;
2287 return Parent::CanDraw2D(aMatrix);
2290 bool Is2D(Matrix* aMatrix) const {
2291 if (!Is2D()) {
2292 return false;
2294 if (aMatrix) {
2295 aMatrix->_11 = _11;
2296 aMatrix->_12 = _12;
2297 aMatrix->_21 = _21;
2298 aMatrix->_22 = _22;
2299 aMatrix->_31 = _41;
2300 aMatrix->_32 = _42;
2302 return true;
2305 template <class F>
2306 RectTyped<TargetUnits, F> ProjectRectBounds(
2307 const RectTyped<SourceUnits, F>& aRect,
2308 const RectTyped<TargetUnits, F>& aClip) const {
2309 return Parent::ProjectRectBounds(aRect, aClip);
2312 const Parent& GetMatrix() const { return *this; }
2314 private:
2315 enum class MatrixType : uint8_t {
2316 Identity,
2317 Simple, // 2x3 Matrix
2318 Full // 4x4 Matrix
2321 Matrix4x4TypedFlagged(Float a11, Float a12, Float a13, Float a14, Float a21,
2322 Float a22, Float a23, Float a24, Float a31, Float a32,
2323 Float a33, Float a34, Float a41, Float a42, Float a43,
2324 Float a44,
2325 typename Matrix4x4TypedFlagged::MatrixType aType)
2326 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41,
2327 a42, a43, a44) {
2328 mType = aType;
2330 static Matrix4x4TypedFlagged FromUnknownMatrix(
2331 const Matrix4x4Flagged& aUnknown) {
2332 return Matrix4x4TypedFlagged{
2333 aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14, aUnknown._21,
2334 aUnknown._22, aUnknown._23, aUnknown._24, aUnknown._31, aUnknown._32,
2335 aUnknown._33, aUnknown._34, aUnknown._41, aUnknown._42, aUnknown._43,
2336 aUnknown._44, aUnknown.mType};
2338 Matrix4x4Flagged ToUnknownMatrix() const {
2339 return Matrix4x4Flagged{_11, _12, _13, _14, _21, _22, _23, _24, _31,
2340 _32, _33, _34, _41, _42, _43, _44, mType};
2343 template <class F>
2344 PointTyped<TargetUnits, F> TransformPointSimple(
2345 const PointTyped<SourceUnits, F>& aPoint) const {
2346 PointTyped<SourceUnits, F> temp;
2347 temp.x = aPoint.x * _11 + aPoint.y * +_21 + _41;
2348 temp.y = aPoint.x * _12 + aPoint.y * +_22 + _42;
2349 return temp;
2352 void Analyze() {
2353 if (Parent::IsIdentity()) {
2354 mType = MatrixType::Identity;
2355 return;
2358 if (Parent::Is2D()) {
2359 mType = MatrixType::Simple;
2360 return;
2363 mType = MatrixType::Full;
2366 MatrixType mType;
2369 using Matrix4x4Flagged = Matrix4x4TypedFlagged<UnknownUnits, UnknownUnits>;
2371 } // namespace gfx
2372 } // namespace mozilla
2374 #endif /* MOZILLA_GFX_MATRIX_H_ */