Bug 918613: Convert cairo path code to use cairo_path_t. r=jrmuizel
authorBas Schouten <bschouten@mozilla.com>
Sat, 28 Sep 2013 16:20:24 +0200
changeset 149151 3287d7bbdc14370007bc4fc56356766b6412a49e
parent 149150 92f573a2f75d7f3e9312522db8fd85be7ac0d13c
child 149152 a8aca6978ed9646c2737a27c15053dc0b10db597
push id25374
push usercbook@mozilla.com
push dateSun, 29 Sep 2013 09:37:16 +0000
treeherdermozilla-central@8f805d3ef377 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs918613
milestone27.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 918613: Convert cairo path code to use cairo_path_t. r=jrmuizel
gfx/2d/2D.h
gfx/2d/DrawTargetCairo.cpp
gfx/2d/DrawTargetCairo.h
gfx/2d/PathCairo.cpp
gfx/2d/PathCairo.h
gfx/2d/ScaledFontBase.cpp
gfx/2d/ScaledFontBase.h
gfx/2d/ScaledFontDWrite.cpp
gfx/2d/ScaledFontDWrite.h
gfx/2d/ScaledFontMac.cpp
gfx/2d/ScaledFontMac.h
gfx/thebes/gfxFont.cpp
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -500,17 +500,17 @@ public:
    */
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
 
   /* This copies the path describing the glyphs into a PathBuilder. We use this
    * API rather than a generic API to append paths because it allows easier
    * implementation in some backends, and more efficient implementation in
    * others.
    */
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder) = 0;
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint = nullptr) = 0;
 
   virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
 
   void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
     mUserData.Add(key, userData, destroy);
   }
   void *GetUserData(UserDataKey *key) {
     return mUserData.Get(key);
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -32,16 +32,18 @@
 #include "cairo-win32.h"
 #endif
 
 #include <algorithm>
 
 namespace mozilla {
 namespace gfx {
 
+cairo_surface_t *DrawTargetCairo::mDummySurface;
+
 namespace {
 
 // An RAII class to prepare to draw a context and optional path. Saves and
 // restores the context on construction/destruction.
 class AutoPrepareForDrawing
 {
 public:
   AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx)
@@ -373,25 +375,21 @@ NeedIntermediateSurface(const Pattern& a
   if (aOptions.mAlpha == 1.0)
     return false;
 
   return true;
 }
 
 DrawTargetCairo::DrawTargetCairo()
   : mContext(nullptr)
-  , mPathObserver(nullptr)
 {
 }
 
 DrawTargetCairo::~DrawTargetCairo()
 {
-  if (mPathObserver) {
-    mPathObserver->ForgetDrawTarget();
-  }
   cairo_destroy(mContext);
   if (mSurface) {
     cairo_surface_destroy(mSurface);
   }
 }
 
 IntSize
 DrawTargetCairo::GetSize()
@@ -424,16 +422,28 @@ DrawTargetCairo::Flush()
 }
 
 void
 DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nullptr */)
 {
   WillChange(aPath);
 }
 
+cairo_surface_t*
+DrawTargetCairo::GetDummySurface()
+{
+  if (mDummySurface) {
+    return mDummySurface;
+  }
+
+  mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+
+  return mDummySurface;
+}
+
 void
 DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
                              const Rect &aDest,
                              const Rect &aSource,
                              const DrawSurfaceOptions &aSurfOptions,
                              const DrawOptions &aOptions)
 {
   AutoPrepareForDrawing prep(this, mContext);
@@ -703,33 +713,33 @@ DrawTargetCairo::Stroke(const Path *aPat
                         const DrawOptions &aOptions /* = DrawOptions() */)
 {
   AutoPrepareForDrawing prep(this, mContext, aPath);
 
   if (aPath->GetBackendType() != BACKEND_CAIRO)
     return;
 
   PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
-  path->CopyPathTo(mContext, this);
+  path->SetPathOnContext(mContext);
 
   DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
 }
 
 void
 DrawTargetCairo::Fill(const Path *aPath,
                       const Pattern &aPattern,
                       const DrawOptions &aOptions /* = DrawOptions() */)
 {
   AutoPrepareForDrawing prep(this, mContext, aPath);
 
   if (aPath->GetBackendType() != BACKEND_CAIRO)
     return;
 
   PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
-  path->CopyPathTo(mContext, this);
+  path->SetPathOnContext(mContext);
 
   DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
 }
 
 void
 DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
                             const GlyphBuffer &aBuffer,
                             const Pattern &aPattern,
@@ -831,17 +841,17 @@ DrawTargetCairo::PushClip(const Path *aP
   if (aPath->GetBackendType() != BACKEND_CAIRO) {
     return;
   }
 
   WillChange(aPath);
   cairo_save(mContext);
 
   PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
-  path->CopyPathTo(mContext, this);
+  path->SetPathOnContext(mContext);
   cairo_clip_preserve(mContext);
 }
 
 void
 DrawTargetCairo::PushClipRect(const Rect& aRect)
 {
   WillChange();
   cairo_save(mContext);
@@ -856,19 +866,17 @@ DrawTargetCairo::PopClip()
 {
   // save/restore does not affect the path, so no need to call WillChange()
   cairo_restore(mContext);
 }
 
 TemporaryRef<PathBuilder>
 DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const
 {
-  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mContext,
-                                                          const_cast<DrawTargetCairo*>(this),
-                                                          aFillRule);
+  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
 
   return builder;
 }
 
 void
 DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator)
 {
   if (aOperator != OP_SOURCE)
@@ -1066,31 +1074,16 @@ DrawTargetCairo::MarkSnapshotIndependent
     mSnapshot = nullptr;
   }
 }
 
 void
 DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
 {
   MarkSnapshotIndependent();
-
-  if (mPathObserver &&
-      (!aPath || !mPathObserver->ContainsPath(aPath))) {
-    mPathObserver->PathWillChange();
-    mPathObserver = nullptr;
-  }
-}
-
-void
-DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver)
-{
-  if (mPathObserver && mPathObserver != aPathObserver) {
-    mPathObserver->PathWillChange();
-  }
-  mPathObserver = aPathObserver;
 }
 
 void
 DrawTargetCairo::SetTransform(const Matrix& aTransform)
 {
   mTransform = aTransform;
 
   cairo_matrix_t mat;
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -137,25 +137,25 @@ public:
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
                         ExtendMode aExtendMode = EXTEND_CLAMP) const;
 
   virtual void *GetNativeSurface(NativeSurfaceType aType);
 
   bool Init(cairo_surface_t* aSurface, const IntSize& aSize);
 
