Bug 952977: Convert SnapTransforms to gfx::Matrix r=nical
authorDavid Zbarsky <dzbarsky@gmail.com>
Mon, 27 Jan 2014 10:27:20 -0500
changeset 181342 7f4f11fa70cfddd35b77e08ec871c3753e073e04
parent 181341 8e6349f0a18d107eacad11f4bca64d8acc96b663
child 181343 17e04ba8fe305814bedfa6aef56ebb415defb1dc
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs952977
milestone29.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 952977: Convert SnapTransforms to gfx::Matrix r=nical
gfx/2d/Matrix.h
gfx/layers/ImageLayers.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/ReadbackLayer.h
gfx/layers/basic/BasicContainerLayer.cpp
gfx/layers/composite/ImageLayerComposite.cpp
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -201,16 +201,20 @@ public:
 
   bool IsIntegerTranslation() const
   {
     return IsTranslation() &&
            FuzzyEqual(_31, floorf(_31 + 0.5f)) &&
            FuzzyEqual(_32, floorf(_32 + 0.5f));
   }
 
+  Point GetTranslation() const {
+    return Point(_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))
           || (FuzzyEqual(_12, 0.0) && FuzzyEqual(_21, 0.0)));
   }
@@ -254,23 +258,49 @@ public:
         _23 != 0.0f || _24 != 0.0f ||
         _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f ||
         _43 != 0.0f || _44 != 1.0f) {
       return false;
     }
     return true;
   }
 
+  bool Is2D(Matrix* aMatrix) const {
+    if (!Is2D()) {
+      return false;
+    }
+    if (aMatrix) {
+      aMatrix->_11 = _11;
+      aMatrix->_12 = _12;
+      aMatrix->_21 = _21;
+      aMatrix->_22 = _22;
+      aMatrix->_31 = _41;
+      aMatrix->_32 = _42;
+    }
+    return true;
+  }
+
   Matrix As2D() const
   {
     MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform");
 
     return Matrix(_11, _12, _21, _22, _41, _42);
   }
 
+  static Matrix4x4 From2D(const Matrix &aMatrix) {
+    Matrix4x4 matrix;
+    matrix._11 = aMatrix._11;
+    matrix._12 = aMatrix._12;
+    matrix._21 = aMatrix._21;
+    matrix._22 = aMatrix._22;
+    matrix._41 = aMatrix._31;
+    matrix._42 = aMatrix._32;
+    return matrix;
+  }
+
   bool Is2DIntegerTranslation() const
   {
     return Is2D() && As2D().IsIntegerTranslation();
   }
 
   // Apply a scale to this matrix. This scale will be applied -before- the
   // existing transformation of the matrix.
   Matrix4x4 &Scale(Float aX, Float aY, Float aZ)
--- a/gfx/layers/ImageLayers.cpp
+++ b/gfx/layers/ImageLayers.cpp
@@ -24,17 +24,18 @@ ImageLayer::~ImageLayer()
 
 void ImageLayer::SetContainer(ImageContainer* aContainer) 
 {
   mContainer = aContainer;
 }
 
 void ImageLayer::ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
 {
-  gfx3DMatrix local = GetLocalTransform();
+  gfx::Matrix4x4 local;
+  gfx::ToMatrix4x4(GetLocalTransform(), local);
 
   // Snap image edges to pixel boundaries
   gfxRect sourceRect(0, 0, 0, 0);
   if (mContainer) {
     sourceRect.SizeTo(gfx::ThebesIntSize(mContainer->GetCurrentSize()));
     if (mScaleMode != ScaleMode::SCALE_NONE &&
         sourceRect.width != 0.0 && sourceRect.height != 0.0) {
       NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
@@ -42,17 +43,18 @@ void ImageLayer::ComputeEffectiveTransfo
       local.Scale(mScaleToSize.width / sourceRect.width,
                   mScaleToSize.height / sourceRect.height, 1.0);
     }
   }
   // Snap our local transform first, and snap the inherited transform as well.
   // This makes our snapping equivalent to what would happen if our content
   // was drawn into a ThebesLayer (gfxContext would snap using the local
   // transform, then we'd snap again when compositing the ThebesLayer).
-  gfx3DMatrix snappedTransform =
+  gfx::Matrix4x4 transformToSurface;
+  gfx::ToMatrix4x4(aTransformToSurface, transformToSurface);
+  mEffectiveTransform =
       SnapTransform(local, sourceRect, nullptr) *
-      SnapTransformTranslation(aTransformToSurface, nullptr);
-  gfx::ToMatrix4x4(snappedTransform, mEffectiveTransform);
+      SnapTransformTranslation(transformToSurface, nullptr);
   ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
 }
 
 }
 }
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -462,78 +462,76 @@ const nsIntRegion&
 Layer::GetEffectiveVisibleRegion()
 {
   if (LayerComposite* shadow = AsLayerComposite()) {
     return shadow->GetShadowVisibleRegion();
   }
   return GetVisibleRegion();
 }
 
