b=415854, make single-pixel optimized images release memory; patch from joe@drew.ca; r+sr=vlad
authorvladimir@pobox.com
Wed, 05 Mar 2008 22:51:13 -0800
changeset 12652 25b22715c7bcc71e1483433f6086d5c3fed15754
parent 12651 18fcca045f2ed2dea809499c011dbe8a7d7fa2eb
child 12653 f8ad6f96274304f470e9c076d0604cdf47f37fd4
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs415854
milestone1.9b5pre
b=415854, make single-pixel optimized images release memory; patch from joe@drew.ca; r+sr=vlad
content/canvas/src/nsCanvasRenderingContext2D.cpp
content/svg/content/src/Makefile.in
content/svg/content/src/nsSVGFilters.cpp
gfx/public/nsIImage.h
gfx/src/thebes/nsThebesImage.cpp
gfx/src/thebes/nsThebesImage.h
gfx/thebes/public/gfxPattern.h
gfx/thebes/src/gfxPattern.cpp
layout/svg/base/src/Makefile.in
layout/svg/base/src/nsSVGImageFrame.cpp
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
modules/libpr0n/src/imgContainer.cpp
modules/libpr0n/src/imgTools.cpp
widget/src/gtk2/nsImageToPixbuf.cpp
widget/src/gtk2/nsImageToPixbuf.h
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -2215,19 +2215,28 @@ nsCanvasRenderingContext2D::CairoSurface
     if (NS_FAILED(rv))
         return NS_ERROR_FAILURE;
 
     if (widthOut)
         *widthOut = imgWidth;
     if (heightOut)
         *heightOut = imgHeight;
 
-    nsRefPtr<gfxASurface> gfxsurf;
-    rv = img->GetSurface(getter_AddRefs(gfxsurf));
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsRefPtr<gfxPattern> gfxpattern;
+    img->GetPattern(getter_AddRefs(gfxpattern));
+    nsRefPtr<gfxASurface> gfxsurf = gfxpattern->GetSurface();
+
+    if (!gfxsurf) {
+        gfxsurf = new gfxImageSurface (gfxIntSize(imgWidth, imgHeight), gfxASurface::ImageFormatARGB32);
+        nsRefPtr<gfxContext> ctx = new gfxContext(gfxsurf);
+
+        ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+        ctx->SetPattern(gfxpattern);
+        ctx->Paint();
+    }
 
     *aCairoSurface = gfxsurf->CairoSurface();
     cairo_surface_reference (*aCairoSurface);
     *imgData = nsnull;
 
     return NS_OK;
 }
 
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -169,12 +169,11 @@ INCLUDES += 	\
 		-I$(srcdir)/../../../xml/content/src \
 		-I$(srcdir)/../../../../dom \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../../layout/svg/base/src \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../events/src \
 		-I$(srcdir)/../../../html/content/src \
 		-I$(topsrcdir)/content/xbl/src \
-		-I$(topsrcdir)/gfx/src/thebes \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -58,17 +58,17 @@
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "gfxContext.h"
 #include "nsSVGLengthList.h"
 #include "nsIDOMSVGURIReference.h"
 #include "nsImageLoadingContent.h"
 #include "imgIContainer.h"
 #include "gfxIImageFrame.h"
-#include "nsThebesImage.h"
+#include "nsIImage.h"
 #include "nsSVGAnimatedPreserveAspectRatio.h"
 #include "nsSVGPreserveAspectRatio.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsSVGMatrix.h"
 #include "nsSVGFilterElement.h"
 #if defined(XP_WIN)
 // Prevent Windows redefining LoadImage
 #undef LoadImage