-  void SetPathObserver(CairoPathContext* aPathObserver);
-
   virtual void SetTransform(const Matrix& aTransform);
 
   // Call to set up aContext for drawing (with the current transform, etc).
   // Pass the path you're going to be using if you have one.
   // Implicitly calls WillChange(aPath).
   void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr);
 
+  static cairo_surface_t *GetDummySurface();
+
 private: // methods
   // Init cairo surface without doing a cairo_surface_reference() call.
   bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize);
 
   enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
   void DrawPattern(const Pattern& aPattern,
                    const StrokeOptions& aStrokeOptions,
                    const DrawOptions& aOptions,
@@ -176,20 +176,15 @@ private: // methods
 private: // data
   cairo_t* mContext;
   cairo_surface_t* mSurface;
   IntSize mSize;
 
   // The latest snapshot of this surface. This needs to be told when this
   // target is modified. We keep it alive as a cache.
   RefPtr<SourceSurfaceCairo> mSnapshot;
-
-  // It is safe to use a regular pointer here because the CairoPathContext will
-  // deregister itself on destruction. Using a RefPtr would extend the life-
-  // span of the CairoPathContext. This causes a problem when
-  // PathBuilderCairo.Finish()
-  mutable CairoPathContext* mPathObserver;
+  static cairo_surface_t *mDummySurface;
 };
 
 }
 }
 
 #endif // _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