-gfx3DMatrix
-Layer::SnapTransformTranslation(const gfx3DMatrix& aTransform,
-                                gfxMatrix* aResidualTransform)
+Matrix4x4
+Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
+                                Matrix* aResidualTransform)
 {
   if (aResidualTransform) {
-    *aResidualTransform = gfxMatrix();
+    *aResidualTransform = Matrix();
   }
 
-  gfxMatrix matrix2D;
-  gfx3DMatrix result;
+  Matrix matrix2D;
+  Matrix4x4 result;
   if (mManager->IsSnappingEffectiveTransforms() &&
       aTransform.Is2D(&matrix2D) &&
       !matrix2D.HasNonTranslation() &&
       matrix2D.HasNonIntegerTranslation()) {
-    gfxPoint snappedTranslation(matrix2D.GetTranslation());
-    snappedTranslation.Round();
-    gfxMatrix snappedMatrix = gfxMatrix().Translate(snappedTranslation);
-    result = gfx3DMatrix::From2D(snappedMatrix);
+    IntPoint snappedTranslation = RoundedToInt(matrix2D.GetTranslation());
+    Matrix snappedMatrix = Matrix().Translate(snappedTranslation.x,
+                                              snappedTranslation.y);
+    result = Matrix4x4::From2D(snappedMatrix);
     if (aResidualTransform) {
       // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
       // (I.e., appying snappedMatrix after aResidualTransform gives the
       // ideal transform.)
       *aResidualTransform =
-        gfxMatrix().Translate(matrix2D.GetTranslation() - snappedTranslation);
+        Matrix().Translate(matrix2D._31 - snappedTranslation.x,
+                           matrix2D._32 - snappedTranslation.y);
     }
   } else {
     result = aTransform;
   }
   return result;
 }
 
-gfx3DMatrix
-Layer::SnapTransform(const gfx3DMatrix& aTransform,
+Matrix4x4
+Layer::SnapTransform(const Matrix4x4& aTransform,
                      const gfxRect& aSnapRect,
-                     gfxMatrix* aResidualTransform)
+                     Matrix* aResidualTransform)
 {
   if (aResidualTransform) {
-    *aResidualTransform = gfxMatrix();
+    *aResidualTransform = Matrix();
   }
 
-  gfxMatrix matrix2D;
-  gfx3DMatrix result;
+  Matrix matrix2D;
+  Matrix4x4 result;
   if (mManager->IsSnappingEffectiveTransforms() &&
       aTransform.Is2D(&matrix2D) &&
       gfx::Size(1.0, 1.0) <= ToSize(aSnapRect.Size()) &&
       matrix2D.PreservesAxisAlignedRectangles()) {
-    gfxPoint transformedTopLeft = matrix2D.Transform(aSnapRect.TopLeft());
-    transformedTopLeft.Round();
-    gfxPoint transformedTopRight = matrix2D.Transform(aSnapRect.TopRight());
-    transformedTopRight.Round();
-    gfxPoint transformedBottomRight = matrix2D.Transform(aSnapRect.BottomRight());
-    transformedBottomRight.Round();
+    IntPoint transformedTopLeft = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopLeft()));
+    IntPoint transformedTopRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopRight()));
+    IntPoint transformedBottomRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.BottomRight()));
 
