Bug 572680 - Make image drawing use the new gfxDrawable interface. r=joe
authorMarkus Stange <mstange@themasta.com>
Fri, 13 Aug 2010 15:30:27 +0200
changeset 50419 b5f727a62c7c0a417f25c847753c41502acf10c5
parent 50418 0fa683b7233de09df0471a31a9b337e2d507045e
child 50420 91f0d2cd19e832ba9b65631d2ce301c9aca0761a
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs572680
milestone2.0b4pre
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 572680 - Make image drawing use the new gfxDrawable interface. r=joe
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
modules/libpr0n/src/imgFrame.cpp
modules/libpr0n/src/imgFrame.h
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -33,16 +33,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "gfxUtils.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
+#include "gfxDrawable.h"
 
 #if defined(XP_WIN) || defined(WINCE)
 #include "gfxWindowsPlatform.h"
 #endif
 
 static PRUint8 sUnpremultiplyTable[256*256];
 static PRUint8 sPremultiplyTable[256*256];
 static PRBool sTablesInitialized = PR_FALSE;
@@ -203,79 +204,25 @@ gfxUtils::UnpremultiplyImageSurface(gfxI
 }
 
 static PRBool
 IsSafeImageTransformComponent(gfxFloat aValue)
 {
   return aValue >= -32768 && aValue <= 32767;
 }
 
-static void
-SetExtendAndFilterOnPattern(gfxPattern* aPattern,
-                            const gfxMatrix& aDeviceToImage,
-                            const gfxASurface::gfxSurfaceType aSurfaceType,
-                            const gfxPattern::GraphicsFilter aDefaultFilter)
-{
-    // In theory we can handle this using cairo's EXTEND_PAD,
-    // but implementation limitations mean we have to consult
-    // the surface type.
-    switch (aSurfaceType) {
-        case gfxASurface::SurfaceTypeXlib:
-        case gfxASurface::SurfaceTypeXcb:
-        {
-            // See bug 324698.  This is a workaround for EXTEND_PAD not being
-            // implemented correctly on linux in the X server.
-            //
-            // Set the filter to CAIRO_FILTER_FAST --- otherwise,
-            // pixman's sampling will sample transparency for the outside edges and we'll
-            // get blurry edges.  CAIRO_EXTEND_PAD would also work here, if
-            // available
-            //
-            // But don't do this for simple downscales because it's horrible.
-            // Downscaling means that device-space coordinates are
-            // scaled *up* to find the image pixel coordinates.
-            //
-            // aDeviceToImage is slightly stale because up above we may
-            // have adjusted the pattern's matrix ... but the adjustment
-            // is only a translation so the scale factors in aDeviceToImage
-            // are still valid.
-            PRBool isDownscale =
-                aDeviceToImage.xx >= 1.0 && aDeviceToImage.yy >= 1.0 &&
-                aDeviceToImage.xy == 0.0 && aDeviceToImage.yx == 0.0;
-            if (!isDownscale) {
-                aPattern->SetFilter(gfxPattern::FILTER_FAST);
-            }
-            break;
-        }
-
-        case gfxASurface::SurfaceTypeQuartz:
-        case gfxASurface::SurfaceTypeQuartzImage:
-            // Don't set EXTEND_PAD, Mac seems to be OK. Really?
-            aPattern->SetFilter(aDefaultFilter);
-            break;
-
-        default:
-            // turn on EXTEND_PAD.
-            // This is what we really want for all surface types, if the
-            // implementation was universally good.
-            aPattern->SetExtend(gfxPattern::EXTEND_PAD);
-            aPattern->SetFilter(aDefaultFilter);
-            break;
-    }
-}
-
 // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
 // the subimage of pixels we're allowed to sample.
