Bug 1377090 - Further generify the Matrix class and define MatrixDouble. r?Bas draft
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 05 Jul 2017 11:18:49 -0400
changeset 604207 9db62d86e90517fad5f243efc6987766123bab3a
parent 604206 83180176cfc71e687cad0f17f2938cab1610c16c
child 604208 fe7517b60be88f07b49c45c365e728ae4dc26dad
push id67001
push userkgupta@mozilla.com
push dateWed, 05 Jul 2017 15:19:48 +0000
reviewersBas
bugs1377090
milestone56.0a1
Bug 1377090 - Further generify the Matrix class and define MatrixDouble. r?Bas To allow MatrixDouble to be a drop-in replacement for gfxMatrix, it needs to accept the "double" versions of Point, Rect, and Size. This patch does that by adding some extra typedefs inside BaseMatrix to abstract over that. It also moves some function implementations into the .h file as they don't need specialization. I left some function implementations in the Matrix.cpp file: - Rotation, because it is specialized for Float and Double, since it uses sinf/cosf vs sin/cos in the two implementations. - The Matrix4x4 multiplication operator overload, because if I put it inside the BaseMatrix class declaration Matrix4x4 isn't defined yet and the compiler doesn't like it. MozReview-Commit-ID: K56dZjJhXWS
gfx/2d/Matrix.cpp
gfx/2d/Matrix.h
gfx/2d/MatrixFwd.h
--- a/gfx/2d/Matrix.cpp
+++ b/gfx/2d/Matrix.cpp
@@ -70,53 +70,54 @@ Matrix::Rotation(Float aAngle)
   newMatrix._11 = c;
   newMatrix._12 = s;
   newMatrix._21 = -s;
   newMatrix._22 = c;
 
   return newMatrix;
 }
 
-template<> Rect
-Matrix::TransformBounds(const Rect &aRect) const
+template<> MatrixDouble
+MatrixDouble::Rotation(Double aAngle)
 {
-  int i;
-  Point quad[4];
-  Float min_x, max_x;
-  Float min_y, max_y;
-
-  quad[0] = TransformPoint(aRect.TopLeft());
-  quad[1] = TransformPoint(aRect.TopRight());
-  quad[2] = TransformPoint(aRect.BottomLeft());
-  quad[3] = TransformPoint(aRect.BottomRight());
+  MatrixDouble newMatrix;
 
-  min_x = max_x = quad[0].x;
-  min_y = max_y = quad[0].y;
+  Double s = sin(aAngle);
+  Double c = cos(aAngle);
 
-  for (i = 1; i < 4; i++) {
-    if (quad[i].x < min_x)
-      min_x = quad[i].x;
-    if (quad[i].x > max_x)
-      max_x = quad[i].x;
+  newMatrix._11 = c;
+  newMatrix._12 = s;
+  newMatrix._21 = -s;
+  newMatrix._22 = c;
 
-    if (quad[i].y < min_y)
-      min_y = quad[i].y;
-    if (quad[i].y > max_y)
-      max_y = quad[i].y;
-  }
-
-  return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
+  return newMatrix;
 }
 