-    gfxMatrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect,
+    Matrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect,
       transformedTopLeft, transformedTopRight, transformedBottomRight);
 
-    result = gfx3DMatrix::From2D(snappedMatrix);
+    result = Matrix4x4::From2D(snappedMatrix);
     if (aResidualTransform && !snappedMatrix.IsSingular()) {
       // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
       // (i.e., appying snappedMatrix after aResidualTransform gives the
       // ideal transform.
-      gfxMatrix snappedMatrixInverse = snappedMatrix;
+      Matrix snappedMatrixInverse = snappedMatrix;
       snappedMatrixInverse.Invert();
       *aResidualTransform = matrix2D * snappedMatrixInverse;
     }
   } else {
     result = aTransform;
   }
   return result;
 }
@@ -884,21 +882,22 @@ ContainerLayer::SortChildrenBy3DZOrder(n
     SortLayersBy3DZOrder(toSort);
     aArray.MoveElementsFrom(toSort);
   }
 }
 
 void
 ContainerLayer::DefaultComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
 {
-  gfxMatrix residual;
+  Matrix residual;
   gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
   idealTransform.ProjectTo2D();
-  gfx3DMatrix snappedTransform = SnapTransformTranslation(idealTransform, &residual);
-  ToMatrix4x4(snappedTransform, mEffectiveTransform);
+  Matrix4x4 ideal;
+  ToMatrix4x4(idealTransform, ideal);
+  mEffectiveTransform = SnapTransformTranslation(ideal, &residual);
 
   bool useIntermediateSurface;
   if (GetMaskLayer()) {
     useIntermediateSurface = true;
 #ifdef MOZ_DUMP_PAINTING
   } else if (gfxUtils::sDumpPainting) {
     useIntermediateSurface = true;
 #endif
@@ -931,17 +930,17 @@ ContainerLayer::DefaultComputeEffectiveT
           }
         }
       }
     }
   }
 
   mUseIntermediateSurface = useIntermediateSurface;
   if (useIntermediateSurface) {
-    ComputeEffectiveTransformsForChildren(gfx3DMatrix::From2D(residual));
+    ComputeEffectiveTransformsForChildren(gfx3DMatrix::From2D(ThebesMatrix(residual)));
   } else {
     ComputeEffectiveTransformsForChildren(idealTransform);
   }
 
   if (idealTransform.CanDraw2D()) {
     ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
   } else {
     ComputeEffectiveTransformForMaskLayer(gfx3DMatrix());
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1360,32 +1360,32 @@ protected:
    * that case resampling is inevitable and there's no point in trying to
    * avoid it. In fact snapping can cause problems because pixel edges in the
    * layer's content can be rendered unpredictably (jiggling) as the scale
    * interacts with the snapping of the translation, especially with animated
    * transforms.
    * @param aResidualTransform a transform to apply before the result transform
    * in order to get the results to completely match aTransform.
    */
-  gfx3DMatrix SnapTransformTranslation(const gfx3DMatrix& aTransform,
-                                       gfxMatrix* aResidualTransform);
+  gfx::Matrix4x4 SnapTransformTranslation(const gfx::Matrix4x4& aTransform,
+                                          gfx::Matrix* aResidualTransform);
   /**
    * See comment for SnapTransformTranslation.
    * This function implements type 2 snapping. If aTransform is a translation
    * and/or scale, transform aSnapRect by aTransform, snap to pixel boundaries,
    * and return the transform that maps aSnapRect to that rect. Otherwise
    * just return aTransform.
    * @param aSnapRect a rectangle whose edges should be snapped to pixel
    * boundaries in the destination surface.
    * @param aResidualTransform a transform to apply before the result transform
    * in order to get the results to completely match aTransform.
    */
-  gfx3DMatrix SnapTransform(const gfx3DMatrix& aTransform,
-                            const gfxRect& aSnapRect,
-                            gfxMatrix* aResidualTransform);
+  gfx::Matrix4x4 SnapTransform(const gfx::Matrix4x4& aTransform,
+                               const gfxRect& aSnapRect,
+                               gfx::Matrix* aResidualTransform);
 
   /**
    * Returns true if this layer's effective transform is not just
    * a translation by integers, or if this layer or some ancestor layer
    * is marked as having a transform that may change without a full layer
    * transaction.
    */
   bool MayResample();
@@ -1475,27 +1475,27 @@ public:
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
 
   virtual ThebesLayer* AsThebesLayer() { return this; }
 
   MOZ_LAYER_DECL_NAME("ThebesLayer", TYPE_THEBES)
 
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
   {
-    gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
-    gfxMatrix residual;
-    gfx3DMatrix snappedTransform = SnapTransformTranslation(idealTransform,
+    gfx::Matrix4x4 idealTransform;
+    gfx::ToMatrix4x4(GetLocalTransform() * aTransformToSurface, idealTransform);
+    gfx::Matrix residual;
+    mEffectiveTransform = SnapTransformTranslation(idealTransform,
         mAllowResidualTranslation ? &residual : nullptr);
-    gfx::ToMatrix4x4(snappedTransform, mEffectiveTransform);
     // The residual can only be a translation because SnapTransformTranslation
     // only changes the transform if it's a translation
-    NS_ASSERTION(!residual.HasNonTranslation(),
+    NS_ASSERTION(residual.IsTranslation(),
                  "Residual transform can only be a translation");
-    if (!residual.GetTranslation().WithinEpsilonOf(mResidualTranslation, 1e-3f)) {
-      mResidualTranslation = residual.GetTranslation();
+    if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) {
+      mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation());
       NS_ASSERTION(-0.5 <= mResidualTranslation.x && mResidualTranslation.x < 0.5 &&
                    -0.5 <= mResidualTranslation.y && mResidualTranslation.y < 0.5,
                    "Residual translation out of range");
       mValidRegion.SetEmpty();
     }
     ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
   }
 
@@ -1748,19 +1748,19 @@ public:
 
   // This getter can be used anytime.
   virtual const gfxRGBA& GetColor() { return mColor; }
 
   MOZ_LAYER_DECL_NAME("ColorLayer", TYPE_COLOR)
 
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
   {
-    gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
-    gfx3DMatrix snappedTransform = SnapTransformTranslation(idealTransform, nullptr);
-    gfx::ToMatrix4x4(snappedTransform, mEffectiveTransform);
+    gfx::Matrix4x4 idealTransform;
+    gfx::ToMatrix4x4(GetLocalTransform() * aTransformToSurface, idealTransform);
+    mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
     ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
   }
 
 protected:
   ColorLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData),
       mColor(0.0, 0.0, 0.0, 0.0)
   {}
@@ -1893,21 +1893,24 @@ public:
   MOZ_LAYER_DECL_NAME("CanvasLayer", TYPE_CANVAS)
 
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
   {
     // Snap our local transform first, and snap the inherited transform as well.
     // This makes our snapping equivalent to what would happen if our content
     // was drawn into a ThebesLayer (gfxContext would snap using the local
     // transform, then we'd snap again when compositing the ThebesLayer).
-    gfx3DMatrix snappedTransform =
-        SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height),
+    gfx::Matrix4x4 localTransform;
+    gfx::Matrix4x4 transformToSurface;
+    gfx::ToMatrix4x4(GetLocalTransform(), localTransform);
+    gfx::ToMatrix4x4(aTransformToSurface, transformToSurface);
+    mEffectiveTransform =
+        SnapTransform(localTransform, gfxRect(0, 0, mBounds.width, mBounds.height),
                       nullptr)*
-        SnapTransformTranslation(aTransformToSurface, nullptr);
-    gfx::ToMatrix4x4(snappedTransform, mEffectiveTransform);
+        SnapTransformTranslation(transformToSurface, nullptr);
     ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
   }
 
 protected:
   CanvasLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData)
     , mPreTransCallback(nullptr)
     , mPreTransCallbackData(nullptr)