--- a/gfx/2d/PathCairo.cpp
+++ b/gfx/2d/PathCairo.cpp
@@ -8,315 +8,287 @@
 #include "DrawTargetCairo.h"
 #include "Logging.h"
 #include "PathHelpers.h"
 #include "HelpersCairo.h"
 
 namespace mozilla {
 namespace gfx {
 
-CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget)
- : mContext(aCtx)
- , mDrawTarget(aDrawTarget)
-{
-  cairo_reference(mContext);
-
-  // A new path in the DrawTarget's context.
-  aDrawTarget->SetPathObserver(this);
-  cairo_new_path(mContext);
-}
-
-CairoPathContext::CairoPathContext(CairoPathContext& aPathContext)
- : mContext(aPathContext.mContext)
- , mDrawTarget(nullptr)
-{
-  cairo_reference(mContext);
-  DuplicateContextAndPath();
-}
-
-CairoPathContext::~CairoPathContext()
-{
-  if (mDrawTarget) {
-    DrawTargetCairo* drawTarget = mDrawTarget;
-    ForgetDrawTarget();
-
-    // We need to set mDrawTarget to nullptr before we tell DrawTarget otherwise
-    // we will think we need to make a defensive copy of the path.
-    drawTarget->SetPathObserver(nullptr);
-  }
-  cairo_destroy(mContext);
-}
-
-void
-CairoPathContext::DuplicateContextAndPath()
-{
-  // Duplicate the path.
-  cairo_path_t* path = cairo_copy_path(mContext);
-
-  // Duplicate the context.
-  cairo_surface_t* surf = cairo_get_target(mContext);
-  cairo_matrix_t matrix;
-  cairo_get_matrix(mContext, &matrix);
-  cairo_destroy(mContext);
-
-  mContext = cairo_create(surf);
-
-  // Set the matrix to match the source context so that the path is copied in
-  // device space. After this point it doesn't matter what the transform is
-  // set to because it's always swapped out before use.
-  cairo_set_matrix(mContext, &matrix);
-
-  // Add the path, and throw away our duplicate.
-  cairo_append_path(mContext, path);
-  cairo_path_destroy(path);
-}
-
-void
-CairoPathContext::ForgetDrawTarget()
+PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
+  : mFillRule(aFillRule)
 {
-  // We don't need to set the path observer back to nullptr in this case
-  // because ForgetDrawTarget() is trigged when the target has been
-  // grabbed by another path observer.
-  mDrawTarget = nullptr;
 }
 
 void
-CairoPathContext::PathWillChange()
-{
-  // Once we've copied out the context's path, there's no use to holding on to
-  // the draw target. Thus, there's nothing for us to do if we're independent
-  // of the draw target, since we'll have already copied out the context's
-  // path.
-  if (mDrawTarget) {
-    // The context we point to is going to change from under us. To continue
-    // using this path, we need to copy it to a new context.
-    DuplicateContextAndPath();
-    ForgetDrawTarget();
-  }
-}
-
-void
-CairoPathContext::CopyPathTo(cairo_t* aToContext, Matrix& aTransform)
-{
-  if (aToContext != mContext) {
-    CairoTempMatrix tempMatrix(mContext, aTransform);
-    cairo_path_t* path = cairo_copy_path(mContext);
-    cairo_new_path(aToContext);
-    cairo_append_path(aToContext, path);
-    cairo_path_destroy(path);
-  }
-}
-
-bool
-CairoPathContext::ContainsPath(const Path* aPath)
-{
-  if (aPath->GetBackendType() != BACKEND_CAIRO) {
-    return false;
-  }
-
-  const PathCairo* path = static_cast<const PathCairo*>(aPath);
-  RefPtr<CairoPathContext> ctx = const_cast<PathCairo*>(path)->GetPathContext();
-  return ctx == this;
-}
-
-PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext,
-                                   FillRule aFillRule,
-                                   const Matrix& aTransform /* = Matrix() */)
- : mPathContext(aPathContext)
- , mTransform(aTransform)
- , mFillRule(aFillRule)
-{}
-
-PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule)
- : mPathContext(new CairoPathContext(aCtx, aDrawTarget))
- , mTransform(aDrawTarget->GetTransform())
- , mFillRule(aFillRule)
-{}
-
-void
 PathBuilderCairo::MoveTo(const Point &aPoint)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  cairo_move_to(*mPathContext, aPoint.x, aPoint.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_MOVE_TO;
+  data.header.length = 2;
+  mPathData.push_back(data);
+  data.point.x = aPoint.x;
+  data.point.y = aPoint.y;
+  mPathData.push_back(data);
+
+  mBeginPoint = mCurrentPoint = aPoint;
 }
 
 void
 PathBuilderCairo::LineTo(const Point &aPoint)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  cairo_line_to(*mPathContext, aPoint.x, aPoint.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_LINE_TO;
+  data.header.length = 2;
+  mPathData.push_back(data);
+  data.point.x = aPoint.x;
+  data.point.y = aPoint.y;
+  mPathData.push_back(data);
+
+  mCurrentPoint = aPoint;
 }
 
 void
 PathBuilderCairo::BezierTo(const Point &aCP1,
                            const Point &aCP2,
                            const Point &aCP3)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_CURVE_TO;