@@ -5577,46 +5577,41 @@ nsSVGFEImageElement::Filter(nsSVGFilterI
   nsCOMPtr<imgIContainer> imageContainer;
   if (currentRequest)
     currentRequest->GetImage(getter_AddRefs(imageContainer));
 
   nsCOMPtr<gfxIImageFrame> currentFrame;
   if (imageContainer)
     imageContainer->GetCurrentFrame(getter_AddRefs(currentFrame));
 
-  gfxASurface *thebesSurface = nsnull;
+  nsRefPtr<gfxPattern> thebesPattern = nsnull;
   if (currentFrame) {
     nsCOMPtr<nsIImage> img(do_GetInterface(currentFrame));
 
-    nsThebesImage *thebesImage = nsnull;
-    if (img)
-      thebesImage = static_cast<nsThebesImage*>(img.get());
-
-    if (thebesImage)
-      thebesSurface = thebesImage->ThebesSurface();
+    img->GetPattern(getter_AddRefs(thebesPattern));
   }
 
-  if (thebesSurface) {
+  if (thebesPattern) {
     PRInt32 x, y, nativeWidth, nativeHeight;
     currentFrame->GetX(&x);
     currentFrame->GetY(&y);
     currentFrame->GetWidth(&nativeWidth);
     currentFrame->GetHeight(&nativeHeight);
 
     nsCOMPtr<nsIDOMSVGMatrix> trans;
     trans = nsSVGUtils::GetViewBoxTransform(filterSubregion.width, filterSubregion.height,
                                             x, y,
                                             nativeWidth, nativeHeight,
                                             mPreserveAspectRatio);
     nsCOMPtr<nsIDOMSVGMatrix> xy, fini;
     NS_NewSVGMatrix(getter_AddRefs(xy), 1, 0, 0, 1, filterSubregion.x, filterSubregion.y);
     xy->Multiply(trans, getter_AddRefs(fini));
 
     gfxContext ctx(targetSurface);
-    nsSVGUtils::CompositeSurfaceMatrix(&ctx, thebesSurface, fini, 1.0);
+    nsSVGUtils::CompositePatternMatrix(&ctx, thebesPattern, fini, nativeWidth, nativeHeight, 1.0);
   }
 
   return NS_OK;
 }
 
 nsRect
 nsSVGFEImageElement::ComputeTargetBBox(const nsTArray<nsRect>& aSourceBBoxes,
         const nsSVGFilterInstance& aInstance)
--- a/gfx/public/nsIImage.h
+++ b/gfx/public/nsIImage.h
@@ -39,16 +39,17 @@
 #define nsIImage_h___
 
 #include "nsISupports.h"
 #include "nsIRenderingContext.h"
 #include "nsRect.h"
 #include "gfxRect.h"
 
 class gfxASurface;
+class gfxPattern;
 
 class nsIDeviceContext;
 
 struct nsColorMap
 {
   //I lifted this from the image lib. The difference is that
   //this uses nscolor instead of NI_RGB. Multiple color pollution
   //is a bad thing. MMP
@@ -208,17 +209,21 @@ public:
    * @return a bitmap info structure for the Device Dependent Bits
    */
   virtual void* GetBitInfo() = 0;
 
 
   /**
    * LockImagePixels
    * Lock the image pixels so that we can access them directly,
-   * with safely. May be a noop on some platforms.
+   * with safety. May be a noop on some platforms.
+   *
+   * If you want to be able to call GetSurface(), wrap the call in
+   * LockImagePixels()/UnlockImagePixels(). This also allows you to write to
+   * the surface returned by GetSurface().
    *
    * aMaskPixels = PR_TRUE for the mask, PR_FALSE for the image
    *
    * Must be balanced by a call to UnlockImagePixels().
    *
    * @update - sfraser 10/18/99
    * @return error result
    */
@@ -234,24 +239,35 @@ public:
    *
    * @update - sfraser 10/18/99
    * @return error result
    */
   NS_IMETHOD UnlockImagePixels(PRBool aMaskPixels) = 0;
 
   /**
    * GetSurface
-   * Return the Thebes gfxASurface in aSurface.
+   * Return the Thebes gfxASurface in aSurface, if there is one. Should be
+   * wrapped by LockImagePixels()/UnlockImagePixels().
    *
    * aSurface will be AddRef'd (as with most getters), so
    * getter_AddRefs should be used.
    */
   NS_IMETHOD GetSurface(gfxASurface **aSurface) = 0;
 
   /**
+   * GetSurface
+   * Return the Thebes gfxPattern in aPattern. It is always possible to get a
+   * gfxPattern (unlike the gfxASurface from GetSurface()).
+   *
+   * aPattern will be AddRef'd (as with most getters), so
+   * getter_AddRefs should be used.
+   */
+  NS_IMETHOD GetPattern(gfxPattern **aPattern) = 0;
+
+  /**
    * SetHasNoAlpha
    *
    * Hint to the image that all the pixels are fully opaque, even if
    * the original format requested a 1-bit or 8-bit alpha mask
    */
   virtual void SetHasNoAlpha() = 0;
 };
 
--- a/gfx/src/thebes/nsThebesImage.cpp
+++ b/gfx/src/thebes/nsThebesImage.cpp
@@ -254,34 +254,26 @@ nsThebesImage::Optimize(nsIDeviceContext
                 mSinglePixelColor = gfxRGBA
                     (firstPixel,
                      (mFormat == gfxImageSurface::ImageFormatRGB24 ?
                       gfxRGBA::PACKED_XRGB :
                       gfxRGBA::PACKED_ARGB_PREMULTIPLIED));
 
                 mSinglePixel = PR_TRUE;
 
-                // XXX we can't do this until we either teach anyone
-                // who calls GetSurface() about single-color stuff,
-                // or until we make GetSurface() create a new temporary
-                // surface to return (and that callers understand that
-                // modifying that surface won't modify the image).
-                // Current users are drag & drop and clipboard.
-#if 0
                 // blow away the older surfaces, to release data
 
                 mImageSurface = nsnull;
                 mOptSurface = nsnull;
 #ifdef XP_WIN
                 mWinSurface = nsnull;
 #endif
 #ifdef XP_MACOSX
                 mQuartzSurface = nsnull;
 #endif
-#endif
                 return NS_OK;
             }
         }
 
         // if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment
     }
 
     // if we're being forced to use image surfaces due to