--- a/gfx/layers/ReadbackLayer.h
+++ b/gfx/layers/ReadbackLayer.h
@@ -85,21 +85,23 @@ public:
   MOZ_LAYER_DECL_NAME("ReadbackLayer", TYPE_READBACK)
 
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
   {
     // Snap our local transform first, and snap the inherited transform as well.
     // This makes our snapping equivalent to what would happen if our content
     // was drawn into a ThebesLayer (gfxContext would snap using the local
     // transform, then we'd snap again when compositing the ThebesLayer).
-    gfx3DMatrix snappedTransform =
-        SnapTransform(GetLocalTransform(), gfxRect(0, 0, mSize.width, mSize.height),
+    gfx::Matrix4x4 localTransform, transformToSurface;
+    gfx::ToMatrix4x4(aTransformToSurface, transformToSurface);
+    gfx::ToMatrix4x4(GetLocalTransform(), localTransform);
+    mEffectiveTransform =
+        SnapTransform(localTransform, gfxRect(0, 0, mSize.width, mSize.height),
                       nullptr)*
-        SnapTransformTranslation(aTransformToSurface, nullptr);
-    gfx::ToMatrix4x4(snappedTransform, mEffectiveTransform);
+        SnapTransformTranslation(transformToSurface, nullptr);
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the callback object to which readback updates will be delivered.
    * This also resets the "needed rectangle" so that on the next layer tree
    * transaction we will try to deliver the full contents of the readback
    * area to the sink.
--- a/gfx/layers/basic/BasicContainerLayer.cpp
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -34,30 +34,31 @@ BasicContainerLayer::~BasicContainerLaye
 }
 
 void
 BasicContainerLayer::ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
 {
   // We push groups for container layers if we need to, which always
   // are aligned in device space, so it doesn't really matter how we snap
   // containers.
-  gfxMatrix residual;
+  Matrix residual;
   gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
   idealTransform.ProjectTo2D();
 
   if (!idealTransform.CanDraw2D()) {
     ToMatrix4x4(idealTransform, mEffectiveTransform);
     ComputeEffectiveTransformsForChildren(gfx3DMatrix());
     ComputeEffectiveTransformForMaskLayer(gfx3DMatrix());
     mUseIntermediateSurface = true;
     return;
   }
 
-  gfx3DMatrix snappedTransform = SnapTransformTranslation(idealTransform, &residual);
-  ToMatrix4x4(snappedTransform, mEffectiveTransform);
+  Matrix4x4 ideal;
+  ToMatrix4x4(idealTransform, ideal);
+  mEffectiveTransform = SnapTransformTranslation(ideal, &residual);
   // We always pass the ideal matrix down to our children, so there is no
   // need to apply any compensation using the residual from SnapTransformTranslation.
   ComputeEffectiveTransformsForChildren(idealTransform);
 
   ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
   
   Layer* child = GetFirstChild();
   bool hasSingleBlendingChild = false;
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -101,17 +101,18 @@ ImageLayerComposite::RenderLayer(const n
                         GetEffectiveTransform(),
                         gfx::ToFilter(mFilter),
                         clipRect);
 }
 
 void 
 ImageLayerComposite::ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
 {
-  gfx3DMatrix local = GetLocalTransform();
+  gfx::Matrix4x4 local;
+  gfx::ToMatrix4x4(GetLocalTransform(), local);
 
   // Snap image edges to pixel boundaries
   gfxRect sourceRect(0, 0, 0, 0);
   if (mImageHost &&
       mImageHost->IsAttached() &&
       (mImageHost->GetDeprecatedTextureHost() || mImageHost->GetAsTextureHost())) {
     IntSize size =
       mImageHost->GetAsTextureHost() ? mImageHost->GetAsTextureHost()->GetSize()
@@ -124,20 +125,21 @@ ImageLayerComposite::ComputeEffectiveTra
       local.Scale(mScaleToSize.width / sourceRect.width,
                   mScaleToSize.height / sourceRect.height, 1.0);
     }
   }
   // Snap our local transform first, and snap the inherited transform as well.
   // This makes our snapping equivalent to what would happen if our content
   // was drawn into a ThebesLayer (gfxContext would snap using the local
   // transform, then we'd snap again when compositing the ThebesLayer).
-  gfx3DMatrix snappedTransform =
+  gfx::Matrix4x4 transformToSurface;
+  gfx::ToMatrix4x4(aTransformToSurface, transformToSurface);
+  mEffectiveTransform =
       SnapTransform(local, sourceRect, nullptr) *
-      SnapTransformTranslation(aTransformToSurface, nullptr);
-  gfx::ToMatrix4x4(snappedTransform, mEffectiveTransform);
+      SnapTransformTranslation(transformToSurface, nullptr);
   ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
 }
 
 CompositableHost*
 ImageLayerComposite::GetCompositableHost()
 {
   if (mImageHost && mImageHost->IsAttached()) {
     return mImageHost.get();
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -671,16 +671,40 @@ gfxUtils::TransformRectToRect(const gfxR
     m.xy = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
     m.yx = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
     m.x0 = aToTopLeft.x - m.xy*aFrom.y;
     m.y0 = aToTopLeft.y - m.yx*aFrom.x;
   }
   return m;
 }
 
+Matrix
+gfxUtils::TransformRectToRect(const gfxRect& aFrom, const IntPoint& aToTopLeft,
+                              const IntPoint& aToTopRight, const IntPoint& aToBottomRight)
+{
+  Matrix m;
+  if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
+    // Not a rotation, so xy and yx are zero
+    m._12 = m._21 = 0.0;
+    m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width;
+    m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height;
+    m._31 = aToTopLeft.x - m._11*aFrom.x;
+    m._32 = aToTopLeft.y - m._22*aFrom.y;
+  } else {
+    NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
+                 "Destination rectangle not axis-aligned");
+    m._11 = m._22 = 0.0;
+    m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
+    m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
+    m._31 = aToTopLeft.x - m._21*aFrom.y;
+    m._32 = aToTopLeft.y - m._12*aFrom.x;
+  }
+  return m;
+}
+
 bool
 gfxUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
 {
   *aOut = nsIntRect(int32_t(aIn.X()), int32_t(aIn.Y()),
   int32_t(aIn.Width()), int32_t(aIn.Height()));
   return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height).IsEqualEdges(aIn);
 }
 
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -17,16 +17,18 @@ struct nsIntRect;
 namespace mozilla {
 namespace layers {
 class PlanarYCbCrData;
 }
 }
 
 class gfxUtils {
 public:
+    typedef mozilla::gfx::IntPoint IntPoint;
+    typedef mozilla::gfx::Matrix Matrix;
     /*
      * Premultiply or Unpremultiply aSourceSurface, writing the result
      * to aDestSurface or back into aSourceSurface if aDestSurface is null.
      *
      * If aDestSurface is given, it must have identical format, dimensions, and
      * stride as the source.
      *
      * If the source is not gfxImageFormat::ARGB32, no operation is performed.  If
@@ -104,16 +106,21 @@ public:
      * aToTopLeft/aToTopRight/aToBottomRight. aFrom must be
      * nonempty and the destination rectangle must be axis-aligned.
      */
     static gfxMatrix TransformRectToRect(const gfxRect& aFrom,
                                          const gfxPoint& aToTopLeft,
                                          const gfxPoint& aToTopRight,
                                          const gfxPoint& aToBottomRight);
 
+    static Matrix TransformRectToRect(const gfxRect& aFrom,
+                                      const IntPoint& aToTopLeft,
+                                      const IntPoint& aToTopRight,
+                                      const IntPoint& aToBottomRight);
+
     /**
      * If aIn can be represented exactly using an nsIntRect (i.e.
      * integer-aligned edges and coordinates in the int32_t range) then we
      * set aOut to that rectangle, otherwise return failure.
     */
     static bool GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut);
 
     /**