Bug 1181240 - Part 1: Copy methods from gfx3DMatrix,r=vlad
authorKearwood (Kip) Gilbert <kgilbert@mozilla.com>
Thu, 09 Jul 2015 16:27:38 -0700
changeset 285996 24c93e2d0d6861c72b7f2959e3dba19540ed40d9
parent 285995 69542d63fcc858f402992155e870ceec0e350ba4
child 285997 5d03e5f9661453561fe206d4bbb5bdad72794098
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad
bugs1181240
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1181240 - Part 1: Copy methods from gfx3DMatrix,r=vlad - Copied methods from gfx3DMatrix to Matrix4x4, gfxPoint, and gfxRect that with at double-precision floating point.
gfx/2d/Matrix.cpp
gfx/2d/Matrix.h
gfx/thebes/gfxMatrix.cpp
gfx/thebes/gfxMatrix.h
gfx/thebes/gfxPoint.h
gfx/thebes/gfxRect.cpp
gfx/thebes/gfxRect.h
--- a/gfx/2d/Matrix.cpp
+++ b/gfx/2d/Matrix.cpp
@@ -4,21 +4,66 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Matrix.h"
 #include "Quaternion.h"
 #include "Tools.h"
 #include <algorithm>
 #include <ostream>
 #include <math.h>
+#include <float.h>  // for FLT_EPSILON
 
 #include "mozilla/FloatingPoint.h" // for UnspecifiedNaN
 
 using namespace std;
 
