Bug 403181. Various fixes to image rendering. Most importantly, we track the desired subimage of a CSS background image and ensure we don't sample outside it. r=vlad,sr=dbaron
authorroc+@cs.cmu.edu
Fri, 07 Mar 2008 00:34:12 -0800
changeset 12714 830c80df044a0f9b0e3c8bfabe282ebb06cfec29
parent 12713 381ebbb8970a2891c1608a25485055d6173fb785
child 12715 20dcbc90ce81b686b26fad90a97f516e178f08d2
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)
reviewersvlad, dbaron
bugs403181
milestone1.9b5pre
Bug 403181. Various fixes to image rendering. Most importantly, we track the desired subimage of a CSS background image and ensure we don't sample outside it. r=vlad,sr=dbaron
gfx/cairo/README
gfx/cairo/libpixman/src/pixman-compose.c
gfx/cairo/pixman-fbFetchTransformed-backout.patch
gfx/public/nsIImage.h
gfx/src/thebes/nsThebesImage.cpp
gfx/src/thebes/nsThebesImage.h
gfx/thebes/public/gfxRect.h
gfx/thebes/src/gfxRect.cpp
layout/base/nsCSSRendering.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/reftests/bugs/412093-1-ref.html
layout/reftests/bugs/412093-1.html
layout/reftests/bugs/reftest.list
widget/src/xpwidgets/nsBaseDragService.cpp
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -26,8 +26,9 @@ win32-logical-font-scale.patch: set CAIR
 nonfatal-assertions.patch: Make assertions non-fatal
 
 buggy-repeat.patch: Unconditionally turn on buggy-repeat handling to bandaid bug 413583.
 
 ==== pixman patches ====
 
 endian.patch: include cairo-platform.h for endian macros
 
