Bug 757329: Support changing transforms while emitting path instructions. r=roc
authorBas Schouten <bschouten@mozilla.com>
Tue, 22 May 2012 07:29:19 +0200
changeset 94554 8574f784774aa94db31eb10cb0e7b0ddd1173da1
parent 94553 bbb12d0bcf4966ee6dfbcc9d42a69a83256bfc12
child 94555 abbf642df56288b8e14498db8f2484f3746d9786
push id9676
push userbschouten@mozilla.com
push dateTue, 22 May 2012 05:29:46 +0000
treeherdermozilla-inbound@abbf642df562 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs757329
milestone15.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 757329: Support changing transforms while emitting path instructions. r=roc
gfx/2d/Matrix.h
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxContext.h
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -135,17 +135,17 @@ public:
   bool operator!=(const Matrix& other) const
   {
     return !(*this == other);
   }
 
   /* Returns true if the matrix is a rectilinear transformation (i.e.
    * grid-aligned rectangles are transformed to grid-aligned rectangles)
    */
-  bool IsRectilinear() {
+  bool IsRectilinear() const {
     if (FuzzyEqual(_12, 0) && FuzzyEqual(_21, 0)) {
       return true;
     } else if (FuzzyEqual(_22, 0) && FuzzyEqual(_11, 0)) {
       return true;
     }
 
     return false;
   }
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -205,16 +205,19 @@ gfxContext::Restore()
     if (CurrentState().clipWasReset &&
         CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
       PushClipsToDT(mDT);
     }
 
     mStateStack.RemoveElementAt(mStateStack.Length() - 1);
 
     if (mPathBuilder || mPath || mPathIsRect) {
+      // Support here isn't fully correct if the path is continued -after-
+      // the restore. We don't currently have users that do this and we should
+      // make sure there will not be any. Sadly we can't assert this easily.
       mTransformChanged = true;
       mPathTransform = mDT->GetTransform();
     }
 
     mDT = CurrentState().drawTarget;
     mDT->SetTransform(CurrentState().transform);
   }
 }