+  data.header.length = 4;
+  mPathData.push_back(data);
+  data.point.x = aCP1.x;
+  data.point.y = aCP1.y;
+  mPathData.push_back(data);
+  data.point.x = aCP2.x;
+  data.point.y = aCP2.y;
+  mPathData.push_back(data);
+  data.point.x = aCP3.x;
+  data.point.y = aCP3.y;
+  mPathData.push_back(data);
+
+  mCurrentPoint = aCP3;
 }
 
 void
 PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
                                     const Point &aCP2)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-
   // We need to elevate the degree of this quadratic B├ęzier to cubic, so we're
   // going to add an intermediate control point, and recompute control point 1.
   // The first and last control points remain the same.
   // This formula can be found on http://fontforge.sourceforge.net/bezier.html
   Point CP0 = CurrentPoint();
   Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
   Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
   Point CP3 = aCP2;
 
-  cairo_curve_to(*mPathContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_CURVE_TO;
+  data.header.length = 4;
+  mPathData.push_back(data);
+  data.point.x = CP1.x;
+  data.point.y = CP1.y;
+  mPathData.push_back(data);
+  data.point.x = CP2.x;
+  data.point.y = CP2.y;
+  mPathData.push_back(data);
+  data.point.x = CP3.x;
+  data.point.y = CP3.y;
+  mPathData.push_back(data);
+
+  mCurrentPoint = aCP2;
 }
 
 void
 PathBuilderCairo::Close()
 {
-  PrepareForWrite();
-  cairo_close_path(*mPathContext);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_CLOSE_PATH;
+  data.header.length = 1;
+  mPathData.push_back(data);
+
+  mCurrentPoint = mBeginPoint;
 }
 
 void
 PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
                      float aEndAngle, bool aAntiClockwise)
 {
   ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
 }
 
 Point
 PathBuilderCairo::CurrentPoint() const
 {
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  double x, y;
-  cairo_get_current_point(*mPathContext, &x, &y);
-  return Point((Float)x, (Float)y);
+  return mCurrentPoint;
 }
 
 TemporaryRef<Path>
 PathBuilderCairo::Finish()
 {
-  return new PathCairo(mPathContext, mTransform, mFillRule);
+  return new PathCairo(mFillRule, mPathData, mCurrentPoint);
 }
 
-TemporaryRef<CairoPathContext>
-PathBuilderCairo::GetPathContext()
+PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
+  : mFillRule(aFillRule)
+  , mContainingContext(nullptr)
+  , mCurrentPoint(aCurrentPoint)
 {
-  return mPathContext;
+  mPathData.swap(aPathData);
 }
 
