Bug 740598 - Add optimized version of CopyRect for the cairo backend. r=Bas
authorMatt Woodrow <mwoodrow@mozilla.com>
Tue, 15 Oct 2013 14:55:16 +1300
changeset 150713 0cb34784d9da2f7ed3c11d306709e58ada105219
parent 150712 b3a7906130010476f88e2599f22d6233cf0ee1ad
child 150743 ffa4e404f6f71fcfc80856ac85d8e0751d2bce45
push id34959
push usermwoodrow@mozilla.com
push dateTue, 15 Oct 2013 01:55:26 +0000
treeherdermozilla-inbound@0cb34784d9da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs740598
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 740598 - Add optimized version of CopyRect for the cairo backend. r=Bas
gfx/2d/DrawTargetCairo.cpp
gfx/2d/DrawTargetCairo.h
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -124,16 +124,35 @@ GetCairoSurfaceSize(cairo_surface_t* sur
 #endif
 
     default:
       return false;
   }
 }
 
 static bool
+SupportsSelfCopy(cairo_surface_t* surface)
+{
+  switch (cairo_surface_get_type(surface))
+  {
+#ifdef CAIRO_HAS_QUARTZ_SURFACE
+    case CAIRO_SURFACE_TYPE_QUARTZ:
+      return true;
+#endif
+#ifdef CAIRO_HAS_WIN32_SURFACE
+    case CAIRO_SURFACE_TYPE_WIN32:
+    case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
+      return true;
+#endif
+    default:
+      return false;
+  }
+}
+
+static bool
 PatternIsCompatible(const Pattern& aPattern)
 {
   switch (aPattern.GetType())
   {
     case PATTERN_LINEAR_GRADIENT:
     {
       const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
       return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
@@ -632,40 +651,81 @@ DrawTargetCairo::FillRect(const Rect &aR
 
   cairo_new_path(mContext);
   cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
 
   DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
 }
 
 void
+DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface,
+                                     const IntRect &aSource,
+                                     const IntPoint &aDest)
+{
+  cairo_identity_matrix(mContext);
+
+  cairo_set_source_surface(mContext, aSurface, aDest.x - aSource.x, aDest.y - aSource.y);
+  cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
+  cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
+
+  cairo_reset_clip(mContext);
+  cairo_new_path(mContext);
+  cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height);
+  cairo_fill(mContext);
+}
+
+void
 DrawTargetCairo::CopySurface(SourceSurface *aSurface,
                              const IntRect &aSource,
                              const IntPoint &aDest)
 {
   AutoPrepareForDrawing prep(this, mContext);
   AutoClearDeviceOffset clear(aSurface);
 
   if (!aSurface || aSurface->GetType() != SURFACE_CAIRO) {
     gfxWarning() << "Unsupported surface type specified";
     return;
   }
 
   cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
 
-  cairo_identity_matrix(mContext);
+  CopySurfaceInternal(surf, aSource, aDest);
+}
+
+void
+DrawTargetCairo::CopyRect(const IntRect &aSource,
+                          const IntPoint &aDest)
+{
+  AutoPrepareForDrawing prep(this, mContext);
+
+  IntRect source = aSource;
+  cairo_surface_t* surf = mSurface;
 
-  cairo_set_source_surface(mContext, surf, aDest.x - aSource.x, aDest.y - aSource.y);
-  cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
-  cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
+  if (!SupportsSelfCopy(mSurface) &&
+      aDest.y >= aSource.y &&
+      aDest.y < aSource.YMost()) {
+    cairo_surface_t* similar = cairo_surface_create_similar(mSurface,
+                                                            GfxFormatToCairoContent(GetFormat()),
+                                                            aSource.width, aSource.height);
+    cairo_t* ctx = cairo_create(similar);
+    cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_surface(ctx, surf, -aSource.x, -aSource.y);
+    cairo_paint(ctx);
+    cairo_destroy(ctx);
 
-  cairo_reset_clip(mContext);
-  cairo_new_path(mContext);
-  cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height);
-  cairo_fill(mContext);
+    source.x = 0;
+    source.y = 0;
+    surf = similar;
+  }
+
+  CopySurfaceInternal(surf, source, aDest);
+
+  if (surf != mSurface) {
+    cairo_surface_destroy(surf);
+  }
 }
 
 void
 DrawTargetCairo::ClearRect(const Rect& aRect)
 {
   AutoPrepareForDrawing prep(this, mContext);
 
   cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -73,16 +73,18 @@ public:
                                      Float aSigma,
                                      CompositionOp aOperator);
 
   virtual void ClearRect(const Rect &aRect);
 
   virtual void CopySurface(SourceSurface *aSurface,
                            const IntRect &aSourceRect,
                            const IntPoint &aDestination);
+  virtual void CopyRect(const IntRect &aSourceRect,
+                        const IntPoint &aDestination);
 
   virtual void FillRect(const Rect &aRect,
                         const Pattern &aPattern,
                         const DrawOptions &aOptions = DrawOptions());
   virtual void StrokeRect(const Rect &aRect,
                           const Pattern &aPattern,
                           const StrokeOptions &aStrokeOptions = StrokeOptions(),
                           const DrawOptions &aOptions = DrawOptions());
@@ -156,16 +158,20 @@ private: // methods
   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,
                    DrawPatternType aDrawType);
 
+  void CopySurfaceInternal(cairo_surface_t* aSurface,
+                           const IntRect& aSource,
+                           const IntPoint& aDest);
+
   // Call before you make any changes to the backing surface with which this
   // context is associated. Pass the path you're going to be using if you have
   // one.
   void WillChange(const Path* aPath = nullptr);
 
   // Call if there is any reason to disassociate the snapshot from this draw
   // target; for example, because we're going to be destroyed.
   void MarkSnapshotIndependent();