@@ -386,17 +378,25 @@ nsThebesImage::LockImagePixels(PRBool aM
             return NS_ERROR_OUT_OF_MEMORY;
         gfxContext context(mImageSurface);
         context.SetOperator(gfxContext::OPERATOR_SOURCE);
         if (mSinglePixel)
             context.SetColor(mSinglePixelColor);
         else
             context.SetSource(mOptSurface);
         context.Paint();
+
+#ifdef XP_WIN
+        mWinSurface = nsnull;
+#endif
+#ifdef XP_MACOSX
+        mQuartzSurface = nsnull;
+#endif
     }
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThebesImage::UnlockImagePixels(PRBool aMaskPixels)
 {
     if (aMaskPixels)
         return NS_ERROR_NOT_IMPLEMENTED;
--- a/gfx/src/thebes/nsThebesImage.h
+++ b/gfx/src/thebes/nsThebesImage.h
@@ -39,16 +39,17 @@
 #ifndef _NSTHEBESIMAGE_H_
 #define _NSTHEBESIMAGE_H_
 
 #include "nsIImage.h"
 
 #include "gfxColor.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
+#include "gfxPattern.h"
 #if defined(XP_WIN)
 #include "gfxWindowsSurface.h"
 #elif defined(XP_MACOSX)
 #include "gfxQuartzImageSurface.h"
 #endif
 
 class nsThebesImage : public nsIImage
 {
@@ -91,16 +92,32 @@ public:
     NS_IMETHOD UnlockImagePixels(PRBool aMaskPixels);
 
     NS_IMETHOD GetSurface(gfxASurface **aSurface) {
         *aSurface = ThebesSurface();
         NS_ADDREF(*aSurface);
         return NS_OK;
     }
 
+    NS_IMETHOD GetPattern(gfxPattern **aPattern) {
+        *aPattern = ThebesPattern();
+        NS_ADDREF(*aPattern);
+        return NS_OK;
+    }
+
+    gfxPattern* ThebesPattern() {
+        gfxPattern *pattern;
+        if (mSinglePixel)
+            pattern = new gfxPattern(mSinglePixelColor);
+        else
+            pattern = new gfxPattern(ThebesSurface());
+
+        return pattern;
+    }
+
     gfxASurface* ThebesSurface() {
         if (mOptSurface)
             return mOptSurface;
 #if defined(XP_WIN)
         if (mWinSurface)
             return mWinSurface;
 #elif defined(XP_MACOSX)
         if (mQuartzSurface)
--- a/gfx/thebes/public/gfxPattern.h
+++ b/gfx/thebes/public/gfxPattern.h
@@ -73,16 +73,27 @@ public:
         EXTEND_REFLECT,
         EXTEND_PAD
     };
 
     // none, repeat, reflect
     void SetExtend(GraphicsExtend extend);
     GraphicsExtend Extend() const;
 
+    enum GraphicsPatternType {
+        PATTERN_SOLID,
+        PATTERN_SURFACE,
+        PATTERN_LINEAR,
+        PATTERN_RADIAL
+    };
+
+    GraphicsPatternType GetType() const;
+
+    int CairoStatus();
+
     void SetFilter(int filter);
     int Filter() const;
 
     /* returns TRUE if it succeeded */
     PRBool GetSolidColor(gfxRGBA& aColor);
 
     already_AddRefed<gfxASurface> GetSurface();
 
--- a/gfx/thebes/src/gfxPattern.cpp
+++ b/gfx/thebes/src/gfxPattern.cpp
@@ -163,11 +163,22 @@ gfxPattern::GetSolidColor(gfxRGBA& aColo
 already_AddRefed<gfxASurface>
 gfxPattern::GetSurface()
 {
     cairo_surface_t *surf = nsnull;
 
     if (cairo_pattern_get_surface (mPattern, &surf) != CAIRO_STATUS_SUCCESS)
         return nsnull;
 
-
     return gfxASurface::Wrap(surf);
 }
+
+gfxPattern::GraphicsPatternType
+gfxPattern::GetType() const
+{
+    return (GraphicsPatternType) cairo_pattern_get_type(mPattern);
+}
+
+int
+gfxPattern::CairoStatus()
+{
+    return cairo_pattern_status(mPattern);
+}
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -112,17 +112,16 @@ include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	= \
 		-I$(srcdir)/../../../base \
 		-I$(srcdir)/../../../generic \
 		-I$(srcdir)/../../../style \
 		-I$(srcdir)/../../../xul/base/src \
 		-I$(srcdir)/../../../../content/svg/content/src \
 		-I$(srcdir)/../../../../content/base/src \
-		-I$(topsrcdir)/gfx/src/thebes \
 		$(NULL)
 
 libs::
 	$(INSTALL) $(srcdir)/svg.css $(DIST)/bin/res
 
 install::
 	$(SYSINSTALL) $(IFLAGS1) $(srcdir)/svg.css $(DESTDIR)$(mozappdir)/res
 
--- a/layout/svg/base/src/nsSVGImageFrame.cpp
+++ b/layout/svg/base/src/nsSVGImageFrame.cpp
@@ -42,17 +42,17 @@
 #include "nsStubImageDecoderObserver.h"
 #include "nsImageLoadingContent.h"
 #include "nsIDOMSVGImageElement.h"
 #include "nsSVGElement.h"
 #include "nsSVGUtils.h"
 #include "nsSVGMatrix.h"
 #include "gfxContext.h"
 #include "nsIInterfaceRequestorUtils.h"
-#include "nsThebesImage.h"
+#include "nsIImage.h"
 
 class nsSVGImageFrame;
 
 class nsSVGImageListener : public nsStubImageDecoderObserver
 {
 public:
   nsSVGImageListener(nsSVGImageFrame *aFrame);
 
@@ -252,29 +252,24 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderSta
     if (currentRequest)
       currentRequest->GetImage(getter_AddRefs(mImageContainer));
   }
 
   nsCOMPtr<gfxIImageFrame> currentFrame;
   if (mImageContainer)
     mImageContainer->GetCurrentFrame(getter_AddRefs(currentFrame));
 
-  gfxASurface *thebesSurface = nsnull;
+  nsRefPtr<gfxPattern> thebesPattern = nsnull;
   if (currentFrame) {
     nsCOMPtr<nsIImage> img(do_GetInterface(currentFrame));
 
-    nsThebesImage *thebesImage = nsnull;
-    if (img)
-      thebesImage = static_cast<nsThebesImage*>(img.get());
-
-    if (thebesImage)
-      thebesSurface = thebesImage->ThebesSurface();
+    img->GetPattern(getter_AddRefs(thebesPattern));
   }
 
-  if (thebesSurface) {
+  if (thebesPattern) {
     gfxContext *gfx = aContext->GetGfxContext();
 
     if (GetStyleDisplay()->IsScrollableOverflow()) {
       gfx->Save();
 
       nsCOMPtr<nsIDOMSVGMatrix> ctm;
       GetCanvasTM(getter_AddRefs(ctm));
 
@@ -288,17 +283,17 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderSta
     // fill-opacity doesn't affect <image>, so if we're allowed to
     // optimize group opacity, the opacity used for compositing the
     // image into the current canvas is just the group opacity.
     float opacity = 1.0f;
     if (nsSVGUtils::CanOptimizeOpacity(this)) {
       opacity = GetStyleDisplay()->mOpacity;
     }
 
-    nsSVGUtils::CompositeSurfaceMatrix(gfx, thebesSurface, fini, opacity);
+    nsSVGUtils::CompositePatternMatrix(gfx, thebesPattern, fini, width, height, opacity);
 
     if (GetStyleDisplay()->IsScrollableOverflow())
       gfx->Restore();
   }
 
   return rv;
 }
 
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -1595,16 +1595,37 @@ nsSVGUtils::CompositeSurfaceMatrix(gfxCo
 
   aContext->SetSource(aSurface);
   aContext->Paint(aOpacity);
 
   aContext->Restore();
 }
 
 void
+nsSVGUtils::CompositePatternMatrix(gfxContext *aContext,
+                                   gfxPattern *aPattern,
+                                   nsIDOMSVGMatrix *aCTM, float aWidth, float aHeight, float aOpacity)
+{
+  gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
+  if (matrix.IsSingular())
+    return;
+
+  aContext->Save();
+
+  SetClipRect(aContext, aCTM, 0, 0, aWidth, aHeight);
+
+  aContext->Multiply(matrix);
+
+  aContext->SetPattern(aPattern);
+  aContext->Paint(aOpacity);
+
+  aContext->Restore();
+}
+
+void
 nsSVGUtils::SetClipRect(gfxContext *aContext,
                         nsIDOMSVGMatrix *aCTM, float aX, float aY,
                         float aWidth, float aHeight)
 {
   gfxMatrix matrix = ConvertSVGMatrixToThebes(aCTM);
   if (matrix.IsSingular())
     return;
 
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -64,16 +64,17 @@ class nsIDOMSVGAnimatedPreserveAspectRat
 class nsISVGValueObserver;
 class nsIAtom;
 class nsSVGLength2;
 class nsSVGElement;
 class nsSVGSVGElement;
 class nsAttrValue;
 class gfxContext;
 class gfxASurface;
+class gfxPattern;
 class nsIRenderingContext;
 class gfxImageSurface;
 struct gfxRect;
 struct gfxMatrix;
 struct gfxSize;
 struct gfxIntSize;
 struct nsStyleFont;
 class nsSVGEnum;
@@ -405,16 +406,20 @@ public:
               float aRX, float aRY, float aRWidth, float aRHeight,
               float aX, float aY);
 
 
   static void CompositeSurfaceMatrix(gfxContext *aContext,
                                      gfxASurface *aSurface,
                                      nsIDOMSVGMatrix *aCTM, float aOpacity);
 
+  static void CompositePatternMatrix(gfxContext *aContext,
+                                     gfxPattern *aPattern,
+                                     nsIDOMSVGMatrix *aCTM, float aWidth, float aHeight, float aOpacity);
+
   static void SetClipRect(gfxContext *aContext,
                           nsIDOMSVGMatrix *aCTM, float aX, float aY,
                           float aWidth, float aHeight);
 
   /* Using group opacity instead of fill or stroke opacity on a
    * geometry object seems to be a common authoring mistake.  If we're
    * not applying filters and not both stroking and filling, we can
    * generate the same result without going through the overhead of a
--- a/modules/libpr0n/src/imgContainer.cpp
+++ b/modules/libpr0n/src/imgContainer.cpp
@@ -1034,40 +1034,46 @@ nsresult imgContainer::DoComposite(gfxII
 // Fill aFrame with black. Does also clears the mask.
 void imgContainer::ClearFrame(gfxIImageFrame *aFrame)
 {
   if (!aFrame)
     return;
 
   nsCOMPtr<nsIImage> img(do_GetInterface(aFrame));
   nsRefPtr<gfxASurface> surf;
+
+  img->LockImagePixels(0);
   img->GetSurface(getter_AddRefs(surf));
 
   // Erase the surface to transparent
   gfxContext ctx(surf);
   ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
   ctx.Paint();
+  img->UnlockImagePixels(0);
 }
 
 //******************************************************************************
 void imgContainer::ClearFrame(gfxIImageFrame *aFrame, nsIntRect &aRect)
 {
   if (!aFrame || aRect.width <= 0 || aRect.height <= 0) {
     return;
   }
 
   nsCOMPtr<nsIImage> img(do_GetInterface(aFrame));
   nsRefPtr<gfxASurface> surf;
+
+  img->LockImagePixels(0);
   img->GetSurface(getter_AddRefs(surf));
 
   // Erase the destination rectangle to transparent
   gfxContext ctx(surf);
   ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
   ctx.Rectangle(gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
   ctx.Fill();
+  img->UnlockImagePixels(0);
 }
 
 
 //******************************************************************************
 // Whether we succeed or fail will not cause a crash, and there's not much
 // we can do about a failure, so there we don't return a nsresult
 PRBool imgContainer::CopyFrameImage(gfxIImageFrame *aSrcFrame,
                                     gfxIImageFrame *aDstFrame)
@@ -1179,37 +1185,39 @@ nsresult imgContainer::DrawFrameTo(gfxII
         dstPixels += dstRect.width;
       }
     }
     aDst->UnlockImageData();
     return NS_OK;
   }
 
   nsCOMPtr<nsIImage> srcImg(do_GetInterface(aSrc));
-  nsRefPtr<gfxASurface> srcSurf;
-  srcImg->GetSurface(getter_AddRefs(srcSurf));
+  nsRefPtr<gfxPattern> srcPatt;
+  srcImg->GetPattern(getter_AddRefs(srcPatt));
 
   nsCOMPtr<nsIImage> dstImg(do_GetInterface(aDst));
   nsRefPtr<gfxASurface> dstSurf;
+  // Note: dstImage has LockImageData() called on it above, so it's safe to get
+  // the surface.
   dstImg->GetSurface(getter_AddRefs(dstSurf));
 
   gfxContext dst(dstSurf);
   dst.Translate(gfxPoint(aSrcRect.x, aSrcRect.y));
   dst.Rectangle(gfxRect(0, 0, aSrcRect.width, aSrcRect.height), PR_TRUE);
   
   // first clear the surface if the blend flag says so
   PRInt32 blendMethod;
   aSrc->GetBlendMethod(&blendMethod);
   if (blendMethod == imgIContainer::kBlendSource) {
     gfxContext::GraphicsOperator defaultOperator = dst.CurrentOperator();
     dst.SetOperator(gfxContext::OPERATOR_CLEAR);
     dst.Fill();
     dst.SetOperator(defaultOperator);
   }
-  dst.SetSource(srcSurf);
+  dst.SetPattern(srcPatt);
   dst.Paint();
 
   return NS_OK;
 }
 
 
 /********* Methods to implement lazy allocation of nsIProperties object *************/
 NS_IMETHODIMP imgContainer::Get(const char *prop, const nsIID & iid, void * *result)
--- a/modules/libpr0n/src/imgTools.cpp
+++ b/modules/libpr0n/src/imgTools.cpp
@@ -314,36 +314,35 @@ NS_IMETHODIMP imgTools::EncodeScaledImag
 
     frame->GetImageBytesPerRow(&strideSize);
     bitmapDataLength = aScaledHeight * strideSize;
 
   } else {
     // Prepare to draw a scaled version of the image to a temporary surface...
 
     // Get the source image surface
-    nsRefPtr<gfxASurface> gfxsurf;
-    rv = img->GetSurface(getter_AddRefs(gfxsurf));
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsRefPtr<gfxPattern> gfxpat;
+    img->GetPattern(getter_AddRefs(gfxpat));
 
     // Create a temporary image surface
     dest = new gfxImageSurface(gfxIntSize(aScaledWidth, aScaledHeight),
                                gfxASurface::ImageFormatARGB32);
     if (!dest)
       return NS_ERROR_OUT_OF_MEMORY;
 
     gfxContext ctx(dest);
 
     // Set scaling
     gfxFloat sw = (double) aScaledWidth / w;
     gfxFloat sh = (double) aScaledHeight / h;
     ctx.Scale(sw, sh);
 
     // Paint a scaled image
     ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
-    ctx.SetSource(gfxsurf);
+    ctx.SetPattern(gfxpat);
     ctx.Paint();
 
     bitmapData = dest->Data();
     strideSize = dest->Stride();
     bitmapDataLength = aScaledHeight * strideSize;
   }
 
   // Encode the bitmap
--- a/widget/src/gtk2/nsImageToPixbuf.cpp
+++ b/widget/src/gtk2/nsImageToPixbuf.cpp
@@ -66,62 +66,37 @@ nsImageToPixbuf::ConvertImageToPixbuf(ns
 }
 
 GdkPixbuf*
 nsImageToPixbuf::ImageToPixbuf(nsIImage* aImage)
 {
     PRInt32 width = aImage->GetWidth(),
             height = aImage->GetHeight();
 
-    nsRefPtr<gfxASurface> surface;
-    aImage->GetSurface(getter_AddRefs(surface));
+    nsRefPtr<gfxPattern> pattern;
+    aImage->GetPattern(getter_AddRefs(pattern));
 
-    return SurfaceToPixbuf(surface, width, height);
+    return PatternToPixbuf(pattern, width, height);
 }
 
 GdkPixbuf*
-nsImageToPixbuf::SurfaceToPixbuf(gfxASurface* aSurface, PRInt32 aWidth, PRInt32 aHeight)
+nsImageToPixbuf::ImgSurfaceToPixbuf(gfxImageSurface* aImgSurface, PRInt32 aWidth, PRInt32 aHeight)
 {
-    if (aSurface->CairoStatus()) {
-        NS_ERROR("invalid surface");
-        return nsnull;
-    }
-
-    nsRefPtr<gfxImageSurface> imgSurface;
-    if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
-        imgSurface = static_cast<gfxImageSurface*>
-                                (static_cast<gfxASurface*>(aSurface));
-    } else {
-        imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
-					 gfxImageSurface::ImageFormatARGB32);
-                                       
-        if (!imgSurface)
-            return nsnull;
-
-        nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
-        if (!context)
-            return nsnull;
-
-        context->SetOperator(gfxContext::OPERATOR_SOURCE);
-        context->SetSource(aSurface);
-        context->Paint();
-    }
-
     GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, PR_TRUE, 8,
                                        aWidth, aHeight);
     if (!pixbuf)
         return nsnull;
 
     PRUint32 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
     guchar* pixels = gdk_pixbuf_get_pixels (pixbuf);
 
-    long cairoStride = imgSurface->Stride();
-    unsigned char* cairoData = imgSurface->Data();
+    long cairoStride = aImgSurface->Stride();
+    unsigned char* cairoData = aImgSurface->Data();
 
-    gfxASurface::gfxImageFormat format = imgSurface->Format();
+    gfxASurface::gfxImageFormat format = aImgSurface->Format();
 
     for (PRInt32 row = 0; row < aHeight; ++row) {
         for (PRInt32 col = 0; col < aWidth; ++col) {
             guchar* pixel = pixels + row * rowstride + 4 * col;
 
             PRUint32* cairoPixel = reinterpret_cast<PRUint32*>
                                                    ((cairoData + row * cairoStride + 4 * col));
 
@@ -147,8 +122,75 @@ nsImageToPixbuf::SurfaceToPixbuf(gfxASur
                 *pixel++ = b;
                 *pixel++ = 0xFF; // A
             }
         }
     }
 
     return pixbuf;
 }