-void
-PathBuilderCairo::PrepareForWrite()
+PathCairo::PathCairo(cairo_t *aContext)
+  : mFillRule(FILL_WINDING)
+  , mContainingContext(nullptr)
 {
-  // Only PathBuilder and PathCairo maintain references to CairoPathContext.
-  // DrawTarget does not. If we're sharing a reference to the context then we
-  // need to create a copy that we can modify. This provides copy on write
-  // behaviour.
-  if (mPathContext->refCount() != 1) {
-    mPathContext = new CairoPathContext(*mPathContext);
+  cairo_path_t *path = cairo_copy_path(aContext);
+
+  // XXX - mCurrentPoint is not properly set here, the same is true for the
+  // D2D Path code, we never require current point when hitting this codepath
+  // but this should be fixed.
+  for (int i = 0; i < path->num_data; i++) {
+    mPathData.push_back(path->data[i]);
+  }
+
+  cairo_path_destroy(path);
+}
+
+PathCairo::~PathCairo()
+{
+  if (mContainingContext) {
+    cairo_destroy(mContainingContext);
   }
 }
 
-PathCairo::PathCairo(CairoPathContext* aPathContext, Matrix& aTransform,
-                     FillRule aFillRule)
- : mPathContext(aPathContext)
- , mTransform(aTransform)
- , mFillRule(aFillRule)
-{}
-
 TemporaryRef<PathBuilder>
 PathCairo::CopyToBuilder(FillRule aFillRule) const
 {
-  return new PathBuilderCairo(mPathContext, aFillRule, mTransform);
+  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+  builder->mPathData = mPathData;
+  builder->mCurrentPoint = mCurrentPoint;
+
+  return builder;
 }
 
 TemporaryRef<PathBuilder>
 PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
 {
-  // We are given the transform we would apply from device space to user space.
-  // However in cairo our path is in device space so we view the transform as
-  // being the other way round. We therefore need to apply the inverse transform
-  // to our current cairo transform.
-  Matrix inverse = aTransform;
-  inverse.Invert();
+  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
 
-  return new PathBuilderCairo(mPathContext, aFillRule, mTransform * inverse);
+  AppendPathToBuilder(builder, &aTransform);
+  builder->mCurrentPoint = aTransform * mCurrentPoint;
+
+  return builder;
 }
 
 bool
 PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
-
   Matrix inverse = aTransform;
   inverse.Invert();
   Point transformed = inverse * aPoint;
 
-  // Needs the correct fill rule set.
-  cairo_set_fill_rule(*mPathContext, GfxFillRuleToCairoFillRule(mFillRule));
-  return cairo_in_fill(*mPathContext, transformed.x, transformed.y);
+  EnsureContainingContext();
+
+  return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
 }
 
 bool
 PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
                                const Point &aPoint,
                                const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
-
   Matrix inverse = aTransform;
   inverse.Invert();
   Point transformed = inverse * aPoint;
 
-  SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
-  return cairo_in_stroke(*mPathContext, transformed.x, transformed.y);
+  EnsureContainingContext();
+
+  SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
+
+  return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
 }
 
 Rect
 PathCairo::GetBounds(const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
+  EnsureContainingContext();
 
   double x1, y1, x2, y2;
 
-  cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2);
+  cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
   Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
   return aTransform.TransformBounds(bounds);
 }
 
 Rect
 PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
                             const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
+  EnsureContainingContext();
 
   double x1, y1, x2, y2;
 
-  SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
+  SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
 
-  cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2);
+  cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
   Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
   return aTransform.TransformBounds(bounds);
 }
 
-TemporaryRef<CairoPathContext>
-PathCairo::GetPathContext()
+void
+PathCairo::EnsureContainingContext() const
 {
-  return mPathContext;
+  if (mContainingContext) {
+    return;
+  }
+
+  mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
+
+  SetPathOnContext(mContainingContext);
 }
 
 void
-PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget)
+PathCairo::SetPathOnContext(cairo_t *aContext) const
+{
+  // Needs the correct fill rule set.
+  cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
+
+  cairo_new_path(aContext);
+
+  if (mPathData.size()) {
+    cairo_path_t path;
+    path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
+    path.num_data = mPathData.size();
+    path.status = CAIRO_STATUS_SUCCESS;
+    cairo_append_path(aContext, &path);
+  }
+}
+
+void
+PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
 {
-  mPathContext->CopyPathTo(aContext, mTransform);
-  cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
+  if (aTransform) {
+    int i = 0;
+    while (i < mPathData.size()) {
+      uint32_t pointCount = mPathData[i].header.length - 1;
+      aBuilder->mPathData.push_back(mPathData[i]);
+      i++;
+      for (int c = 0; c < pointCount; c++) {
+        cairo_path_data_t data;
+        Point newPoint = *aTransform * Point(mPathData[i].point.x, mPathData[i].point.y);
+        data.point.x = newPoint.x;
+        data.point.y = newPoint.y;
+        aBuilder->mPathData.push_back(data);
+        i++;
+      }
+    }
+  } else {
+    for (int i = 0; i < mPathData.size(); i++) {
+      aBuilder->mPathData.push_back(mPathData[i]);
+    }
+  }
 }
 
 }
 }