@@ -546,90 +549,74 @@ gfxContext::DrawSurface(gfxASurface *sur
 
 // transform stuff
 void
 gfxContext::Translate(const gfxPoint& pt)
 {
   if (mCairo) {
     cairo_translate(mCairo, pt.x, pt.y);
   } else {
-    MOZ_ASSERT(!mPathBuilder);
+    Matrix newMatrix = mDT->GetTransform();
 
-    Matrix newMatrix = mDT->GetTransform();
-    TransformWillChange();
-    mDT->SetTransform(newMatrix.Translate(Float(pt.x), Float(pt.y)));
+    ChangeTransform(newMatrix.Translate(Float(pt.x), Float(pt.y)));
   }
 }
 
 void
 gfxContext::Scale(gfxFloat x, gfxFloat y)
 {
   if (mCairo) {
     cairo_scale(mCairo, x, y);
   } else {
-    MOZ_ASSERT(!mPathBuilder);
+    Matrix newMatrix = mDT->GetTransform();
 
-    Matrix newMatrix = mDT->GetTransform();
-    TransformWillChange();
-    mDT->SetTransform(newMatrix.Scale(Float(x), Float(y)));
+    ChangeTransform(newMatrix.Scale(Float(x), Float(y)));
   }
 }
 
 void
 gfxContext::Rotate(gfxFloat angle)
 {
   if (mCairo) {
     cairo_rotate(mCairo, angle);
   } else {
-    MOZ_ASSERT(!mPathBuilder);
-
-    TransformWillChange();
     Matrix rotation = Matrix::Rotation(Float(angle));
-    mDT->SetTransform(rotation * mDT->GetTransform());
+    ChangeTransform(rotation * mDT->GetTransform());
   }
 }
 
 void
 gfxContext::Multiply(const gfxMatrix& matrix)
 {
   if (mCairo) {
     const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
     cairo_transform(mCairo, &mat);
   } else {
-    MOZ_ASSERT(!mPathBuilder);
-
-    TransformWillChange();
-    mDT->SetTransform(ToMatrix(matrix) * mDT->GetTransform());
+    ChangeTransform(ToMatrix(matrix) * mDT->GetTransform());
   }
 }
 
 void
 gfxContext::SetMatrix(const gfxMatrix& matrix)
 {
   if (mCairo) {
     const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
     cairo_set_matrix(mCairo, &mat);
   } else {
-    MOZ_ASSERT(!mPathBuilder);
-
-    TransformWillChange();
-    mDT->SetTransform(ToMatrix(matrix));
+    ChangeTransform(ToMatrix(matrix));
   }
 }
 
 void
 gfxContext::IdentityMatrix()
 {
   if (mCairo) {
     cairo_identity_matrix(mCairo);
   } else {
-    MOZ_ASSERT(!mPathBuilder);
-
-    TransformWillChange();
-    mDT->SetTransform(Matrix());
+    ChangeTransform(Matrix());
   }
 }
 
 gfxMatrix
 gfxContext::CurrentMatrix() const
 {
   if (mCairo) {
     cairo_matrix_t mat;
@@ -2070,18 +2057,46 @@ gfxContext::GetOp()
 
 /* SVG font code can change the transform after having set the pattern on the
  * context. When the pattern is set it is in user space, if the transform is
  * changed after doing so the pattern needs to be converted back into userspace.
  * We just store the old pattern here so that we only do the work needed here
  * if the pattern is actually used.
  */
 void
-gfxContext::TransformWillChange()
+gfxContext::ChangeTransform(Matrix &aNewMatrix)
 {
   AzureState &state = CurrentState();
 
   if ((state.pattern || state.sourceSurface)
       && !state.patternTransformChanged) {
     state.patternTransform = mDT->GetTransform();
     state.patternTransformChanged = true;
   }
+
+  if (mPathBuilder || mPathIsRect) {
+    Matrix invMatrix = aNewMatrix;
+    
+    invMatrix.Invert();
+
+    Matrix toNewUS = mDT->GetTransform() * invMatrix;
+
+    if (toNewUS.IsRectilinear() && mPathIsRect) {
+      mRect = toNewUS.TransformBounds(mRect);
+    } else if (mPathIsRect) {
+      mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
+      
+      mPathBuilder->MoveTo(toNewUS * mRect.TopLeft());
+      mPathBuilder->LineTo(toNewUS * mRect.TopRight());
+      mPathBuilder->LineTo(toNewUS * mRect.BottomRight());
+      mPathBuilder->LineTo(toNewUS * mRect.BottomLeft());
+      mPathBuilder->Close();
+    } else {
+      RefPtr<Path> path = mPathBuilder->Finish();
+      // Create path in device space.
+      mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
+    }
+    // No need to consider the transform changed now!
+    mTransformChanged = false;
+  }
+
+  mDT->SetTransform(aNewMatrix);
 }
\ No newline at end of file
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -736,17 +736,17 @@ private:
 
   // This ensures mPath contains a valid path (in user space!)
   void EnsurePath();
   // This ensures mPathBuilder contains a valid PathBuilder (in user space!)
   void EnsurePathBuilder();
   void FillAzure(mozilla::gfx::Float aOpacity);
   void PushClipsToDT(mozilla::gfx::DrawTarget *aDT);
   CompositionOp GetOp();
-  void TransformWillChange();
+  void ChangeTransform(mozilla::gfx::Matrix &aNewMatrix);
 
   bool mPathIsRect;
   bool mTransformChanged;
   Matrix mPathTransform;
   Rect mRect;
   mozilla::RefPtr<PathBuilder> mPathBuilder;
   mozilla::RefPtr<Path> mPath;
   Matrix mTransform;