-template<> Matrix&
-Matrix::NudgeToIntegers()
+template<> Matrix4x4
+MatrixDouble::operator*(const Matrix4x4& aMatrix) const
 {
-  NudgeToInteger(&_11);
-  NudgeToInteger(&_12);
-  NudgeToInteger(&_21);
-  NudgeToInteger(&_22);
-  NudgeToInteger(&_31);
-  NudgeToInteger(&_32);
-  return *this;
+  Matrix4x4 resultMatrix;
+
+  resultMatrix._11 = this->_11 * aMatrix._11 + this->_12 * aMatrix._21;
+  resultMatrix._12 = this->_11 * aMatrix._12 + this->_12 * aMatrix._22;
+  resultMatrix._13 = this->_11 * aMatrix._13 + this->_12 * aMatrix._23;
+  resultMatrix._14 = this->_11 * aMatrix._14 + this->_12 * aMatrix._24;
+
+  resultMatrix._21 = this->_21 * aMatrix._11 + this->_22 * aMatrix._21;
+  resultMatrix._22 = this->_21 * aMatrix._12 + this->_22 * aMatrix._22;
+  resultMatrix._23 = this->_21 * aMatrix._13 + this->_22 * aMatrix._23;
+  resultMatrix._24 = this->_21 * aMatrix._14 + this->_22 * aMatrix._24;
+
+  resultMatrix._31 = aMatrix._31;
+  resultMatrix._32 = aMatrix._32;
+  resultMatrix._33 = aMatrix._33;
+  resultMatrix._34 = aMatrix._34;
+
+  resultMatrix._41 = this->_31 * aMatrix._11 + this->_32 * aMatrix._21 + aMatrix._41;
+  resultMatrix._42 = this->_31 * aMatrix._12 + this->_32 * aMatrix._22 + aMatrix._42;
+  resultMatrix._43 = this->_31 * aMatrix._13 + this->_32 * aMatrix._23 + aMatrix._43;
+  resultMatrix._44 = this->_31 * aMatrix._14 + this->_32 * aMatrix._24 + aMatrix._44;
+
+  return resultMatrix;
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -23,16 +23,23 @@ namespace gfx {
 static inline bool FuzzyEqual(Float aV1, Float aV2) {
   // XXX - Check if fabs does the smart thing and just negates the sign bit.
   return fabs(aV2 - aV1) < 1e-6;
 }
 
 template<class T>
 class BaseMatrix
 {
+  // Alias that maps to either Point or PointDouble depending on whether T is a
+  // float or a double.
+  typedef PointTyped<UnknownUnits, T> MatrixPoint;
+  // Same for size and rect
+  typedef SizeTyped<UnknownUnits, T> MatrixSize;
+  typedef RectTyped<UnknownUnits, T> MatrixRect;
+
 public:
   BaseMatrix()
     : _11(1.0f), _12(0)
     , _21(0), _22(1.0f)
     , _31(0), _32(0)
   {}
   BaseMatrix(T a11, T a12, T a21, T a22, T a31, T a32)
     : _11(a11), _12(a12)
@@ -59,44 +66,72 @@ public:
                    << " "  << aMatrix._12
                    << "; " << aMatrix._21
                    << " "  << aMatrix._22
                    << "; " << aMatrix._31
                    << " "  << aMatrix._32
                    << "; ]";
   }
 
-  Point TransformPoint(const Point &aPoint) const
+  MatrixPoint TransformPoint(const MatrixPoint &aPoint) const
   {
-    Point retPoint;
+    MatrixPoint retPoint;
 
     retPoint.x = aPoint.x * _11 + aPoint.y * _21 + _31;
     retPoint.y = aPoint.x * _12 + aPoint.y * _22 + _32;
 
     return retPoint;
   }
 
-  Size TransformSize(const Size &aSize) const
+  MatrixSize TransformSize(const MatrixSize &aSize) const
   {
-    Size retSize;
+    MatrixSize retSize;
 
     retSize.width = aSize.width * _11 + aSize.height * _21;
     retSize.height = aSize.width * _12 + aSize.height * _22;
 
     return retSize;
   }
 
-  GFX2D_API Rect TransformBounds(const Rect& aRect) const;
+  GFX2D_API MatrixRect TransformBounds(const MatrixRect& aRect) const
+  {
+    int i;
+    MatrixPoint quad[4];
+    T min_x, max_x;
+    T min_y, max_y;
+
+    quad[0] = TransformPoint(aRect.TopLeft());
+    quad[1] = TransformPoint(aRect.TopRight());
+    quad[2] = TransformPoint(aRect.BottomLeft());
+    quad[3] = TransformPoint(aRect.BottomRight());
+
+    min_x = max_x = quad[0].x;
+    min_y = max_y = quad[0].y;
+
+    for (i = 1; i < 4; i++) {
+      if (quad[i].x < min_x)
+        min_x = quad[i].x;
+      if (quad[i].x > max_x)
+        max_x = quad[i].x;
+
+      if (quad[i].y < min_y)
+        min_y = quad[i].y;
+      if (quad[i].y > max_y)
+        max_y = quad[i].y;
+    }
+
+    return MatrixRect(min_x, min_y, max_x - min_x, max_y - min_y);
+  }
 
   static BaseMatrix<T> Translation(T aX, T aY)
   {
     return BaseMatrix<T>(1.0f, 0.0f, 0.0f, 1.0f, aX, aY);
   }
 
-  static BaseMatrix<T> Translation(Point aPoint)
+  static BaseMatrix<T> Translation(MatrixPoint aPoint)
   {
     return Translation(aPoint.x, aPoint.y);
   }
 
   /**
    * Apply a translation to this matrix.
    *
    * The "Pre" in this method's name means that the translation is applied
@@ -118,17 +153,17 @@ public:
   BaseMatrix<T> &PreTranslate(T aX, T aY)
   {
     _31 += _11 * aX + _21 * aY;
     _32 += _12 * aX + _22 * aY;
 
     return *this;
   }
 
-  BaseMatrix<T> &PreTranslate(const Point &aPoint)
+  BaseMatrix<T> &PreTranslate(const MatrixPoint &aPoint)
   {
     return PreTranslate(aPoint.x, aPoint.y);
   }
 
   /**
    * Similar to PreTranslate, but the translation is applied -after- this
    * matrix's existing transformation instead of before it.
    *
@@ -142,17 +177,17 @@ public:
    */
   BaseMatrix<T> &PostTranslate(T aX, T aY)
   {
     _31 += aX;
     _32 += aY;
     return *this;
   }
 
-  BaseMatrix<T> &PostTranslate(const Point &aPoint)
+  BaseMatrix<T> &PostTranslate(const MatrixPoint &aPoint)
   {
     return PostTranslate(aPoint.x, aPoint.y);
   }
 
   static BaseMatrix<T> Scaling(T aScaleX, T aScaleY)
   {
     return BaseMatrix<T>(aScaleX, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f);
   }
@@ -252,16 +287,21 @@ public:
 
   BaseMatrix<T>& operator*=(const BaseMatrix<T> &aMatrix)
   {
     *this = *this * aMatrix;
     return *this;
   }
 
   /**
+   * Multiplies *this with aMatrix and returns the result.
+   */
+  Matrix4x4 operator*(const Matrix4x4& aMatrix) const;
+
+  /**
    * Multiplies in the opposite order to operator=*.
    */
   BaseMatrix<T> &PreMultiply(const BaseMatrix<T> &aMatrix)
   {
     *this = aMatrix * *this;
     return *this;
   }
 
@@ -356,17 +396,26 @@ public:
   /* Returns true if the matrix is singular.
    */
   bool IsSingular() const
   {
     T det = Determinant();
     return !mozilla::IsFinite(det) || det == 0;
   }
 
-  GFX2D_API BaseMatrix<T> &NudgeToIntegers();
+  GFX2D_API BaseMatrix<T>& NudgeToIntegers()
+  {
+    NudgeToInteger(&_11);
+    NudgeToInteger(&_12);
+    NudgeToInteger(&_21);
+    NudgeToInteger(&_22);
+    NudgeToInteger(&_31);
+    NudgeToInteger(&_32);
+    return *this;
+  }
 
   bool IsTranslation() const
   {
     return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) &&
            FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f);
   }
 
   static bool FuzzyIsInteger(T aValue)