--- a/gfx/2d/PathCairo.h
+++ b/gfx/2d/PathCairo.h
@@ -3,124 +3,59 @@
  * 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/. */
 
 #ifndef MOZILLA_GFX_PATH_CAIRO_H_
 #define MOZILLA_GFX_PATH_CAIRO_H_
 
 #include "2D.h"
 #include "cairo.h"
+#include <vector>
 
 namespace mozilla {
 namespace gfx {
 
 class DrawTargetCairo;
-
-// A reference to a cairo context that can maintain and set a path.
-//
-// This class exists to make it possible for us to not construct paths manually
-// using cairo_path_t, which in the common case is a speed and memory
-// optimization (as the cairo_t maintains the path for us, and we don't have to
-// use cairo_append_path). Instead, we can share a cairo_t with a DrawTarget,
-// and have it inform us when we need to make a copy of the path.
-//
-// Exactly one Path* object represents the current path on a given DrawTarget's
-// context. That Path* object registers its CairoPathContext with the
-// DrawTarget it's associated with. If that DrawTarget is going to change its
-// path, it has to tell the CairoPathContext beforehand so the path can be
-// saved off.
-// The path ownership is transferred to every new instance of CairoPathContext
-// in the constructor. We inform the draw target of the new context object,
-// which causes us to save off a copy of the path, as we're not going to be
-// informed upon changes any more.
-// Any transformation on aCtx is not applied to this path, though a path can be
-// transformed separately from its context by passing a matrix to the
-// constructor.
-class CairoPathContext : public RefCounted<CairoPathContext>
-{
-public:
-  // Construct a new empty CairoPathContext that uses the given draw target and
-  // its cairo context. Using the existing context may save having to copy the
-  // path later.
-  CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget);
-
-  // Copy the path.
-  CairoPathContext(CairoPathContext& aPathContext);
-
-  ~CairoPathContext();
-
-  // Copy the path on mContext to be the path on aToContext, if they aren't the
-  // same. At this point we set the fill rule for the destination context as
-  // there is little point in doing this earlier.
-  void CopyPathTo(cairo_t* aToContext, Matrix& aTransform);
-
-  // This method must be called by the draw target before it changes the path
-  // currently on the cairo context.
-  void PathWillChange();
-
-  // This method must be called as the draw target is dying. In this case, we
-  // forget our reference to the draw target, and become the only reference to
-  // our context.
-  void ForgetDrawTarget();
-
-  // Create a duplicate context, and copy this path to that context.
-  void DuplicateContextAndPath();
-
-  // Returns true if this CairoPathContext represents path.
-  bool ContainsPath(const Path* path);
-
-  cairo_t* GetContext() const { return mContext; }
-  DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; }
-  operator cairo_t* () const { return mContext; }
-
-private: // data
-  cairo_t* mContext;
-  // Not a RefPtr to avoid cycles.
-  DrawTargetCairo* mDrawTarget;
-};
+class PathCairo;
 
 class PathBuilderCairo : public PathBuilder
 {
 public:
-  // Creates a new empty path. It also implicitly takes ownership of aCtx by
-  // calling aDrawTarget->SetPathObserver(). Therefore, if the draw target has a
-  // path observer, this constructor will cause it to copy out its path.
-  PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule);
-
-  // Creates a path builder out of an existing CairoPathContext with a new fill
-  // rule and transform.
-  PathBuilderCairo(CairoPathContext* aContext, FillRule aFillRule, const Matrix& aTransform = Matrix());
+  PathBuilderCairo(FillRule aFillRule);
 
   virtual void MoveTo(const Point &aPoint);
   virtual void LineTo(const Point &aPoint);
   virtual void BezierTo(const Point &aCP1,
                         const Point &aCP2,
                         const Point &aCP3);
   virtual void QuadraticBezierTo(const Point &aCP1,
                                  const Point &aCP2);
   virtual void Close();
   virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
                    float aEndAngle, bool aAntiClockwise = false);
   virtual Point CurrentPoint() const;
   virtual TemporaryRef<Path> Finish();
 