+namespace {
+
+/* Force small values to zero.  We do this to avoid having sin(360deg)
+ * evaluate to a tiny but nonzero value.
+ */
+double
+FlushToZero(double aVal)
+{
+  // XXX Is double precision really necessary here
+  if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON) {
+    return 0.0f;
+  } else {
+    return aVal;
+  }
+}
+
+/* Computes tan(aTheta).  For values of aTheta such that tan(aTheta) is
+ * undefined or very large, SafeTangent returns a manageably large value
+ * of the correct sign.
+ */
+double
+SafeTangent(double aTheta)
+{
+  // XXX Is double precision really necessary here
+  const double kEpsilon = 0.0001;
+
+  /* tan(theta) = sin(theta)/cos(theta); problems arise when
+   * cos(theta) is too close to zero.  Limit cos(theta) to the
+   * range [-1, -epsilon] U [epsilon, 1].
+   */
+
+  double sinTheta = sin(aTheta);
+  double cosTheta = cos(aTheta);
+
+  if (cosTheta >= 0 && cosTheta < kEpsilon) {
+    cosTheta = kEpsilon;
+  } else if (cosTheta < 0 && cosTheta >= -kEpsilon) {
+    cosTheta = -kEpsilon;
+  }
+  return FlushToZero(sinTheta / cosTheta);
+}
+
+} // namespace
+
 namespace mozilla {
 namespace gfx {
 
 std::ostream&
 operator<<(std::ostream& aStream, const Matrix& aMatrix)
 {
   return aStream << "[ " << aMatrix._11
                  << " "  << aMatrix._12
@@ -392,10 +437,139 @@ Matrix4x4::SetRotationFromQuaternion(con
   _23 = yz - wx;
   _33 = 1.0f - (xx + yy);
   _43 = 0.0f;
 
   _14 = _42 = _43 = 0.0f;
   _44 = 1.0f;
 }
 
+void
+Matrix4x4::SkewXY(double aXSkew, double aYSkew)
+{
+  // XXX Is double precision really necessary here
+  float tanX = SafeTangent(aXSkew);
+  float tanY = SafeTangent(aYSkew);
+  float temp;
+
+  temp = _11;
+  _11 += tanY * _21;
+  _21 += tanX * temp;
+
+  temp = _12;
+  _12 += tanY * _22;
+  _22 += tanX * temp;
+
+  temp = _13;
+  _13 += tanY * _23;
+  _23 += tanX * temp;
+
+  temp = _14;
+  _14 += tanY * _24;
+  _24 += tanX * temp;
+}
+
+void
+Matrix4x4::RotateX(double aTheta)
+{
+  // XXX Is double precision really necessary here
+  double cosTheta = FlushToZero(cos(aTheta));
+  double sinTheta = FlushToZero(sin(aTheta));
+
+  float temp;
+
+  temp = _21;
+  _21 = cosTheta * _21 + sinTheta * _31;
+  _31 = -sinTheta * temp + cosTheta * _31;
+
+  temp = _22;
+  _22 = cosTheta * _22 + sinTheta * _32;
+  _32 = -sinTheta * temp + cosTheta * _32;
+
+  temp = _23;
+  _23 = cosTheta * _23 + sinTheta * _33;
+  _33 = -sinTheta * temp + cosTheta * _33;
+
+  temp = _24;
+  _24 = cosTheta * _24 + sinTheta * _34;
+  _34 = -sinTheta * temp + cosTheta * _34;
+}
+
+void
+Matrix4x4::RotateY(double aTheta)
+{
+  // XXX Is double precision really necessary here
+  double cosTheta = FlushToZero(cos(aTheta));
+  double sinTheta = FlushToZero(sin(aTheta));
+
+  float temp;
+
+  temp = _11;
+  _11 = cosTheta * _11 + -sinTheta * _31;
+  _31 = sinTheta * temp + cosTheta * _31;
+
+  temp = _12;
+  _12 = cosTheta * _12 + -sinTheta * _32;
+  _32 = sinTheta * temp + cosTheta * _32;
+
+  temp = _13;
+  _13 = cosTheta * _13 + -sinTheta * _33;
+  _33 = sinTheta * temp + cosTheta * _33;
+
+  temp = _14;
+  _14 = cosTheta * _14 + -sinTheta * _34;
+  _34 = sinTheta * temp + cosTheta * _34;
+}
+
+void
+Matrix4x4::RotateZ(double aTheta)
+{
+  // XXX Is double precision really necessary here
+  double cosTheta = FlushToZero(cos(aTheta));
+  double sinTheta = FlushToZero(sin(aTheta));
+
+  float temp;
+
+  temp = _11;
+  _11 = cosTheta * _11 + sinTheta * _21;
+  _21 = -sinTheta * temp + cosTheta * _21;
+
+  temp = _12;
+  _12 = cosTheta * _12 + sinTheta * _22;
+  _22 = -sinTheta * temp + cosTheta * _22;
+
+  temp = _13;
+  _13 = cosTheta * _13 + sinTheta * _23;
+  _23 = -sinTheta * temp + cosTheta * _23;
+
+  temp = _14;
+  _14 = cosTheta * _14 + sinTheta * _24;
+  _24 = -sinTheta * temp + cosTheta * _24;
+}
+
+void
+Matrix4x4::Perspective(float aDepth)
+{
+  MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!");
+  _31 += -1.0/aDepth * _41;
+  _32 += -1.0/aDepth * _42;
+  _33 += -1.0/aDepth * _43;
+  _34 += -1.0/aDepth * _44;
+}
+
+Point3D
+Matrix4x4::GetNormalVector() const
+{
+  // Define a plane in transformed space as the transformations
+  // of 3 points on the z=0 screen plane.
+  Point3D a = *this * Point3D(0, 0, 0);
+  Point3D b = *this * Point3D(0, 1, 0);
+  Point3D c = *this * Point3D(1, 0, 0);
+
+  // Convert to two vectors on the surface of the plane.
+  Point3D ab = b - a;
+  Point3D ac = c - a;
+
+  return ac.CrossProduct(ab);
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -711,16 +711,21 @@ public:
       (*this)[2] += (*this)[0] * aSkew;
   }
 
   void SkewYZ(Float aSkew)
   {
       (*this)[2] += (*this)[1] * aSkew;
   }
 
+  Matrix4x4 &ChangeBasis(const Point3D& aOrigin)
+  {
+    return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z);
+  }
+
   Matrix4x4 &ChangeBasis(Float aX, Float aY, Float aZ)
   {
     // Translate to the origin before applying this matrix
     PreTranslate(-aX, -aY, -aZ);
 
     // Translate back into position after applying this matrix
     PostTranslate(aX, aY, aZ);
 
@@ -945,16 +950,28 @@ public:
 
   // Sets this matrix to a rotation matrix given by aQuat.
   // This quaternion *MUST* be normalized!
   // Implemented in Quaternion.cpp
   void SetRotationFromQuaternion(const Quaternion& aQuat);
 
   // Set all the members of the matrix to NaN
   void SetNAN();
+
+  void SkewXY(double aXSkew, double aYSkew);
+
+  void RotateX(double aTheta);
+
+  void RotateY(double aTheta);
+
+  void RotateZ(double aTheta);
+
+  void Perspective(float aDepth);
+
+  Point3D GetNormalVector() const;
 };
 
 class Matrix5x4
 {
 public:
   Matrix5x4()
     : _11(1.0f), _12(0), _13(0), _14(0)
     , _21(0), _22(1.0f), _23(0), _24(0)
--- a/gfx/thebes/gfxMatrix.cpp
+++ b/gfx/thebes/gfxMatrix.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxMatrix.h"
 #include "cairo.h"
 #include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
 
 #define CAIRO_MATRIX(x) reinterpret_cast<cairo_matrix_t*>((x))
 #define CONST_CAIRO_MATRIX(x) reinterpret_cast<const cairo_matrix_t*>((x))
 
 const gfxMatrix&
 gfxMatrix::Reset()
 {
     cairo_matrix_init_identity(CAIRO_MATRIX(this));
@@ -153,8 +154,36 @@ gfxMatrix::NudgeToIntegers(void)
     NudgeToInteger(&_11);
     NudgeToInteger(&_21);
     NudgeToInteger(&_12);
     NudgeToInteger(&_22);
     NudgeToInteger(&_31);
     NudgeToInteger(&_32);
     return *this;
 }
+
+mozilla::gfx::Matrix4x4
+gfxMatrix::operator *(const mozilla::gfx::Matrix4x4& aMatrix) const
+{
+  Matrix4x4 resultMatrix;
+
+  resultMatrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
+  resultMatrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
+  resultMatrix._13 = _11 * aMatrix._13 + _12 * aMatrix._23;
+  resultMatrix._14 = _11 * aMatrix._14 + _12 * aMatrix._24;
+
+  resultMatrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
+  resultMatrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
+  resultMatrix._23 = _21 * aMatrix._13 + _22 * aMatrix._23;
+  resultMatrix._24 = _21 * aMatrix._14 + _22 * aMatrix._24;
+
+  resultMatrix._31 = aMatrix._31;
+  resultMatrix._32 = aMatrix._32;
+  resultMatrix._33 = aMatrix._33;
+  resultMatrix._34 = aMatrix._34;
+
+  resultMatrix._41 = _31 * aMatrix._11 + _32 * aMatrix._21 + aMatrix._41;
+  resultMatrix._42 = _31 * aMatrix._12 + _32 * aMatrix._22 + aMatrix._42;
+  resultMatrix._43 = _31 * aMatrix._13 + _32 * aMatrix._23 + aMatrix._43;
+  resultMatrix._44 = _31 * aMatrix._14 + _32 * aMatrix._24 + aMatrix._44;
+
+  return resultMatrix;
+}
--- a/gfx/thebes/gfxMatrix.h
+++ b/gfx/thebes/gfxMatrix.h
@@ -6,16 +6,22 @@
 #ifndef GFX_MATRIX_H
 #define GFX_MATRIX_H
 
 #include "gfxPoint.h"
 #include "gfxTypes.h"
 #include "gfxRect.h"
 #include "mozilla/Attributes.h"
 
+namespace mozilla {
+namespace gfx {
+class Matrix4x4;
+} // namespace gfx
+} // namespace mozilla
+
 // XX - I don't think this class should use gfxFloat at all,
 // but should use 'double' and be called gfxDoubleMatrix;
 // we can then typedef that to gfxMatrix where we typedef
 // double to be gfxFloat.
 
 /**
  * A matrix that represents an affine transformation. Projective
  * transformations are not supported. This matrix looks like:
@@ -74,16 +80,21 @@ public:
 
     /**
      * Multiplies *this with m and returns the result.
      */
     gfxMatrix operator * (const gfxMatrix& m) const {
         return gfxMatrix(*this) *= m;
     }
 
+    /**
+     * Multiplies *this with aMatrix and returns the result.
+     */
+    mozilla::gfx::Matrix4x4 operator * (const mozilla::gfx::Matrix4x4& aMatrix) const;
+
     /* Returns true if the other matrix is fuzzy-equal to this matrix.
      * Note that this isn't a cheap comparison!
      */
     bool operator==(const gfxMatrix& other) const
     {
       return FuzzyEqual(_11, other._11) && FuzzyEqual(_12, other._12) &&
              FuzzyEqual(_21, other._21) && FuzzyEqual(_22, other._22) &&
              FuzzyEqual(_31, other._31) && FuzzyEqual(_32, other._32);
--- a/gfx/thebes/gfxPoint.h
+++ b/gfx/thebes/gfxPoint.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_POINT_H
 #define GFX_POINT_H
 
 #include "nsMathUtils.h"
 #include "mozilla/gfx/BaseSize.h"
 #include "mozilla/gfx/BasePoint.h"
+#include "mozilla/gfx/Matrix.h"
 #include "nsSize.h"
 #include "nsPoint.h"
 
 #include "gfxTypes.h"
 
 struct gfxSize : public mozilla::gfx::BaseSize<gfxFloat, gfxSize> {
     typedef mozilla::gfx::BaseSize<gfxFloat, gfxSize> Super;
 
@@ -27,16 +28,30 @@ struct gfxPoint : public mozilla::gfx::B
 
     gfxPoint() : Super() {}
     gfxPoint(gfxFloat aX, gfxFloat aY) : Super(aX, aY) {}
     MOZ_IMPLICIT gfxPoint(const nsIntPoint& aPoint) : Super(aPoint.x, aPoint.y) {}
 
     bool WithinEpsilonOf(const gfxPoint& aPoint, gfxFloat aEpsilon) {
         return fabs(aPoint.x - x) < aEpsilon && fabs(aPoint.y - y) < aEpsilon;
     }
+
+    void Transform(const mozilla::gfx::Matrix4x4 &aMatrix)
+    {
+      // Transform this point with aMatrix
+      double px = x;
+      double py = y;
+
+      x = px * aMatrix._11 + py * aMatrix._21 + aMatrix._41;
+      y = px * aMatrix._12 + py * aMatrix._22 + aMatrix._42;
+
+      double w = px * aMatrix._14 + py * aMatrix._24 + aMatrix._44;
+      x /= w;
+      y /= w;
+    }
 };
 
 inline gfxPoint
 operator*(const gfxPoint& aPoint, const gfxSize& aSize)
 {
   return gfxPoint(aPoint.x * aSize.width, aPoint.y * aSize.height);
 }
 
--- a/gfx/thebes/gfxRect.cpp
+++ b/gfx/thebes/gfxRect.cpp
@@ -2,16 +2,69 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxRect.h"
 
 #include "nsMathUtils.h"
 
+#include "mozilla/gfx/Matrix.h"
+
+#include "gfxQuad.h"
+
+gfxQuad
+gfxRect::TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const
+{
+  gfxPoint points[4];
+
+  points[0] = TopLeft();
+  points[1] = TopRight();
+  points[2] = BottomRight();
+  points[3] = BottomLeft();
+
+  points[0].Transform(aMatrix);
+  points[1].Transform(aMatrix);
+  points[2].Transform(aMatrix);
+  points[3].Transform(aMatrix);
+
+  // Could this ever result in lines that intersect? I don't think so.
+  return gfxQuad(points[0], points[1], points[2], points[3]);
+}
+
+void
+gfxRect::TransformBounds(const mozilla::gfx::Matrix4x4 &aMatrix)
+{
+  gfxPoint quad[4];
+
+  quad[0] = TopLeft();
+  quad[1] = TopRight();
+  quad[2] = BottomLeft();
+  quad[3] = BottomRight();
+
+  quad[0].Transform(aMatrix);
+  double min_x = quad[0].x;
+  double max_x = quad[0].x;
+  double min_y = quad[0].y;
+  double max_y = quad[0].y;
+
+  for (int i=1; i<4; i++) {
+    quad[i].Transform(aMatrix);
+    min_x = std::min(quad[i].x, min_x);
+    max_x = std::max(quad[i].x, max_x);
+    min_y = std::min(quad[i].y, min_y);
+    max_y = std::max(quad[i].y, max_y);
+  }
+
+  x = min_x;
+  y = min_y;
+  width = max_x - min_x;
+  height = max_y - min_y;
+}
+
 static bool
 WithinEpsilonOfInteger(gfxFloat aX, gfxFloat aEpsilon)
 {
     return fabs(NS_round(aX) - aX) <= fabs(aEpsilon);
 }
 
 bool
 gfxRect::WithinEpsilonOfIntegerPixels(gfxFloat aEpsilon) const
--- a/gfx/thebes/gfxRect.h
+++ b/gfx/thebes/gfxRect.h
@@ -9,16 +9,24 @@
 #include "gfxTypes.h"
 #include "gfxPoint.h"
 #include "nsDebug.h"
 #include "nsRect.h"
 #include "mozilla/gfx/BaseMargin.h"
 #include "mozilla/gfx/BaseRect.h"
 #include "mozilla/Assertions.h"
 
+namespace mozilla {
+namespace gfx {
+class Matrix4x4;
+} // namepsace gfx
+} // namespace mozilla
+
+struct gfxQuad;
+
 struct gfxMargin : public mozilla::gfx::BaseMargin<gfxFloat, gfxMargin> {
   typedef mozilla::gfx::BaseMargin<gfxFloat, gfxMargin> Super;
 
   // Constructors
   gfxMargin() : Super() {}
   gfxMargin(const gfxMargin& aMargin) : Super(aMargin) {}
   gfxMargin(gfxFloat aTop, gfxFloat aRight, gfxFloat aBottom, gfxFloat aLeft)
     : Super(aTop, aRight, aBottom, aLeft) {}
@@ -132,11 +140,21 @@ struct gfxRect :
 
     void ScaleInverse(gfxFloat k) {
         NS_ASSERTION(k > 0.0, "Invalid (negative) scale factor");
         x /= k;
         y /= k;
         width /= k;
         height /= k;
     }
+
+    /*
+     * Transform this rectangle with aMatrix, resulting in a gfxQuad.
+     */
+    gfxQuad TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const;
+
+    /*
+     * Transform this rectangle with aMatrix, as an axis-aligned bounding box
+     */
+    void TransformBounds(const mozilla::gfx::Matrix4x4 &aMatrix);
 };
 
 #endif /* GFX_RECT_H */