@@ -381,18 +430,18 @@ public:
 
   bool IsAllIntegers() const
   {
     return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) &&
            FuzzyIsInteger(_21) && FuzzyIsInteger(_22) &&
            FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
   }
 
-  Point GetTranslation() const {
-    return Point(_31, _32);
+  MatrixPoint GetTranslation() const {
+    return MatrixPoint(_31, _32);
   }
 
   /**
    * Returns true if matrix is multiple of 90 degrees rotation with flipping,
    * scaling and translation.
    */
   bool PreservesAxisAlignedRectangles() const {
       return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0))
@@ -409,19 +458,55 @@ public:
   }
 
   /**
    * Returns true if the matrix has negative scaling (i.e. flip).
    */
   bool HasNegativeScaling() const {
       return (_11 < 0.0) || (_22 < 0.0);
   }
+
+  /**
+   * Computes the scale factors of this matrix; that is,
+   * the amounts each basis vector is scaled by.
+   * The xMajor parameter indicates if the larger scale is
+   * to be assumed to be in the X direction or not.
+   */
+  MatrixSize ScaleFactors(bool xMajor) const {
+    T det = Determinant();
+
+    if (det == 0.0) {
+      return MatrixSize(0.0, 0.0);
+    }
+
+    MatrixSize sz = xMajor ? MatrixSize(1.0, 0.0) : MatrixSize(0.0, 1.0);
+    sz = TransformSize(sz);
+
+    T major = sqrt(sz.width * sz.width + sz.height * sz.height);
+    T minor = 0.0;
+
+    // ignore mirroring
+    if (det < 0.0) {
+      det = - det;
+    }
+
+    if (major) {
+      minor = det / major;
+    }
+
+    if (xMajor) {
+      return MatrixSize(major, minor);
+    }
+
+    return MatrixSize(minor, major);
+  }
 };
 
 typedef BaseMatrix<Float> Matrix;
+typedef BaseMatrix<Double> MatrixDouble;
 
 // Helper functions used by Matrix4x4Typed defined in Matrix.cpp
 double
 SafeTangent(double aTheta);
 double
 FlushToZero(double aVal);
 
 template<class Units, class F>
--- a/gfx/2d/MatrixFwd.h
+++ b/gfx/2d/MatrixFwd.h
@@ -14,16 +14,19 @@ namespace mozilla {
 namespace gfx {
 
 template<class T>
 class BaseMatrix;
 
 typedef float Float;
 typedef BaseMatrix<Float> Matrix;
 
+typedef double Double;
+typedef BaseMatrix<Double> MatrixDouble;
+
 struct UnknownUnits;
 
 template<class SourceUnits, class TargetUnits>
 class Matrix4x4Typed;
 
 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
 
 } // namespace gfx