-  TemporaryRef<CairoPathContext> GetPathContext();
+private: // data
+  friend class PathCairo;
 
-private: // data
-  void PrepareForWrite();
-
-  RefPtr<CairoPathContext> mPathContext;
-  Matrix mTransform;
   FillRule mFillRule;
+  std::vector<cairo_path_data_t> mPathData;
+  // It's easiest to track this here, parsing the path data to find the current
+  // point is a little tricky.
+  Point mCurrentPoint;
+  Point mBeginPoint;
 };
 
 class PathCairo : public Path
 {
 public:
-  PathCairo(CairoPathContext* aPathContex, Matrix& aTransform, FillRule aFillRule);
+  PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint);
+  PathCairo(cairo_t *aContext);
+  ~PathCairo();
 
   virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
 
   virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
   virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
                                                              FillRule aFillRule = FILL_WINDING) const;
 
   virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
@@ -131,25 +66,24 @@ public:
 
   virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
 
   virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
                                 const Matrix &aTransform = Matrix()) const;
 
   virtual FillRule GetFillRule() const { return mFillRule; }
 
-  TemporaryRef<CairoPathContext> GetPathContext();
+  void SetPathOnContext(cairo_t *aContext) const;
 
-  // Set this path to be the current path for aContext (if it's not already
-  // aContext's path). You must pass the draw target associated with the
-  // context as aDrawTarget.
-  void CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget);
+  void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const;
+private:
+  void EnsureContainingContext() const;
 
-private:
-  RefPtr<CairoPathContext> mPathContext;
-  Matrix mTransform;
   FillRule mFillRule;
+  std::vector<cairo_path_data_t> mPathData;
+  mutable cairo_t *mContainingContext;
+  Point mCurrentPoint;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_PATH_CAIRO_H_ */
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -8,16 +8,18 @@
 #ifdef USE_SKIA
 #include "PathSkia.h"
 #include "skia/SkPaint.h"
 #include "skia/SkPath.h"
 #endif
 
 #ifdef USE_CAIRO
 #include "PathCairo.h"
+#include "DrawTargetCairo.h"
+#include "HelpersCairo.h"
 #endif
 
 #include <vector>
 #include <cmath>
 
 using namespace std;
 
 namespace mozilla {
@@ -69,59 +71,80 @@ ScaledFontBase::GetPathForGlyphs(const G
     paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
     return new PathSkia(path, FILL_WINDING);
   }
 #endif
 #ifdef USE_CAIRO
   if (aTarget->GetType() == BACKEND_CAIRO) {
     MOZ_ASSERT(mScaledFont);
 
-    RefPtr<PathBuilder> builder_iface = aTarget->CreatePathBuilder();
-    PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(builder_iface.get());
+    DrawTarget *dt = const_cast<DrawTarget*>(aTarget);
+    cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NATIVE_SURFACE_CAIRO_CONTEXT));
 
-    // Manually build the path for the PathBuilder.
-    RefPtr<CairoPathContext> context = builder->GetPathContext();
+    bool isNewContext = !ctx;
+    if (!ctx) {
+      ctx = cairo_create(DrawTargetCairo::GetDummySurface());
+      cairo_matrix_t mat;
+      GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat);
+      cairo_set_matrix(ctx, &mat);
+    }
 
-    cairo_set_scaled_font(*context, mScaledFont);
+    cairo_set_scaled_font(ctx, mScaledFont);
 
     // Convert our GlyphBuffer into an array of Cairo glyphs.
     std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
     for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
       glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
       glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
       glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
     }
 
