Backed out 2 changesets (bug 1855992) for causing talos failures @ mozilla::net:...
[gecko.git] / gfx / 2d / Matrix.h
blobc5da2767990af3d3bd832a2c4ca7669c1fbc1114
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);
639 * Returns true if the matrix is isomorphic to a 2D affine transformation.
641 bool Is2D() const {
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) {
645 return false;
647 return true;
650 bool Is2D(BaseMatrix<T>* aMatrix) const {
651 if (!Is2D()) {
652 return false;
654 if (aMatrix) {
655 aMatrix->_11 = _11;
656 aMatrix->_12 = _12;
657 aMatrix->_21 = _21;
658 aMatrix->_22 = _22;
659 aMatrix->_31 = _41;
660 aMatrix->_32 = _42;
662 return true;
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) {
673 return false;
675 if (aMatrix) {
676 aMatrix->_11 = _11;
677 aMatrix->_12 = _12;
678 aMatrix->_21 = _21;
679 aMatrix->_22 = _22;
680 aMatrix->_31 = _41;
681 aMatrix->_32 = _42;
683 return true;
686 Matrix4x4Typed& ProjectTo2D() {
687 _31 = 0.0f;
688 _32 = 0.0f;
689 _13 = 0.0f;
690 _23 = 0.0f;
691 _33 = 1.0f;
692 _43 = 0.0f;
693 _34 = 0.0f;
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;
703 _11 *= scale;
704 _12 *= scale;
705 _21 *= scale;
706 _22 *= scale;
707 _41 *= scale;
708 _42 *= scale;
709 _44 = 1.0f;
711 return *this;
714 template <class F>
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));
730 template <class F>
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
746 // infinite end.
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
786 // use that instead.
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) {
792 min_x = aClip.X();
793 } else if (intercept.x > 0.0f) {
794 max_x = aClip.XMost();
796 if (intercept.y < 0.0f) {
797 min_y = aClip.Y();
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,
809 max_y - min_y);
813 * TransformAndClipBounds transforms aRect as a bounding box, while clipping
814 * the transformed bounds to the extents of aClip.
816 template <class F>
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,
839 max_y - min_y);
842 template <class F>
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
857 * within aClip.
859 template <class F>
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()),
889 polygonBufA);
890 polygon = IntersectPolygon<F>(polygon, P4D(-1.0, 0.0, 0.0, aClip.XMost()),
891 polygonBufB);
892 polygon = IntersectPolygon<F>(polygon, P4D(0.0, 1.0, 0.0, -aClip.Y()),
893 polygonBufA);
894 polygon = IntersectPolygon<F>(polygon, P4D(0.0, -1.0, 0.0, aClip.YMost()),
895 polygonBufB);
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);
904 } else {
905 p = srcPoint.As2DPoint();
907 // Emit only unique points
908 if (vertCount == 0 || p != aVerts[vertCount - 1]) {
909 aVerts[vertCount++] = p;
913 return vertCount;
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;
926 return matrix;
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);
942 template <class F>
943 Point4DTyped<TargetUnits, F> TransformPoint(
944 const Point4DTyped<SourceUnits, F>& aPoint) const {
945 Point4DTyped<TargetUnits, F> retPoint;
947 retPoint.x =
948 aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + aPoint.w * _41;
949 retPoint.y =
950 aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + aPoint.w * _42;
951 retPoint.z =
952 aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + aPoint.w * _43;
953 retPoint.w =
954 aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + aPoint.w * _44;
956 return retPoint;
959 template <class F>
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);
969 return result;
972 template <class F>
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();
979 template <class F>
980 GFX2D_API RectTyped<TargetUnits, F> TransformBounds(
981 const RectTyped<SourceUnits, F>& aRect) const {
982 PointTyped<TargetUnits, F> quad[4];
983 F min_x, max_x;
984 F min_y, max_y;
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) {
996 min_x = quad[i].x;
998 if (quad[i].x > max_x) {
999 max_x = quad[i].x;
1002 if (quad[i].y < min_y) {
1003 min_y = quad[i].y;
1005 if (quad[i].y > max_y) {
1006 max_y = quad[i].y;
1010 return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
1011 max_y - min_y);
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
1036 * the result of:
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
1044 * multiplications.)
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;
1052 return *this;
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
1069 * transformation.
1071 Matrix4x4Typed& PostTranslate(T aX, T aY, T aZ) {
1072 _11 += _14 * aX;
1073 _21 += _24 * aX;
1074 _31 += _34 * aX;
1075 _41 += _44 * aX;
1076 _12 += _14 * aY;
1077 _22 += _24 * aY;
1078 _32 += _34 * aY;
1079 _42 += _44 * aY;
1080 _13 += _14 * aZ;
1081 _23 += _24 * aZ;
1082 _33 += _34 * aZ;
1083 _43 += _44 * aZ;
1085 return *this;
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) {
1105 _11 *= aX;
1106 _12 *= aX;
1107 _13 *= aX;
1108 _14 *= aX;
1109 _21 *= aY;
1110 _22 *= aY;
1111 _23 *= aY;
1112 _24 *= aY;
1113 _31 *= aZ;
1114 _32 *= aZ;
1115 _33 *= aZ;
1116 _34 *= aZ;
1118 return *this;
1122 * Similar to PostTranslate, but applies a scale instead of a translation.
1124 Matrix4x4Typed& PostScale(T aScaleX, T aScaleY, T aScaleZ) {
1125 _11 *= aScaleX;
1126 _21 *= aScaleX;
1127 _31 *= aScaleX;
1128 _41 *= aScaleX;
1129 _12 *= aScaleY;
1130 _22 *= aScaleY;
1131 _32 *= aScaleY;
1132 _42 *= aScaleY;
1133 _13 *= aScaleZ;
1134 _23 *= aScaleZ;
1135 _33 *= aScaleZ;
1136 _43 *= aScaleZ;
1138 return *this;
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);
1158 return *this;
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);
1171 return *this;
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 +
1192 _14 * aMatrix._41;
1193 matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 +
1194 _24 * aMatrix._41;
1195 matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 +
1196 _34 * aMatrix._41;
1197 matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 +
1198 _44 * aMatrix._41;
1199 matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 +
1200 _14 * aMatrix._42;
1201 matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 +
1202 _24 * aMatrix._42;
1203 matrix._32 = _31 * aMatrix._12 + _32 * aMatrix._22 + _33 * aMatrix._32 +
1204 _34 * aMatrix._42;
1205 matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + _43 * aMatrix._32 +
1206 _44 * aMatrix._42;
1207 matrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23 + _13 * aMatrix._33 +
1208 _14 * aMatrix._43;
1209 matrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23 + _23 * aMatrix._33 +
1210 _24 * aMatrix._43;
1211 matrix._33 = _31 * aMatrix._13 + _32 * aMatrix._23 + _33 * aMatrix._33 +
1212 _34 * aMatrix._43;
1213 matrix._43 = _41 * aMatrix._13 + _42 * aMatrix._23 + _43 * aMatrix._33 +
1214 _44 * aMatrix._43;
1215 matrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24 + _13 * aMatrix._34 +
1216 _14 * aMatrix._44;
1217 matrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24 + _23 * aMatrix._34 +
1218 _24 * aMatrix._44;
1219 matrix._34 = _31 * aMatrix._14 + _32 * aMatrix._24 + _33 * aMatrix._34 +
1220 _34 * aMatrix._44;
1221 matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 +
1222 _44 * aMatrix._44;
1224 return matrix;
1227 Matrix4x4Typed& operator*=(
1228 const Matrix4x4Typed<TargetUnits, TargetUnits, T>& aMatrix) {
1229 *this = *this * aMatrix;
1230 return *this;
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.
1260 bool Invert() {
1261 T det = Determinant();
1262 if (!det) {
1263 return false;
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;
1300 result._11 /= det;
1301 result._12 /= det;
1302 result._13 /= det;
1303 result._14 /= det;
1304 result._21 /= det;
1305 result._22 /= det;
1306 result._23 /= det;
1307 result._24 /= det;
1308 result._31 /= det;
1309 result._32 /= det;
1310 result._33 /= det;
1311 result._34 /= det;
1312 result._41 /= det;
1313 result._42 /= det;
1314 result._43 /= det;
1315 result._44 /= det;
1316 *this = result;
1318 return true;
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");
1327 return clone;
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()) {
1334 return Some(clone);
1336 return Nothing();
1339 void Normalize() {
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);
1403 return *this;
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),
1409 *((&_31) + 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)) {
1426 return false;
1428 Matrix4x4Typed mat = *this;
1429 mat.Normalize();
1430 if (HasPerspectiveComponent()) {
1431 // We do not support projection matrices
1432 return false;
1435 // Extract translation
1436 translation.x = mat._41;
1437 translation.y = mat._42;
1438 translation.z = mat._43;
1440 // Remove translation
1441 mat._41 = 0.0f;
1442 mat._42 = 0.0f;
1443 mat._43 = 0.0f;
1445 // Extract scale
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);
1450 // Remove scale
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
1454 return false;
1457 // Extract rotation
1458 rotation.SetFromRotationMatrix(this->ToUnknownMatrix());
1459 return true;
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);
1472 _21 = xy - wz;
1473 _31 = xz + wy;
1474 _41 = 0.0f;
1476 _12 = xy + wz;
1477 _22 = 1.0f - (xx + zz);
1478 _32 = yz - wx;
1479 _42 = 0.0f;
1481 _13 = xz - wy;
1482 _23 = yz + wx;
1483 _33 = 1.0f - (xx + yy);
1484 _43 = 0.0f;
1486 _14 = _42 = _43 = 0.0f;
1487 _44 = 1.0f;
1490 // Set all the members of the matrix to NaN
1491 void SetNAN() {
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) &&
1517 std::isfinite(_44);
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);
1524 T temp;
1526 temp = _11;
1527 _11 += tanY * _21;
1528 _21 += tanX * temp;
1530 temp = _12;
1531 _12 += tanY * _22;
1532 _22 += tanX * temp;
1534 temp = _13;
1535 _13 += tanY * _23;
1536 _23 += tanX * temp;
1538 temp = _14;
1539 _14 += tanY * _24;
1540 _24 += tanX * temp;
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));
1548 T temp;
1550 temp = _21;
1551 _21 = cosTheta * _21 + sinTheta * _31;
1552 _31 = -sinTheta * temp + cosTheta * _31;
1554 temp = _22;
1555 _22 = cosTheta * _22 + sinTheta * _32;
1556 _32 = -sinTheta * temp + cosTheta * _32;
1558 temp = _23;
1559 _23 = cosTheta * _23 + sinTheta * _33;
1560 _33 = -sinTheta * temp + cosTheta * _33;
1562 temp = _24;
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));
1572 T temp;
1574 temp = _11;
1575 _11 = cosTheta * _11 + -sinTheta * _31;
1576 _31 = sinTheta * temp + cosTheta * _31;
1578 temp = _12;
1579 _12 = cosTheta * _12 + -sinTheta * _32;
1580 _32 = sinTheta * temp + cosTheta * _32;
1582 temp = _13;
1583 _13 = cosTheta * _13 + -sinTheta * _33;
1584 _33 = sinTheta * temp + cosTheta * _33;
1586 temp = _14;
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));
1596 T temp;
1598 temp = _11;
1599 _11 = cosTheta * _11 + sinTheta * _21;
1600 _21 = -sinTheta * temp + cosTheta * _21;
1602 temp = _12;
1603 _12 = cosTheta * _12 + sinTheta * _22;
1604 _22 = -sinTheta * temp + cosTheta * _22;
1606 temp = _13;
1607 _13 = cosTheta * _13 + sinTheta * _23;
1608 _23 = -sinTheta * temp + cosTheta * _23;
1610 temp = _14;
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()) {
1622 return;
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);
1641 _14 = 0.0f;
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);
1645 _24 = 0.0f;
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;
1649 _34 = 0.0f;
1650 _41 = 0.0f;
1651 _42 = 0.0f;
1652 _43 = 0.0f;
1653 _44 = 1.0f;
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 {
1715 MOZ_ASSERT(Is2D());
1716 if (gfx::FuzzyEqual(_12, 0) && gfx::FuzzyEqual(_21, 0)) {
1717 return true;
1718 } else if (gfx::FuzzyEqual(_22, 0) && gfx::FuzzyEqual(_11, 0)) {
1719 return true;
1721 return false;
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));
1747 return Nothing();
1751 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
1752 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits, double> Matrix4x4Double;
1754 class Matrix5x4 {
1755 public:
1756 Matrix5x4()
1757 : _11(1.0f),
1758 _12(0),
1759 _13(0),
1760 _14(0),
1761 _21(0),
1762 _22(1.0f),
1763 _23(0),
1764 _24(0),
1765 _31(0),
1766 _32(0),
1767 _33(1.0f),
1768 _34(0),
1769 _41(0),
1770 _42(0),
1771 _43(0),
1772 _44(1.0f),
1773 _51(0),
1774 _52(0),
1775 _53(0),
1776 _54(0) {}
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)
1781 : _11(a11),
1782 _12(a12),
1783 _13(a13),
1784 _14(a14),
1785 _21(a21),
1786 _22(a22),
1787 _23(a23),
1788 _24(a24),
1789 _31(a31),
1790 _32(a32),
1791 _33(a33),
1792 _34(a34),
1793 _41(a41),
1794 _42(a42),
1795 _43(a43),
1796 _44(a44),
1797 _51(a51),
1798 _52(a52),
1799 _53(a53),
1800 _54(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 +
1851 aMatrix._51;
1852 resultMatrix._52 = this->_51 * aMatrix._12 + this->_52 * aMatrix._22 +
1853 this->_53 * aMatrix._32 + this->_54 * aMatrix._42 +
1854 aMatrix._52;
1855 resultMatrix._53 = this->_51 * aMatrix._13 + this->_52 * aMatrix._23 +
1856 this->_53 * aMatrix._33 + this->_54 * aMatrix._43 +
1857 aMatrix._53;
1858 resultMatrix._54 = this->_51 * aMatrix._14 + this->_52 * aMatrix._24 +
1859 this->_53 * aMatrix._34 + this->_54 * aMatrix._44 +
1860 aMatrix._54;
1862 return resultMatrix;
1865 Matrix5x4& operator*=(const Matrix5x4& aMatrix) {
1866 *this = *this * aMatrix;
1867 return *this;
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] << ';';
1874 f += 4;
1875 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
1876 f += 4;
1877 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
1878 f += 4;
1879 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << ';';
1880 f += 4;
1881 aStream << ' ' << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3]
1882 << "; ]";
1883 return aStream;
1886 union {
1887 struct {
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> {
1907 public:
1908 using Parent = Matrix4x4Typed<SourceUnits, TargetUnits>;
1909 using TargetPoint = PointTyped<TargetUnits>;
1910 using Parent::_11;
1911 using Parent::_12;
1912 using Parent::_13;
1913 using Parent::_14;
1914 using Parent::_21;
1915 using Parent::_22;
1916 using Parent::_23;
1917 using Parent::_24;
1918 using Parent::_31;
1919 using Parent::_32;
1920 using Parent::_33;
1921 using Parent::_34;
1922 using Parent::_41;
1923 using Parent::_42;
1924 using Parent::_43;
1925 using Parent::_44;
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,
1932 Float a44)
1933 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41,
1934 a42, a43, a44) {
1935 Analyze();
1938 MOZ_IMPLICIT Matrix4x4TypedFlagged(const Parent& aOther) : Parent(aOther) {
1939 Analyze();
1942 template <class F>
1943 PointTyped<TargetUnits, F> TransformPoint(
1944 const PointTyped<SourceUnits, F>& aPoint) const {
1945 if (mType == MatrixType::Identity) {
1946 return aPoint;
1949 if (mType == MatrixType::Simple) {
1950 return TransformPointSimple(aPoint);
1953 return Parent::TransformPoint(aPoint);
1956 template <class F>
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) {
1991 return true;
1993 return Parent::FuzzyEqual(o);
1996 Matrix4x4TypedFlagged& PreTranslate(Float aX, Float aY, Float aZ) {
1997 if (mType == MatrixType::Identity) {
1998 _41 = aX;
1999 _42 = aY;
2000 _43 = aZ;
2002 if (!aZ) {
2003 mType = MatrixType::Simple;
2004 return *this;
2006 mType = MatrixType::Full;
2007 return *this;
2010 Parent::PreTranslate(aX, aY, aZ);
2012 if (aZ != 0) {
2013 mType = MatrixType::Full;
2016 return *this;
2019 Matrix4x4TypedFlagged& PostTranslate(Float aX, Float aY, Float aZ) {
2020 if (mType == MatrixType::Identity) {
2021 _41 = aX;
2022 _42 = aY;
2023 _43 = aZ;
2025 if (!aZ) {
2026 mType = MatrixType::Simple;
2027 return *this;
2029 mType = MatrixType::Full;
2030 return *this;
2033 Parent::PostTranslate(aX, aY, aZ);
2035 if (aZ != 0) {
2036 mType = MatrixType::Full;
2039 return *this;
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);
2049 return *this;
2052 bool IsIdentity() const { return mType == MatrixType::Identity; }
2054 template <class F>
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();
2073 return *this;
2076 bool IsSingular() const {
2077 if (mType == MatrixType::Identity) {
2078 return false;
2080 return Parent::Determinant() == 0.0;
2083 bool Invert() {
2084 if (mType == MatrixType::Identity) {
2085 return true;
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) {
2095 return clone;
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
2102 // change.
2103 return clone;
2106 template <typename NewTargetUnits>
2107 bool operator==(
2108 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2109 if (mType == MatrixType::Identity &&
2110 aMatrix.mType == MatrixType::Identity) {
2111 return true;
2113 // Depending on the usage it may make sense to compare more flags.
2114 return Parent::operator==(aMatrix);
2117 template <typename NewTargetUnits>
2118 bool operator!=(
2119 const Matrix4x4TypedFlagged<TargetUnits, NewTargetUnits>& aMatrix) const {
2120 if (mType == MatrixType::Identity &&
2121 aMatrix.mType == MatrixType::Identity) {
2122 return false;
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) {
2132 return aMatrix;
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;
2153 matrix.Analyze();
2154 return matrix;
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) {
2164 return aMatrix;
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;
2181 return matrix;
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;
2201 return matrix;
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;
2212 matrix._13 = _13;
2213 matrix._23 = _23;
2214 matrix._33 = _33;
2215 matrix._43 = _43;
2216 matrix._14 = _14;
2217 matrix._24 = _24;
2218 matrix._34 = _34;
2219 matrix._44 = _44;
2220 matrix.mType = MatrixType::Full;
2221 return matrix;
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) {
2231 if (aMatrix) {
2232 aMatrix->_11 = _11;
2233 aMatrix->_12 = _12;
2234 aMatrix->_21 = _21;
2235 aMatrix->_22 = _22;
2236 aMatrix->_31 = _41;
2237 aMatrix->_32 = _42;
2239 return true;
2241 return Parent::CanDraw2D(aMatrix);
2244 bool Is2D(Matrix* aMatrix) const {
2245 if (!Is2D()) {
2246 return false;
2248 if (aMatrix) {
2249 aMatrix->_11 = _11;
2250 aMatrix->_12 = _12;
2251 aMatrix->_21 = _21;
2252 aMatrix->_22 = _22;
2253 aMatrix->_31 = _41;
2254 aMatrix->_32 = _42;
2256 return true;
2259 template <class F>
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; }
2268 private:
2269 enum class MatrixType : uint8_t {
2270 Identity,
2271 Simple, // 2x3 Matrix
2272 Full // 4x4 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,
2278 Float a44,
2279 typename Matrix4x4TypedFlagged::MatrixType aType)
2280 : Parent(a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41,
2281 a42, a43, a44) {
2282 mType = aType;
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};
2297 template <class F>
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;
2303 return temp;
2306 void Analyze() {
2307 if (Parent::IsIdentity()) {
2308 mType = MatrixType::Identity;
2309 return;
2312 if (Parent::Is2D()) {
2313 mType = MatrixType::Simple;
2314 return;
2317 mType = MatrixType::Full;
2320 MatrixType mType;
2323 using Matrix4x4Flagged = Matrix4x4TypedFlagged<UnknownUnits, UnknownUnits>;
2325 } // namespace gfx
2326 } // namespace mozilla
2328 #endif /* MOZILLA_GFX_MATRIX_H_ */