-static already_AddRefed<gfxPattern>
-CreateSamplingRestrictedPattern(gfxASurface* aSurface,
-                                gfxContext* aContext,
-                                const gfxMatrix& aUserSpaceToImageSpace,
-                                const gfxRect& aSourceRect,
-                                const gfxRect& aSubimage,
-                                const gfxImageSurface::gfxImageFormat aFormat)
+static already_AddRefed<gfxDrawable>
+CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
+                                 gfxContext* aContext,
+                                 const gfxMatrix& aUserSpaceToImageSpace,
+                                 const gfxRect& aSourceRect,
+                                 const gfxRect& aSubimage,
+                                 const gfxImageSurface::gfxImageFormat aFormat)
 {
     gfxRect userSpaceClipExtents = aContext->GetClipExtents();
     // This isn't optimal --- if aContext has a rotation then GetClipExtents
     // will have to do a bounding-box computation, and TransformBounds might
     // too, so we could get a better result if we computed image space clip
     // extents in one go --- but it doesn't really matter and this is easier
     // to understand.
     gfxRect imageSpaceClipExtents =
@@ -296,35 +243,28 @@ CreateSamplingRestrictedPattern(gfxASurf
         return nsnull;
 
     gfxIntSize size(PRInt32(needed.Width()), PRInt32(needed.Height()));
     nsRefPtr<gfxASurface> temp =
         gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, aFormat);
     if (!temp || temp->CairoStatus())
         return nsnull;
 
-    nsRefPtr<gfxPattern> tmpPattern = new gfxPattern(aSurface);
-    if (!tmpPattern)
-        return nsnull;
-
-    tmpPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
-    tmpPattern->SetMatrix(gfxMatrix().Translate(needed.pos));
-
     gfxContext tmpCtx(temp);
     tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
-    tmpCtx.SetPattern(tmpPattern);
-    tmpCtx.Paint();
+    aDrawable->Draw(&tmpCtx, needed - needed.pos, PR_TRUE,
+                    gfxPattern::FILTER_FAST, gfxMatrix().Translate(needed.pos));
 
     nsRefPtr<gfxPattern> resultPattern = new gfxPattern(temp);
     if (!resultPattern)
         return nsnull;
 
-    resultPattern->SetMatrix(
-        gfxMatrix(aUserSpaceToImageSpace).Multiply(gfxMatrix().Translate(-needed.pos)));
-    return resultPattern.forget();
+    nsRefPtr<gfxDrawable> drawable = 
+        new gfxSurfaceDrawable(temp, size, gfxMatrix().Translate(-needed.pos));
+    return drawable.forget();
 }
 
 // working around cairo/pixman bug (bug 364968)
 // Our device-space-to-image-space transform may not be acceptable to pixman.
 struct NS_STACK_CLASS AutoCairoPixmanBugWorkaround
 {
     AutoCairoPixmanBugWorkaround(gfxContext*      aContext,
                                  const gfxMatrix& aDeviceSpaceToImageSpace,
@@ -417,75 +357,64 @@ DeviceToImageTransform(gfxContext* aCont
     gfxMatrix currentMatrix = aContext->CurrentMatrix();
     gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert();
     deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
     return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace);
 }
 
 /* static */ void
 gfxUtils::DrawPixelSnapped(gfxContext*      aContext,
-                           gfxASurface*     aSurface,
+                           gfxDrawable*     aDrawable,
                            const gfxMatrix& aUserSpaceToImageSpace,
                            const gfxRect&   aSubimage,
                            const gfxRect&   aSourceRect,
                            const gfxRect&   aImageRect,
                            const gfxRect&   aFill,
                            const gfxImageSurface::gfxImageFormat aFormat,
                            const gfxPattern::GraphicsFilter& aFilter)
 {
     PRBool doTile = !aImageRect.Contains(aSourceRect);
 
     nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
     gfxASurface::gfxSurfaceType surfaceType = currentTarget->GetType();
-    gfxMatrix currentMatrix = aContext->CurrentMatrix();
     gfxMatrix deviceSpaceToImageSpace =
         DeviceToImageTransform(aContext, aUserSpaceToImageSpace);
 
     AutoCairoPixmanBugWorkaround workaround(aContext, deviceSpaceToImageSpace,
                                             aFill, surfaceType);
     if (!workaround.Succeeded())
         return;
 
-    nsRefPtr<gfxPattern> pattern = new gfxPattern(aSurface);
-    pattern->SetMatrix(aUserSpaceToImageSpace);
+    nsRefPtr<gfxDrawable> drawable = aDrawable;
 
     // OK now, the hard part left is to account for the subimage sampling
     // restriction. If all the transforms involved are just integer
     // translations, then we assume no resampling will occur so there's
     // nothing to do.
     // XXX if only we had source-clipping in cairo!
-    if (!currentMatrix.HasNonIntegerTranslation() &&
-        !aUserSpaceToImageSpace.HasNonIntegerTranslation()) {
-        if (doTile) {
-            pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
-        }
-    } else {
+    if (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
+        aUserSpaceToImageSpace.HasNonIntegerTranslation()) {
         if (doTile || !aSubimage.Contains(aImageRect)) {
-            nsRefPtr<gfxPattern> restrictedPattern =
-                CreateSamplingRestrictedPattern(aSurface, aContext,
-                                                aUserSpaceToImageSpace,
-                                                aSourceRect, aSubimage, aFormat);
-            if (restrictedPattern) {
-                pattern.swap(restrictedPattern);
+            nsRefPtr<gfxDrawable> restrictedDrawable =
+              CreateSamplingRestrictedDrawable(aDrawable, aContext,
+                                               aUserSpaceToImageSpace, aSourceRect,
+                                               aSubimage, aFormat);
+            if (restrictedDrawable) {
+                drawable.swap(restrictedDrawable);
             }
         }
-        SetExtendAndFilterOnPattern(pattern, deviceSpaceToImageSpace, surfaceType,
-                                    aFilter);
+        // We no longer need to tile: Either we never needed to, or we already
+        // filled a surface with the tiled pattern; this surface can now be
+        // drawn without tiling.
+        doTile = PR_FALSE;
     }
 
     gfxContext::GraphicsOperator op = aContext->CurrentOperator();
     if ((op == gfxContext::OPERATOR_OVER || workaround.PushedGroup()) &&
         aFormat == gfxASurface::ImageFormatRGB24) {
         aContext->SetOperator(OptimalFillOperator());
     }
 
-    // Phew! Now we can actually draw this image
-    aContext->NewPath();
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-    pattern->SetFilter(gfxPattern::FILTER_FAST); 
-#endif
-    aContext->SetPattern(pattern);
-    aContext->Rectangle(aFill);
-    aContext->Fill();
+    drawable->Draw(aContext, aFill, doTile, aFilter, aUserSpaceToImageSpace);
 
     aContext->SetOperator(op);
 }
 
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -37,16 +37,18 @@
 
 #ifndef GFX_UTILS_H
 #define GFX_UTILS_H
 
 #include "gfxTypes.h"
 #include "gfxPattern.h"
 #include "gfxImageSurface.h"
 
+class gfxDrawable;
+
 class THEBES_API gfxUtils {
 public:
     /*
      * 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.
@@ -55,30 +57,30 @@ public:
      * aDestSurface is given, the data is copied over.
      */
     static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface,
                                         gfxImageSurface *aDestSurface = nsnull);
     static void UnpremultiplyImageSurface(gfxImageSurface *aSurface,
                                           gfxImageSurface *aDestSurface = nsnull);
 
     /**
-     * Draw a surface while working around limitations like bad support
+     * Draw something drawable while working around limitations like bad support
      * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
      * extreme user-space-to-image-space transforms.
      *
      * The input parameters here usually come from the output of our image
      * snapping algorithm in nsLayoutUtils.cpp.
      * This method is split from nsLayoutUtils::DrawPixelSnapped to allow for
      * adjusting the parameters. For example, certain images with transparent
      * margins only have a drawable subimage. For those images, imgFrame::Draw
      * will tweak the rects and transforms that it gets from the pixel snapping
      * algorithm before passing them on to this method.
      */
     static void DrawPixelSnapped(gfxContext*      aContext,
-                                 gfxASurface*     aSurface,
+                                 gfxDrawable*     aDrawable,
                                  const gfxMatrix& aUserSpaceToImageSpace,
                                  const gfxRect&   aSubimage,
                                  const gfxRect&   aSourceRect,
                                  const gfxRect&   aImageRect,
                                  const gfxRect&   aFill,
                                  const gfxImageSurface::gfxImageFormat aFormat,
                                  const gfxPattern::GraphicsFilter& aFilter);
 };
--- a/modules/libpr0n/src/imgFrame.cpp
+++ b/modules/libpr0n/src/imgFrame.cpp
@@ -404,26 +404,26 @@ imgFrame::SurfaceForDrawing(PRBool      
                             PRBool             aDoTile,
                             const nsIntMargin& aPadding,
                             gfxMatrix&         aUserSpaceToImageSpace,
                             gfxRect&           aFill,
                             gfxRect&           aSubimage,
                             gfxRect&           aSourceRect,
                             gfxRect&           aImageRect)
 {
+  gfxIntSize size(PRInt32(aImageRect.Width()), PRInt32(aImageRect.Height()));
   if (!aDoPadding && !aDoPartialDecode) {
     NS_ASSERTION(!mSinglePixel, "This should already have been handled");
-    return SurfaceWithFormat(ThebesSurface(), mFormat);
+    return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(), size), mFormat);
   }
 
   gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
 
   if (aDoTile || mSinglePixel) {
     // Create a temporary surface.
-    gfxIntSize size(PRInt32(aImageRect.Width()), PRInt32(aImageRect.Height()));
     // Give this surface an alpha channel because there are
     // transparent pixels in the padding or undecoded area
     gfxImageSurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
     nsRefPtr<gfxASurface> surface =
       gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, format);
     if (!surface || surface->CairoStatus())
       return SurfaceWithFormat();
 
@@ -432,33 +432,36 @@ imgFrame::SurfaceForDrawing(PRBool      
     tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
     if (mSinglePixel) {
       tmpCtx.SetDeviceColor(mSinglePixelColor);
     } else {
       tmpCtx.SetSource(ThebesSurface(), gfxPoint(aPadding.left, aPadding.top));
     }
     tmpCtx.Rectangle(available);
     tmpCtx.Fill();
-    return SurfaceWithFormat(surface, format);
+    return SurfaceWithFormat(new gfxSurfaceDrawable(surface, size), format);
   }
 
   // Not tiling, and we have a surface, so we can account for
   // padding and/or a partial decode just by twiddling parameters.
   // First, update our user-space fill rect.
   aSourceRect = aSourceRect.Intersect(available);
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
   aFill = imageSpaceToUserSpace.Transform(aSourceRect);
 
   aSubimage = aSubimage.Intersect(available) - gfxPoint(aPadding.left, aPadding.top);
   aUserSpaceToImageSpace.Multiply(gfxMatrix().Translate(-gfxPoint(aPadding.left, aPadding.top)));
   aSourceRect = aSourceRect - gfxPoint(aPadding.left, aPadding.top);
   aImageRect = gfxRect(0, 0, mSize.width, mSize.height);
 
-  return SurfaceWithFormat(ThebesSurface(), mFormat);
+  gfxIntSize availableSize(mDecoded.width, mDecoded.height);
+  return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(),
+                                                  availableSize),
+                           mFormat);
 }
 
 void imgFrame::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter,
                     const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
                     const nsIntMargin &aPadding, const nsIntRect &aSubimage)
 {
   NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
   NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
@@ -484,17 +487,17 @@ void imgFrame::Draw(gfxContext *aContext
 
   PRBool doTile = !imageRect.Contains(sourceRect);
   SurfaceWithFormat surfaceResult =
     SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
                       userSpaceToImageSpace, fill, subimage, sourceRect,
                       imageRect);
 
   if (surfaceResult.IsValid()) {
-    gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mSurface,
+    gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
                                userSpaceToImageSpace,
                                subimage, sourceRect, imageRect, fill,
                                surfaceResult.mFormat, aFilter);
   }
 }
 
 nsresult imgFrame::Extract(const nsIntRect& aRegion, imgFrame** aResult)
 {
--- a/modules/libpr0n/src/imgFrame.h
+++ b/modules/libpr0n/src/imgFrame.h
@@ -42,16 +42,17 @@
 #include "nsRect.h"
 #include "nsPoint.h"
 #include "nsSize.h"
 #include "gfxTypes.h"
 #include "nsID.h"
 #include "gfxIFormats.h"
 #include "gfxContext.h"
 #include "gfxPattern.h"
+#include "gfxDrawable.h"
 #include "gfxImageSurface.h"
 #if defined(XP_WIN)
 #include "gfxWindowsSurface.h"
 #elif defined(XP_MACOSX)
 #include "gfxQuartzImageSurface.h"
 #endif
 #include "nsAutoPtr.h"
 
@@ -138,22 +139,22 @@ public:
   PRUint32 EstimateMemoryUsed() const;
 
 private: // methods
   PRUint32 PaletteDataLength() const {
     return ((1 << mPaletteDepth) * sizeof(PRUint32));
   }
 
   struct SurfaceWithFormat {
-    nsRefPtr<gfxASurface> mSurface;
+    nsRefPtr<gfxDrawable> mDrawable;
     gfxImageSurface::gfxImageFormat mFormat;
     SurfaceWithFormat() {}
-    SurfaceWithFormat(gfxASurface* aSurface, gfxImageSurface::gfxImageFormat aFormat)
-     : mSurface(aSurface), mFormat(aFormat) {}
-    PRBool IsValid() { return !!mSurface; }
+    SurfaceWithFormat(gfxDrawable* aDrawable, gfxImageSurface::gfxImageFormat aFormat)
+     : mDrawable(aDrawable), mFormat(aFormat) {}
+    PRBool IsValid() { return !!mDrawable; }
   };
 
   SurfaceWithFormat SurfaceForDrawing(PRBool             aDoPadding,
                                       PRBool             aDoPartialDecode,
                                       PRBool             aDoTile,
                                       const nsIntMargin& aPadding,
                                       gfxMatrix&         aUserSpaceToImageSpace,
                                       gfxRect&           aFill,