+pixman-fbFetchTransformed-backout.patch: back out bad pixman commit, see http://lists.cairographics.org/archives/cairo/2008-March/013294.html
--- a/gfx/cairo/libpixman/src/pixman-compose.c
+++ b/gfx/cairo/libpixman/src/pixman-compose.c
@@ -4187,18 +4187,18 @@ fbFetchTransformed(bits_image_t * pict, 
     pixman_vector_t v;
     pixman_vector_t unit;
     pixman_bool_t affine = TRUE;
 
     bits = pict->bits;
     stride = pict->rowstride;
 
     /* reference point is the center of the pixel */
-    v.vector[0] = pixman_int_to_fixed(x) + pixman_fixed_1 / 2 - 1;
-    v.vector[1] = pixman_int_to_fixed(y) + pixman_fixed_1 / 2 - 1;
+    v.vector[0] = pixman_int_to_fixed(x) + pixman_fixed_1 / 2;
+    v.vector[1] = pixman_int_to_fixed(y) + pixman_fixed_1 / 2;
     v.vector[2] = pixman_fixed_1;
 
     /* when using convolution filters one might get here without a transform */
     if (pict->common.transform)
     {
         if (!pixman_transform_point_3d (pict->common.transform, &v))
         {
             fbFinishAccess (pict->pDrawable);
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/pixman-fbFetchTransformed-backout.patch
@@ -0,0 +1,32 @@
+diff -NrpU12 mozilla-trunk.c81ad22ccb3f/gfx/cairo/libpixman/src/pixman-compose.c mozilla-trunk/gfx/cairo/libpixman/src/pixman-compose.c
+--- mozilla-trunk.c81ad22ccb3f/gfx/cairo/libpixman/src/pixman-compose.c	2008-03-07 16:22:34.000000000 +1300
++++ mozilla-trunk/gfx/cairo/libpixman/src/pixman-compose.c	2008-03-07 16:22:35.000000000 +1300
+@@ -4183,26 +4183,26 @@ static void
+ fbFetchTransformed(bits_image_t * pict, int x, int y, int width, uint32_t *buffer, uint32_t *mask, uint32_t maskBits)
+ {
+     uint32_t     *bits;
+     int32_t    stride;
+     pixman_vector_t v;
+     pixman_vector_t unit;
+     pixman_bool_t affine = TRUE;
+ 
+     bits = pict->bits;
+     stride = pict->rowstride;
+ 
+     /* reference point is the center of the pixel */
+-    v.vector[0] = pixman_int_to_fixed(x) + pixman_fixed_1 / 2 - 1;
+-    v.vector[1] = pixman_int_to_fixed(y) + pixman_fixed_1 / 2 - 1;
++    v.vector[0] = pixman_int_to_fixed(x) + pixman_fixed_1 / 2;
++    v.vector[1] = pixman_int_to_fixed(y) + pixman_fixed_1 / 2;
+     v.vector[2] = pixman_fixed_1;
+ 
+     /* when using convolution filters one might get here without a transform */
+     if (pict->common.transform)
+     {
+         if (!pixman_transform_point_3d (pict->common.transform, &v))
+         {
+             fbFinishAccess (pict->pDrawable);
+             return;
+         }
+         unit.vector[0] = pict->common.transform->matrix[0][0];
+         unit.vector[1] = pict->common.transform->matrix[1][0];
--- a/gfx/public/nsIImage.h
+++ b/gfx/public/nsIImage.h
@@ -67,20 +67,20 @@ typedef enum {
     nsMaskRequirements_kNeeds8Bit
 } nsMaskRequirements;
 
 
 #define  nsImageUpdateFlags_kColorMapChanged 0x1
 #define  nsImageUpdateFlags_kBitsChanged     0x2
 
 // IID for the nsIImage interface
-// fd31e1f2-bd46-47f1-b8b6-b94ce954f9ce
+// 96d9d7ce-e575-4265-8507-35555112a430
 #define NS_IIMAGE_IID \
-{ 0xfd31e1f2, 0xbd46, 0x47f1, \
-  { 0xb8, 0xb6, 0xb9, 0x4c, 0xe9, 0x54, 0xf9, 0xce } }
+{ 0x96d9d7ce, 0xe575, 0x4265, \
+  { 0x85, 0x07, 0x35, 0x55, 0x51, 0x12, 0xa4, 0x30 } }
 
 // Interface to Images
 class nsIImage : public nsISupports
 {
 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IIMAGE_IID)
 
@@ -185,20 +185,24 @@ public:
    * @update - dwc 2/1/99
    * @return if non null, the colormap for the pixelmap,otherwise the image is not color mapped
    */
   virtual nsColorMap * GetColorMap() = 0;
 
   /**
    * BitBlit the nsIImage to a device, the source and dest can be scaled
    * @param aSourceRect  source rectangle, in image pixels
+   * @param aSubimageRect the subimage that we're extracting the contents from.
+   * It must contain aSourceRect. Pixels outside this rectangle must not
+   * be sampled.
    * @param aDestRect  destination rectangle, in device pixels
    */
   NS_IMETHOD Draw(nsIRenderingContext &aContext,
                   const gfxRect &aSourceRect,
+                  const gfxRect &aSubimageRect,
                   const gfxRect &aDestRect) = 0;
 
   /**
    * Get the alpha depth for the image mask
    * @update - lordpixel 2001/05/16
    * @return  the alpha mask depth for the image, ie, 0, 1 or 8
    */
   virtual PRInt8 GetAlphaDepth() = 0;
--- a/gfx/src/thebes/nsThebesImage.cpp
+++ b/gfx/src/thebes/nsThebesImage.cpp
@@ -407,16 +407,17 @@ nsThebesImage::UnlockImagePixels(PRBool 
 #endif
     return NS_OK;
 }
 
 /* NB: These are pixels, not twips. */
 NS_IMETHODIMP
 nsThebesImage::Draw(nsIRenderingContext &aContext,
                     const gfxRect &aSourceRect,
+                    const gfxRect &aSubimageRect,
                     const gfxRect &aDestRect)
 {
     if (NS_UNLIKELY(aDestRect.IsEmpty())) {
         NS_ERROR("nsThebesImage::Draw zero dest size - please fix caller.");
         return NS_OK;
     }
 
     nsThebesRenderingContext *thebesRC = static_cast<nsThebesRenderingContext*>(&aContext);
@@ -447,42 +448,77 @@ nsThebesImage::Draw(nsIRenderingContext 
         ctx->SetOperator(op);
         return NS_OK;
     }
 
     gfxFloat xscale = aDestRect.size.width / aSourceRect.size.width;
     gfxFloat yscale = aDestRect.size.height / aSourceRect.size.height;
 
     gfxRect srcRect(aSourceRect);
+    gfxRect subimageRect(aSubimageRect);
     gfxRect destRect(aDestRect);
 
     if (!GetIsImageComplete()) {
-      srcRect = srcRect.Intersect(gfxRect(mDecoded.x, mDecoded.y,
-                                          mDecoded.width, mDecoded.height));
+        gfxRect decoded = gfxRect(mDecoded.x, mDecoded.y,
+                                  mDecoded.width, mDecoded.height);
+        srcRect = srcRect.Intersect(decoded);
+        subimageRect = subimageRect.Intersect(decoded);
 
-      // This happens when mDecoded.width or height is zero. bug 368427.
-      if (NS_UNLIKELY(srcRect.size.width == 0 || srcRect.size.height == 0))
-          return NS_OK;
+        // This happens when mDecoded.width or height is zero. bug 368427.
+        if (NS_UNLIKELY(srcRect.size.width == 0 || srcRect.size.height == 0))
+            return NS_OK;
 
-      destRect.pos.x += (srcRect.pos.x - aSourceRect.pos.x)*xscale;
-      destRect.pos.y += (srcRect.pos.y - aSourceRect.pos.y)*yscale;
+        destRect.pos.x += (srcRect.pos.x - aSourceRect.pos.x)*xscale;
+        destRect.pos.y += (srcRect.pos.y - aSourceRect.pos.y)*yscale;
 
-      destRect.size.width  = srcRect.size.width * xscale;
-      destRect.size.height = srcRect.size.height * yscale;
+        destRect.size.width  = srcRect.size.width * xscale;
+        destRect.size.height = srcRect.size.height * yscale;
     }
 
     // if either rectangle is empty now (possibly after the image complete check)
     if (srcRect.IsEmpty() || destRect.IsEmpty())
         return NS_OK;
 
     // Reject over-wide or over-tall images.
     if (!AllowedImageSize(destRect.size.width + 1, destRect.size.height + 1))
         return NS_ERROR_FAILURE;
 
+    // Expand the subimageRect to place its edges on integer coordinates.
+    // Basically, if we're allowed to sample part of a pixel we can
+    // sample the whole pixel.
+    subimageRect.RoundOut();
+
     nsRefPtr<gfxPattern> pat;
+    PRBool ctxHasNonTranslation = ctx->CurrentMatrix().HasNonTranslation();
+    if ((xscale == 1.0 && yscale == 1.0 && !ctxHasNonTranslation) ||
+        subimageRect == gfxRect(0, 0, mWidth, mHeight))
+    {
+        // No need to worry about sampling outside the subimage rectangle,
+        // so no need for a temporary
+        // XXX should we also check for situations where the source rect
+        // is well inside the subimage so we can't sample outside?
+        pat = new gfxPattern(ThebesSurface());
+    } else {
+        // Because of the RoundOut above, the subimageRect has
+        // integer width and height.
+        gfxIntSize size(PRInt32(subimageRect.Width()),
+                        PRInt32(subimageRect.Height()));
+        nsRefPtr<gfxASurface> temp =
+            gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, mFormat);
+        if (!temp || temp->CairoStatus() != 0)
+            return NS_ERROR_FAILURE;
+
+        gfxContext tempctx(temp);
+        tempctx.SetSource(ThebesSurface(), -subimageRect.pos);
+        tempctx.SetOperator(gfxContext::OPERATOR_SOURCE);
+        tempctx.Paint();
+
+        pat = new gfxPattern(temp);
+        srcRect.MoveBy(-subimageRect.pos);
+    }
 
     /* See bug 364968 to understand the necessity of this goop; we basically
      * have to pre-downscale any image that would fall outside of a scaled 16-bit
      * coordinate space.
      */
     if (aDestRect.pos.x * (1.0 / xscale) >= 32768.0 ||
         aDestRect.pos.y * (1.0 / yscale) >= 32768.0)
     {
@@ -495,73 +531,79 @@ nsThebesImage::Draw(nsIRenderingContext 
 
         nsRefPtr<gfxASurface> temp =
             gfxPlatform::GetPlatform()->CreateOffscreenSurface (dim,  mFormat);
         if (!temp || temp->CairoStatus() != 0)
             return NS_ERROR_FAILURE;
 
         gfxContext tempctx(temp);
 
-        gfxPattern srcpat(ThebesSurface());
         gfxMatrix mat;
         mat.Translate(srcRect.pos);
         mat.Scale(1.0 / xscale, 1.0 / yscale);
-        srcpat.SetMatrix(mat);
+        pat->SetMatrix(mat);
 
-        tempctx.SetPattern(&srcpat);
+        tempctx.SetPattern(pat);
         tempctx.SetOperator(gfxContext::OPERATOR_SOURCE);
         tempctx.NewPath();
         tempctx.Rectangle(gfxRect(0.0, 0.0, dim.width, dim.height));
         tempctx.Fill();
 
         pat = new gfxPattern(temp);
 
         srcRect.pos.x = 0.0;
         srcRect.pos.y = 0.0;
         srcRect.size.width = dim.width;
         srcRect.size.height = dim.height;
 
         xscale = 1.0;
         yscale = 1.0;
     }
 
-    if (!pat) {
-        pat = new gfxPattern(ThebesSurface());
-    }
-
     gfxMatrix mat;
     mat.Translate(srcRect.pos);
     mat.Scale(1.0/xscale, 1.0/yscale);
 
     /* Translate the start point of the image (srcRect.pos)
      * to coincide with the destination rectangle origin
      */
     mat.Translate(-destRect.pos);
 
     pat->SetMatrix(mat);
 
-#if !defined(XP_MACOSX) && !defined(XP_WIN) && !defined(XP_OS2)
-    // See bug 324698.  This is a workaround.
-    //
-    // Set the filter to CAIRO_FILTER_FAST if we're scaling up -- 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
-    //
-    // This effectively disables smooth upscaling for images.
-    if (xscale > 1.0 || yscale > 1.0)
-        pat->SetFilter(0);
-#endif
+    nsRefPtr<gfxASurface> target = ctx->CurrentSurface();
+    switch (target->GetType()) {
+    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 if we're scaling up -- 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
+        //
+        // This effectively disables smooth upscaling for images.
+        if (xscale > 1.0 || yscale > 1.0 || ctxHasNonTranslation)
+            pat->SetFilter(0);
+        break;
 
-#if defined(XP_WIN) || defined(XP_OS2)
-    // turn on EXTEND_PAD only for win32, and only when scaling;
-    // it's not implemented correctly on linux in the X server.
-    if (xscale != 1.0 || yscale != 1.0)
-        pat->SetExtend(gfxPattern::EXTEND_PAD);
-#endif
+    case gfxASurface::SurfaceTypeQuartz:
+    case gfxASurface::SurfaceTypeQuartzImage:
+        // Do nothing, Mac seems to be OK. Really?
+        break;
+
+    default:
+        // turn on EXTEND_PAD.
+        // This is what we really want for all surface types, if the
+        // implementation was universally good.
+        if (xscale != 1.0 || yscale != 1.0 || ctxHasNonTranslation)
+            pat->SetExtend(gfxPattern::EXTEND_PAD);
+        break;
+    }
 
     gfxContext::GraphicsOperator op = ctx->CurrentOperator();
     if (op == gfxContext::OPERATOR_OVER && mFormat == gfxASurface::ImageFormatRGB24)
         ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
 
     ctx->NewPath();
     ctx->SetPattern(pat);
     ctx->Rectangle(destRect);
--- a/gfx/src/thebes/nsThebesImage.h
+++ b/gfx/src/thebes/nsThebesImage.h
@@ -72,16 +72,17 @@ public:
     virtual PRInt32 GetAlphaLineStride();
     virtual PRBool GetIsImageComplete();
     virtual void ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect);
     virtual nsresult Optimize(nsIDeviceContext* aContext);
     virtual nsColorMap *GetColorMap();
 
     NS_IMETHOD Draw(nsIRenderingContext &aContext,
                     const gfxRect &aSourceRect,
+                    const gfxRect &aSubimageRect,
                     const gfxRect &aDestRect);
 
     nsresult ThebesDrawTile(gfxContext *thebesContext,
                             nsIDeviceContext* dx,
                             const gfxPoint& aOffset,
                             const gfxRect& aTileRect,
                             const PRInt32 aXPadding,
                             const PRInt32 aYPadding);
--- a/gfx/thebes/public/gfxRect.h
+++ b/gfx/thebes/public/gfxRect.h
@@ -110,26 +110,32 @@ struct THEBES_API gfxRect {
         size.width = PR_MAX(0.0, size.width + (right+left));
         size.height = PR_MAX(0.0, size.height + (bottom+top));
     }
 
     void Outset(const gfxFloat *sides) {
         Outset(sides[0], sides[1], sides[2], sides[3]);
     }
 
-    // Round the rectangle to integer coordinates.
+    // Round the rectangle edges to integer coordinates, such that the rounded
+    // rectangle has the same set of pixel centers as the original rectangle.
+    // Edges at offset 0.5 round up.
     // Suitable for most places where integral device coordinates
     // are needed, but note that any translation should be applied first to
     // avoid pixel rounding errors.
     // Note that this is *not* rounding to nearest integer if the values are negative.
     // They are always rounding as floor(n + 0.5).
     // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
     // If you need similar method which is using NS_round(), you should create
     // new |RoundAwayFromZero()| method.
     void Round();
+    
+    // Snap the rectangle edges to integer coordinates, such that the
+    // resulting rectangle contains the original rectangle.
+    void RoundOut();
 
     // grabbing specific points
     gfxPoint TopLeft() const { return gfxPoint(pos); }
     gfxPoint TopRight() const { return pos + gfxSize(size.width, 0.0); }
     gfxPoint BottomLeft() const { return pos + gfxSize(0.0, size.height); }
     gfxPoint BottomRight() const { return pos + size; }
 
     /* Conditions this border to Cairo's max coordinate space.
--- a/gfx/thebes/src/gfxRect.cpp
+++ b/gfx/thebes/src/gfxRect.cpp
@@ -84,16 +84,31 @@ gfxRect::Round()
 
     pos.x = x0;
     pos.y = y0;
 
     size.width = x1 - x0;
     size.height = y1 - y0;
 }
 
+void
+gfxRect::RoundOut()
+{
+    gfxFloat x0 = NS_floor(X());
+    gfxFloat y0 = NS_floor(Y());
+    gfxFloat x1 = NS_ceil(XMost());
+    gfxFloat y1 = NS_ceil(YMost());
+
+    pos.x = x0;
+    pos.y = y0;
+
+    size.width = x1 - x0;
+    size.height = y1 - y0;
+}
+
 /* Clamp r to CAIRO_COORD_MIN .. CAIRO_COORD_MAX
  * these are to be device coordinates.
  */
 
 #define CAIRO_COORD_MAX (16382.0)
 #define CAIRO_COORD_MIN (-16383.0)
 
 void
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -3902,17 +3902,24 @@ nsCSSRendering::PaintBackgroundWithSC(ns
                  "Bogus x coord for draw rect");
     NS_ASSERTION(drawRect.y < absTileRect.y + tileHeight,
                  "Bogus y coord for draw rect");
     // Figure out whether we can get away with not tiling at all.
     nsRect sourceRect = drawRect - absTileRect.TopLeft();
     if (sourceRect.XMost() <= tileWidth && sourceRect.YMost() <= tileHeight) {
       // The entire drawRect is contained inside a single tile; just
       // draw the corresponding part of the image once.
-      nsLayoutUtils::DrawImage(&aRenderingContext, image, absTileRect, drawRect);
+      // Pass in the subimage rectangle that we expect to be sampled.
+      // This is the tile rectangle, clipped to the bgClipArea, and then
+      // passed in relative to the image top-left.
+      nsRect destRect; // The rectangle we would draw ignoring dirty-rect
+      destRect.IntersectRect(absTileRect, bgClipArea);
+      nsRect subimageRect = destRect - aBorderArea.TopLeft() - tileRect.TopLeft();
+      nsLayoutUtils::DrawImage(&aRenderingContext, image,
+              destRect, drawRect, &subimageRect);
     } else {
       aRenderingContext.DrawTile(image, absTileRect.x, absTileRect.y, &drawRect);
     }
   }
 
   ctx->Restore();
 
 }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2309,16 +2309,17 @@ nsLayoutUtils::DrawImage(nsIRenderingCon
   } else {
     pxSrc.pos.x = pxSrc.pos.y = 0.0;
     PRInt32 w = 0, h = 0;
     aImage->GetWidth(&w);
     aImage->GetHeight(&h);
     pxSrc.size.width = gfxFloat(w);
     pxSrc.size.height = gfxFloat(h);
   }
+  gfxRect pxSubimage = pxSrc;
 
   nsCOMPtr<nsIDeviceContext> dc;
   aRenderingContext->GetDeviceContext(*getter_AddRefs(dc));
 
   nsRefPtr<gfxContext> ctx = aRenderingContext->ThebesContext();
 
   // the dest rect is affected by the current transform; that'll be
   // handled by Image::Draw(), when we actually set up the rectangle.
@@ -2370,17 +2371,19 @@ nsLayoutUtils::DrawImage(nsIRenderingCon
   }
 
   // For Bug 87819
   // imgFrame may want image to start at different position, so adjust
   nsIntRect pxImgFrameRect;
   imgFrame->GetRect(pxImgFrameRect);
 
   if (pxImgFrameRect.x > 0) {
-    pxSrc.pos.x -= gfxFloat(pxImgFrameRect.x);
+    gfxFloat fx(pxImgFrameRect.x);
+    pxSubimage.pos.x -= fx;
+    pxSrc.pos.x -= fx;
 
     gfxFloat scaled_x = pxSrc.pos.x;
     if (pxDirty.size.width != pxSrc.size.width) {
       scaled_x = scaled_x * (pxDirty.size.width / pxSrc.size.width);
     }
 
     if (pxSrc.pos.x < 0.0) {
       pxDirty.pos.x -= scaled_x;
@@ -2391,17 +2394,19 @@ nsLayoutUtils::DrawImage(nsIRenderingCon
       pxSrc.pos.x = 0.0;
     }
   }
   if (pxSrc.pos.x > gfxFloat(pxImgFrameRect.width)) {
     return NS_OK;
   }
 
   if (pxImgFrameRect.y > 0) {
-    pxSrc.pos.y -= gfxFloat(pxImgFrameRect.y);
+    gfxFloat fy(pxImgFrameRect.y);
+    pxSubimage.pos.y -= fy;
+    pxSrc.pos.y -= fy;
 
     gfxFloat scaled_y = pxSrc.pos.y;
     if (pxDirty.size.height != pxSrc.size.height) {
       scaled_y = scaled_y * (pxDirty.size.height / pxSrc.size.height);
     }
 
     if (pxSrc.pos.y < 0.0) {
       pxDirty.pos.y -= scaled_y;
@@ -2411,17 +2416,17 @@ nsLayoutUtils::DrawImage(nsIRenderingCon
         return NS_OK;
       pxSrc.pos.y = 0.0;
     }
   }
   if (pxSrc.pos.y > gfxFloat(pxImgFrameRect.height)) {
     return NS_OK;
   }
 
-  return img->Draw(*aRenderingContext, pxSrc, pxDirty);
+  return img->Draw(*aRenderingContext, pxSrc, pxSubimage, pxDirty);
 }
 
 void
 nsLayoutUtils::SetFontFromStyle(nsIRenderingContext* aRC, nsStyleContext* aSC) 
 {
   const nsStyleFont* font = aSC->GetStyleFont();
   const nsStyleVisibility* visibility = aSC->GetStyleVisibility();
 
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -710,17 +710,18 @@ public:
    * Draw a single image.
    *   @param aRenderingContext Where to draw the image, set up with an
    *                            appropriate scale and transform for drawing in
    *                            app units (aDestRect).
    *   @param aImage            The image.
    *   @param aDestRect         Where to draw the image (app units).
    *   @param aDirtyRect        Draw only within this region (rounded to the
    *                            nearest pixel); the intersection of
-   *                            invalidation and clipping.
+   *                            invalidation and clipping (this is the
+   *                            destination clip)
    *   @param aSourceRect       If null, draw the entire image so it fits in
    *                            aDestRect.  If non-null, the subregion of the
    *                            image that should be drawn (in app units, such
    *                            that converting it to CSS pixels yields image
    *                            pixels).
    */
   static nsresult DrawImage(nsIRenderingContext* aRenderingContext,
                             imgIContainer* aImage,
--- a/layout/reftests/bugs/412093-1-ref.html
+++ b/layout/reftests/bugs/412093-1-ref.html
@@ -1,9 +1,8 @@
-<!DOCTYPE html>
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>Bidi text in inline with background image</title>
   <style type="text/css">
 body {
   font-size: 0;
 }
--- a/layout/reftests/bugs/412093-1.html
+++ b/layout/reftests/bugs/412093-1.html
@@ -1,9 +1,8 @@
-<!DOCTYPE html>
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>Bidi text in inline with background image</title>
   <style type="text/css">
 body {
   font-size: 0;
 }
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -415,17 +415,17 @@ skip-if(MOZ_WIDGET_TOOLKIT=="windows") =
 == 367612-1a.html 367612-1-ref.html
 == 367612-1b.html 367612-1-ref.html
 == 367612-1c.html 367612-1-ref.html
 == 367612-1d.html 367612-1-ref.html
 == 367612-1e.html 367612-1-ref.html
 == 367612-1f.html 367612-1-ref.html
 != 367612-1g.html 367612-1-ref.html
 == 368020-1.html 368020-1-ref.html
-random-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 368020-2.html 368020-2-ref.html # bug 368157 (gtk)
+random == 368020-2.html 368020-2-ref.html # bug 368157 (gtk), bug 403181
 fails == 368020-3.html 368020-3-ref.html # bug 368085
 fails == 368020-4.html 368020-4-ref.html # bug 368085
 random-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 368020-5.html 368020-5-ref.html # bug 388591
 == 368155-1.xhtml 368155-1-ref.xhtml
 == 368155-negative-margins-1.html 368155-negative-margins-1-ref.html
 # we can't test this because there's antialiasing involved, and our comparison
 # is too exact
 # == 368247-1.html 368247-1-ref.html
@@ -642,16 +642,17 @@ skip-if(MOZ_WIDGET_TOOLKIT!="windows") =
 == 402629-3.html 402629-3-ref.html
 #== 402807-1.html 402807-1-ref.html # Fails on Mac (qm-xserve01)
 == 402950-1.html 402950-1-ref.html
 == 403129-1.html 403129-1-ref.html
 == 403129-2.html 403129-2-ref.html
 == 403129-3.html 403129-3-ref.html
 == 403129-4.html 403129-4-ref.html
 random == 403134-1.html 403134-1-ref.html # bug 405377
+== 403181-1.xml 403181-1-ref.xml
 == 403249-1a.html 403249-1-ref.html
 == 403249-1b.html 403249-1-ref.html
 == 403249-2a.html 403249-2-ref.html
 == 403249-2b.html 403249-2-ref.html
 == 403328-1.html 403328-1-ref.html
 == 403426-1.html 403426-1-ref.html
 == 403455-1.html 403455-1-ref.html
 == 403505-1.xml 403505-1-ref.xul
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -561,17 +561,17 @@ nsBaseDragService::DrawDragForImage(nsPr
   // clear the image before drawing
   gfxContext context(surface);
   context.SetOperator(gfxContext::OPERATOR_CLEAR);
   context.Rectangle(gfxRect(0, 0, destRect.width, destRect.height));
   context.Fill();
 
   gfxRect inRect = gfxRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height);
   gfxRect outRect = gfxRect(destRect.x, destRect.y, destRect.width, destRect.height);
-  return img->Draw(*rc, inRect, outRect);
+  return img->Draw(*rc, inRect, inRect, outRect);
 }
 
 void
 nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
                                               PRInt32* aScreenX, PRInt32* aScreenY)
 {
   PRInt32 adj = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel();
   *aScreenX = nsPresContext::CSSPixelsToAppUnits(*aScreenX) / adj;