+
+GdkPixbuf*
+nsImageToPixbuf::SurfaceToPixbuf(gfxASurface* aSurface, PRInt32 aWidth, PRInt32 aHeight)
+{
+    if (aSurface->CairoStatus()) {
+        NS_ERROR("invalid surface");
+        return nsnull;
+    }
+
+    nsRefPtr<gfxImageSurface> imgSurface;
+    if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
+        imgSurface = static_cast<gfxImageSurface*>
+                                (static_cast<gfxASurface*>(aSurface));
+    } else {
+        imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
+					 gfxImageSurface::ImageFormatARGB32);
+                                       
+        if (!imgSurface)
+            return nsnull;
+
+        nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
+        if (!context)
+            return nsnull;
+
+        context->SetOperator(gfxContext::OPERATOR_SOURCE);
+        context->SetSource(aSurface);
+        context->Paint();
+    }
+
+    return ImgSurfaceToPixbuf(imgSurface, aWidth, aHeight);
+}
+  
+GdkPixbuf*
+nsImageToPixbuf::PatternToPixbuf(gfxPattern* aPattern, PRInt32 aWidth, PRInt32 aHeight)
+{
+    if (aPattern->CairoStatus()) {
+        NS_ERROR("invalid pattern");
+        return nsnull;
+    }
+
+    nsRefPtr<gfxImageSurface> imgSurface;
+    if (aPattern->GetType() == gfxPattern::PATTERN_SURFACE) {
+        nsRefPtr<gfxASurface> surface = aPattern->GetSurface();
+        if (surface->GetType() == gfxASurface::SurfaceTypeImage) {
+            imgSurface = static_cast<gfxImageSurface*>
+                                    (static_cast<gfxASurface*>(surface.get()));
+        }
+    } 
+    
+    if (!imgSurface) {
+        imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
+					 gfxImageSurface::ImageFormatARGB32);
+                                       
+        if (!imgSurface)
+            return nsnull;
+
+        nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
+        if (!context)
+            return nsnull;
+
+        context->SetOperator(gfxContext::OPERATOR_SOURCE);
+        context->SetPattern(aPattern);
+        context->Paint();
+    }
+
+    return ImgSurfaceToPixbuf(imgSurface, aWidth, aHeight);
+}
--- a/widget/src/gtk2/nsImageToPixbuf.h
+++ b/widget/src/gtk2/nsImageToPixbuf.h
@@ -36,28 +36,34 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef NSIMAGETOPIXBUF_H_
 #define NSIMAGETOPIXBUF_H_
 
 #include "nsIImageToPixbuf.h"
 
 class gfxASurface;