-    cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
+    cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
 
-    return builder->Finish();
+    RefPtr<PathCairo> newPath = new PathCairo(ctx);
+    if (isNewContext) {
+      cairo_destroy(ctx);
+    }
+
+    return newPath;
   }
 #endif
   return nullptr;
 }
 
 void
-ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
 {
 #ifdef USE_CAIRO
   PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
 
-  RefPtr<CairoPathContext> context = builder->GetPathContext();
+  
+  cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface());
 
-  cairo_set_scaled_font(*context, mScaledFont);
+  if (aTransformHint) {
+    cairo_matrix_t mat;
+    GfxMatrixToCairoMatrix(*aTransformHint, mat);
+    cairo_set_matrix(ctx, &mat);
+  }
 
   // Convert our GlyphBuffer into an array of Cairo glyphs.
   std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
   for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
     glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
     glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
     glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
   }
 
-  cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
+  cairo_set_scaled_font(ctx, mScaledFont);
+  cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+  RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
+  cairo_destroy(ctx);
+
+  cairoPath->AppendPathToBuilder(builder);
 #endif
 }
 
 #ifdef USE_CAIRO_SCALED_FONT
 void
 ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font)
 {
   MOZ_ASSERT(!mScaledFont);
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -28,17 +28,17 @@ namespace gfx {
 class ScaledFontBase : public ScaledFont
 {
 public:
   ScaledFontBase(Float aSize);
   virtual ~ScaledFontBase();
 
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
 
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
 
   float GetSize() { return mSize; }
 
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface() { return mTypeface; }
 #endif
 
   // Not true, but required to instantiate a ScaledFontBase.
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -319,17 +319,17 @@ ScaledFontDWrite::GetPathForGlyphs(const
     static_cast<PathBuilderD2D*>(pathBuilder.get());
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 
   return pathBuilder->Finish();
 }
 
 void
-ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
 {
   // XXX - Check path builder type!
   PathBuilderD2D *pathBuilderD2D =
     static_cast<PathBuilderD2D*>(aBuilder);
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 }
 
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -21,17 +21,17 @@ public:
     : mFontFace(aFont)
     , ScaledFontBase(aSize)
   {}
   ScaledFontDWrite(uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize);
 
   virtual FontType GetType() const { return FONT_DWRITE; }
 
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
 
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
   virtual AntialiasMode GetDefaultAAMode();
 
 #ifdef USE_SKIA
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -97,17 +97,17 @@ ScaledFontMac::GetPathForGlyphs(const Gl
       CGPathRelease(path);
       return ret;
   } else {
       return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
   }
 }
 
 void
-ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
 {
   PathBuilderCG *pathBuilderCG =
     static_cast<PathBuilderCG*>(aBuilder);
   // XXX: check builder type
   for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
     // XXX: we could probably fold both of these transforms together to avoid extra work
     CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
     CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -20,17 +20,17 @@ public:
   ScaledFontMac(CGFontRef aFont, Float aSize);
   virtual ~ScaledFontMac();
 
   virtual FontType GetType() const { return FONT_MAC; }
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
 #endif
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
 private:
   friend class DrawTargetCG;
   CGFontRef mFont;
   CTFontRef mCTFont; // only created if CTFontDrawGlyphs is available, otherwise null
 
   typedef void (CTFontDrawGlyphsFuncT)(CTFontRef,
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2325,17 +2325,18 @@ struct GlyphBufferAzure {
                                 aDrawOptions, aOptions);
             } else {
                 aDT->FillGlyphs(aFont, buf, ColorPattern(state.color),
                                 aDrawOptions, aOptions);
             }
         }
         if (aDrawMode & gfxFont::GLYPH_PATH) {
             aThebesContext->EnsurePathBuilder();
-            aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder);
+			Matrix mat = aDT->GetTransform();
+            aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, &mat);
         }
         if ((aDrawMode & (gfxFont::GLYPH_STROKE | gfxFont::GLYPH_STROKE_UNDERNEATH)) ==
                           gfxFont::GLYPH_STROKE) {
             FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
         }
 
         mNumGlyphs = 0;
     }