+class gfxPattern;
+class gfxImageSurface;
 
 class nsImageToPixbuf : public nsIImageToPixbuf {
     public:
         NS_DECL_ISUPPORTS
         NS_IMETHOD_(GdkPixbuf*) ConvertImageToPixbuf(nsIImage* aImage);
 
         // Friendlier version of ConvertImageToPixbuf for callers inside of
         // widget
         static GdkPixbuf* ImageToPixbuf(nsIImage* aImage);
         static GdkPixbuf* SurfaceToPixbuf(gfxASurface* aSurface,
                                           PRInt32 aWidth, PRInt32 aHeight);
+        static GdkPixbuf* PatternToPixbuf(gfxPattern* aPattern,
+                                          PRInt32 aWidth, PRInt32 aHeight);
     private:
+        static GdkPixbuf* ImgSurfaceToPixbuf(gfxImageSurface* aImgSurface,
+                                             PRInt32 aWidth, PRInt32 aHeight);
         ~nsImageToPixbuf() {}
 };
 
 
 // fc2389b8-c650-4093-9e42-b05e5f0685b7
 #define NS_IMAGE_TO_PIXBUF_CID \
 { 0xfc2389b8, 0xc650, 0x4093, \
   { 0x9e, 0x42, 0xb0, 0x5e, 0x5f, 0x06, 0x85, 0xb7 } }