Bug 455364. Make border-image drawing use nsLayoutUtils::DrawImage and friends so we get the right pixel-snapping behaviour. r+sr=roc
☠☠ backed out by 37b331bbc983 ☠ ☠
authorZack Weinberg <zweinberg@mozilla.com>
Sat, 07 Feb 2009 21:35:54 +1300
changeset 24723 a142df653655b96f2c65088068b0ac7299184213
parent 24722 b1358e48e6ca47aa16aa845ee274f7d9c19a066f
child 24724 9889b0189cd4f6be9b18b1432e25877302933315
child 24727 37b331bbc983793d308f232a4e7624c07d6275eb
push idunknown
push userunknown
push dateunknown
bugs455364
milestone1.9.2a1pre
Bug 455364. Make border-image drawing use nsLayoutUtils::DrawImage and friends so we get the right pixel-snapping behaviour. r+sr=roc
layout/base/nsCSSRendering.cpp
layout/reftests/border-image/center-scaling-1-ref.png
layout/reftests/border-image/center-scaling-1.html
layout/reftests/border-image/center-scaling-2-ref.png
layout/reftests/border-image/center-scaling-2.html
layout/reftests/border-image/center-scaling-3-ref.png
layout/reftests/border-image/center-scaling-3.html
layout/reftests/border-image/diamonds.png
layout/reftests/border-image/different-h-v-1.html
layout/reftests/border-image/different-h-v-2.html
layout/reftests/border-image/different-h-v-ref.html
layout/reftests/border-image/reftest.list
layout/reftests/border-image/reticule.png
layout/reftests/border-image/solid-image-2a.html
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -261,26 +261,27 @@ protected:
   }
 };
 
 /* Local functions */
 static void DrawBorderImage(nsPresContext* aPresContext,
                             nsIRenderingContext& aRenderingContext,
                             nsIFrame* aForFrame,
                             const nsRect& aBorderArea,
-                            const nsStyleBorder& aBorderStyle);
+                            const nsStyleBorder& aBorderStyle,
+                            const nsRect& aDirtyRect);
 
-static void DrawBorderImageSide(gfxContext *aThebesContext,
-                                nsIDeviceContext* aDeviceContext,
-                                imgIContainer* aImage,
-                                gfxRect& aDestRect,
-                                gfxSize aInterSize,
-                                gfxRect& aSourceRect,
-                                PRUint8 aHFillType,
-                                PRUint8 aVFillType);
+static void DrawBorderImageComponent(nsIRenderingContext& aRenderingContext,
+                                     nsIImage* aImage,
+                                     const nsRect& aDirtyRect,
+                                     const nsRect& aFill,
+                                     const nsIntRect& aSrc,
+                                     PRUint8 aHFill,
+                                     PRUint8 aVFill,
+                                     const nsSize& aUnitSize);
 
 static nscolor MakeBevelColor(PRIntn whichSide, PRUint8 style,
                               nscolor aBackgroundColor,
                               nscolor aBorderColor);
 
 static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt,
                                              const gfxSize& aLineSize,
                                              const gfxFloat aAscent,
@@ -473,17 +474,17 @@ nsCSSRendering::PaintBorder(nsPresContex
   if (displayData->mAppearance) {
     nsITheme *theme = aPresContext->GetTheme();
     if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame, displayData->mAppearance))
       return; // Let the theme handle it.
   }
 
   if (aBorderStyle.IsBorderImageLoaded()) {
     DrawBorderImage(aPresContext, aRenderingContext, aForFrame,
-                    aBorderArea, aBorderStyle);
+                    aBorderArea, aBorderStyle, aDirtyRect);
     return;
   }
   
   // Get our style context's color struct.
   const nsStyleColor* ourColor = aStyleContext->GetStyleColor();
 
   // in NavQuirks mode we want to use the parent's context as a starting point
   // for determining the background color
@@ -1599,404 +1600,277 @@ nsCSSRendering::PaintBackgroundWithSC(ns
   }
   fillArea.IntersectRect(fillArea, bgClipArea);
 
   nsLayoutUtils::DrawImage(&aRenderingContext, image,
       destArea, fillArea, anchor + aBorderArea.TopLeft(), dirtyRect);
 }
 
 static void
-DrawBorderImage(nsPresContext* aPresContext,
+DrawBorderImage(nsPresContext*       aPresContext,
                 nsIRenderingContext& aRenderingContext,
-                nsIFrame* aForFrame, const nsRect& aBorderArea,
-                const nsStyleBorder& aBorderStyle)
+                nsIFrame*            aForFrame,
+                const nsRect&        aBorderArea,
+                const nsStyleBorder& aBorderStyle,
+                const nsRect&        aDirtyRect)
 {
-    float percent;
-    nsStyleCoord borderImageSplit[4];
-    PRInt32 borderImageSplitInt[4];
-    nsMargin border;
-    gfxFloat borderTop, borderRight, borderBottom, borderLeft;
-    gfxFloat borderImageSplitGfx[4];
+  if (aDirtyRect.IsEmpty())
+    return;
 
-    border = aBorderStyle.GetActualBorder();
-    if ((0 == border.left) && (0 == border.right) &&
-        (0 == border.top) && (0 == border.bottom)) {
-      // Empty border area
-      return;
-    }
-
-    borderImageSplit[NS_SIDE_TOP] = aBorderStyle.mBorderImageSplit.GetTop();
-    borderImageSplit[NS_SIDE_RIGHT] = aBorderStyle.mBorderImageSplit.GetRight();
-    borderImageSplit[NS_SIDE_BOTTOM] = aBorderStyle.mBorderImageSplit.GetBottom();
-    borderImageSplit[NS_SIDE_LEFT] = aBorderStyle.mBorderImageSplit.GetLeft();
-
-    imgIRequest *req = aPresContext->LoadBorderImage(aBorderStyle.GetBorderImage(), aForFrame);
-
-    nsCOMPtr<imgIContainer> image;
-    req->GetImage(getter_AddRefs(image));
+  // Clone the image loader and set up animation notifications.
+  imgIRequest *req =
+    aPresContext->LoadBorderImage(aBorderStyle.GetBorderImage(), aForFrame);
+#ifdef DEBUG
+  {
+    PRUint32 status = imgIRequest::STATUS_ERROR;
+    if (req)
+      req->GetImageStatus(&status);
 
-    nsSize imageSize;
-    image->GetWidth(&imageSize.width);
-    image->GetHeight(&imageSize.height);
-    imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageSize.width);
-    imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageSize.height);
+    NS_ASSERTION(req && (status & imgIRequest::STATUS_FRAME_COMPLETE),
+                 "no image to draw");
+  }
+#endif
+
+  // Get the actual image, and determine where the split points are.
+  // Note that mBorderImageSplit is in image pixels, not necessarily
+  // CSS pixels.
 
-    // convert percentage values
-    NS_FOR_CSS_SIDES(side) {
-      borderImageSplitInt[side] = 0;
-      switch (borderImageSplit[side].GetUnit()) {
-        case eStyleUnit_Percent:
-          percent = borderImageSplit[side].GetPercentValue();
-          if (side == NS_SIDE_TOP || side == NS_SIDE_BOTTOM)
-            borderImageSplitInt[side] = (nscoord)(percent * imageSize.height);
-          else
-            borderImageSplitInt[side] = (nscoord)(percent * imageSize.width);
-          break;
-        case eStyleUnit_Integer:
-          borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side].
-                                          GetIntValue());
-          break;
-        case eStyleUnit_Factor:
-          borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side].GetFactorValue());
-          break;
-        default:
-          break;
-      }
-    }
+  nsCOMPtr<imgIContainer> imgContainer;
+  req->GetImage(getter_AddRefs(imgContainer));
 
-    gfxContext *thebesCtx = aRenderingContext.ThebesContext();
-    nsCOMPtr<nsIDeviceContext> dc;
-    aRenderingContext.GetDeviceContext(*getter_AddRefs(dc));
+  nsIntSize imageSize;
+  imgContainer->GetWidth(&imageSize.width);
+  imgContainer->GetHeight(&imageSize.height);
+
+  nsCOMPtr<gfxIImageFrame> imgFrame;
+  imgContainer->GetCurrentFrame(getter_AddRefs(imgFrame));
+
+  nsIntRect innerRect;
+  imgFrame->GetRect(innerRect);
 
-    NS_FOR_CSS_SIDES(side) {
-      borderImageSplitGfx[side] = nsPresContext::AppUnitsToFloatCSSPixels(borderImageSplitInt[side]);
-    }
-
-    borderTop = dc->AppUnitsToGfxUnits(border.top);
-    borderRight = dc->AppUnitsToGfxUnits(border.right);
-    borderBottom = dc->AppUnitsToGfxUnits(border.bottom);
-    borderLeft = dc->AppUnitsToGfxUnits(border.left);
-
-    gfxSize gfxImageSize;
-    gfxImageSize.width = nsPresContext::AppUnitsToFloatCSSPixels(imageSize.width);
-    gfxImageSize.height = nsPresContext::AppUnitsToFloatCSSPixels(imageSize.height);
-
-    nsRect outerRect(aBorderArea);
-    gfxRect rectToDraw,
-            rectToDrawSource;
-
-    gfxRect clipRect;
-    clipRect.pos.x = dc->AppUnitsToGfxUnits(outerRect.x);
-    clipRect.pos.y = dc->AppUnitsToGfxUnits(outerRect.y);
-    clipRect.size.width = dc->AppUnitsToGfxUnits(outerRect.width);
-    clipRect.size.height = dc->AppUnitsToGfxUnits(outerRect.height);
-    if (thebesCtx->UserToDevicePixelSnapped(clipRect))
-      clipRect = thebesCtx->DeviceToUser(clipRect);
-
-    thebesCtx->Save();
-    thebesCtx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
-
-    gfxSize middleSize(clipRect.size.width - (borderLeft + borderRight),
-                       clipRect.size.height - (borderTop + borderBottom));
+  nsCOMPtr<nsIImage> img(do_GetInterface(imgFrame));
+  // The inner rectangle should precisely enclose the image pixels for
+  // this frame.
+  NS_ASSERTION(innerRect.width == img->GetWidth(), "img inner width off");
+  NS_ASSERTION(innerRect.height == img->GetHeight(), "img inner height off");
 
-    // middle size in source space
-    gfxIntSize middleSizeSource(gfxImageSize.width - (borderImageSplitGfx[NS_SIDE_RIGHT] + borderImageSplitGfx[NS_SIDE_LEFT]), 
-                                gfxImageSize.height - (borderImageSplitGfx[NS_SIDE_TOP] + borderImageSplitGfx[NS_SIDE_BOTTOM]));
+  // Convert percentages and clamp values to the image size.
+  nsIntMargin split;
+  NS_FOR_CSS_SIDES(s) {
+    nsStyleCoord coord = aBorderStyle.mBorderImageSplit.Get(s);
+    PRInt32 imgDimension = ((s == NS_SIDE_TOP || s == NS_SIDE_BOTTOM)
+                            ? imageSize.height
+                            : imageSize.width);
+    double value;
+    switch (coord.GetUnit()) {
+      case eStyleUnit_Percent:
+        value = coord.GetPercentValue() * imgDimension;
+        break;
+      case eStyleUnit_Factor:
+        value = coord.GetFactorValue();
+        break;
+      default:
+        NS_ASSERTION(coord.GetUnit() == eStyleUnit_Null,
+                     "unexpected CSS unit for image split");
+        value = 0;
+        break;
+    }
+    if (value < 0)
+      value = 0;
+    if (value > imgDimension)
+      value = imgDimension;
+    split.side(s) = NS_lround(value);
+  }
 
-    gfxSize interSizeTop, interSizeBottom, interSizeLeft, interSizeRight,
-            interSizeMiddle;
-    gfxFloat topScale = borderTop/borderImageSplitGfx[NS_SIDE_TOP];
-    gfxFloat bottomScale = borderBottom/borderImageSplitGfx[NS_SIDE_BOTTOM];
-    gfxFloat leftScale = borderLeft/borderImageSplitGfx[NS_SIDE_LEFT];
-    gfxFloat rightScale = borderRight/borderImageSplitGfx[NS_SIDE_RIGHT];
-    gfxFloat middleScaleH,
-             middleScaleV;
-    // TODO: check for nan and properly check for inf
-    if (topScale != 0.0 && borderImageSplitGfx[NS_SIDE_TOP] != 0.0) {
-      middleScaleH = topScale;
-    } else if (bottomScale != 0.0 && borderImageSplitGfx[NS_SIDE_BOTTOM] != 0.0) {
-      middleScaleH = bottomScale;
-    } else {
-      middleScaleH = 1.0;
-    }
+  nsMargin border(aBorderStyle.GetActualBorder());
 
-    if (leftScale != 0.0 && borderImageSplitGfx[NS_SIDE_LEFT] != 0.0) {
-      middleScaleV = leftScale;
-    } else if (rightScale != 0.0 && borderImageSplitGfx[NS_SIDE_RIGHT] != 0.0) {
-      middleScaleV = rightScale;
-    } else {
-      middleScaleV = 1.0;
-    }
-
-    interSizeTop.height = borderTop;
-    interSizeTop.width = middleSizeSource.width*topScale;
-
-    interSizeBottom.height = borderBottom;
-    interSizeBottom.width = middleSizeSource.width*bottomScale;
+  // Compute the special scale factors for the middle component.
+  // Step 1 of the scaling algorithm in css3-background section 4.4,
+  // fourth bullet point:
+  //     The middle image's width is scaled by the same factor as
+  //     the top image unless that factor is zero or infinity, in
+  //     which case the scaling factor of the bottom is substituted,
+  //     and failing that, the width is not scaled. The height of
+  //     the middle image is scaled by the same factor as the left
+  //     image unless that factor is zero or infinity, in which case
+  //     the scaling factor of the right image is substituted, and
+  //     failing that, the height is not scaled.
+  gfxFloat hFactor, vFactor;
 
-    interSizeLeft.width = borderLeft;
-    interSizeLeft.height = middleSizeSource.height*leftScale;
+  if (0 < border.left && 0 < split.left)
+    vFactor = gfxFloat(border.left)/split.left;
+  else if (0 < border.right && 0 < split.right)
+    vFactor = gfxFloat(border.right)/split.right;
+  else
+    vFactor = 1.0;
 
-    interSizeRight.width = borderRight;
-    interSizeRight.height = middleSizeSource.height*rightScale;
-
-    interSizeMiddle.width = middleSizeSource.width*middleScaleH;
-    interSizeMiddle.height = middleSizeSource.height*middleScaleV;
+  if (0 < border.top && 0 < split.top)
+    hFactor = gfxFloat(border.top)/split.top;
+  else if (0 < border.bottom && 0 < split.bottom)
+    hFactor = gfxFloat(border.bottom)/split.bottom;
+  else
+    hFactor = 1.0;
 
-    // draw top left corner
-    rectToDraw = clipRect;
-    rectToDraw.size.width = borderLeft;
-    rectToDraw.size.height = borderTop;
-    rectToDrawSource.pos.x = 0;
-    rectToDrawSource.pos.y = 0;
-    rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT];
-    rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP];
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, rectToDraw.size, rectToDrawSource,
-                        NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
+  // These helper tables recharacterize the 'split' and 'border' margins
+  // in a more convenient form: they are the x/y/width/height coords
+  // required for various bands of the border, and they have been transformed
+  // to be relative to the innerRect (for 'split') or the page (for 'border').
+  enum {
+    LEFT, MIDDLE, RIGHT,
+    TOP = LEFT, BOTTOM = RIGHT
+  };
+  const nscoord borderX[3] = {
+    aBorderArea.x + 0,
+    aBorderArea.x + border.left,
+    aBorderArea.x + aBorderArea.width - border.right,
+  };
+  const nscoord borderY[3] = {
+    aBorderArea.y + 0,
+    aBorderArea.y + border.top,
+    aBorderArea.y + aBorderArea.height - border.bottom,
+  };
+  const nscoord borderWidth[3] = {
+    border.left,
+    aBorderArea.width - border.left - border.right,
+    border.right,
+  };
+  const nscoord borderHeight[3] = {
+    border.top,
+    aBorderArea.height - border.top - border.bottom,
+    border.bottom,
+  };
 
-    // draw top
-    rectToDraw = clipRect;
-    rectToDraw.pos.x += borderLeft;
-    rectToDraw.size.width = middleSize.width;
-    rectToDraw.size.height = borderTop;
-    rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT];
-    rectToDrawSource.pos.y = 0;
-    rectToDrawSource.size.width = middleSizeSource.width;
-    rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP];
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, interSizeTop, rectToDrawSource, 
-                        aBorderStyle.mBorderImageHFill, NS_STYLE_BORDER_IMAGE_STRETCH);
-    
-    // draw top right corner
-    rectToDraw = clipRect;
-    rectToDraw.pos.x += clipRect.size.width - borderRight;
-    rectToDraw.size.width = borderRight;
-    rectToDraw.size.height = borderTop;
-    rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT];
-    rectToDrawSource.pos.y = 0;
-    rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT];
-    rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP];
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, rectToDraw.size, rectToDrawSource,
-                        NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
-    
-    // draw right
-    rectToDraw = clipRect;
-    rectToDraw.pos.x += clipRect.size.width - borderRight;
-    rectToDraw.pos.y += borderTop;
-    rectToDraw.size.width = borderRight;
-    rectToDraw.size.height = middleSize.height;
-    rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT];
-    rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP];
-    rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT];
-    rectToDrawSource.size.height = middleSizeSource.height;
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, interSizeRight, rectToDrawSource, 
-                        NS_STYLE_BORDER_IMAGE_STRETCH, aBorderStyle.mBorderImageVFill);
-    
-    // draw bottom right corner
-    rectToDraw = clipRect;
-    rectToDraw.pos.x += clipRect.size.width - borderRight;
-    rectToDraw.pos.y += clipRect.size.height - borderBottom;
-    rectToDraw.size.width = borderRight;
-    rectToDraw.size.height = borderBottom;
-    rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT];
-    rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM];
-    rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT];
-    rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM];
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, rectToDraw.size, rectToDrawSource,
-                        NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
-    
-    // draw bottom
-    rectToDraw = clipRect;
-    rectToDraw.pos.x += borderLeft;
-    rectToDraw.pos.y += clipRect.size.height - borderBottom;
-    rectToDraw.size.width = middleSize.width;
-    rectToDraw.size.height = borderBottom;
-    rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT];
-    rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM];
-    rectToDrawSource.size.width = middleSizeSource.width;
-    rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM];
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, interSizeBottom, rectToDrawSource, 
-                        aBorderStyle.mBorderImageHFill, NS_STYLE_BORDER_IMAGE_STRETCH);
-    
-    // draw bottom left corner
-    rectToDraw = clipRect;
-    rectToDraw.pos.y += clipRect.size.height - borderBottom;
-    rectToDraw.size.width = borderLeft;
-    rectToDraw.size.height = borderBottom;
-    rectToDrawSource.pos.x = 0;
-    rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM];
-    rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT];
-    rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM];
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, rectToDraw.size, rectToDrawSource,
-                        NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
-    
-    // draw left
-    rectToDraw = clipRect;
-    rectToDraw.pos.y += borderTop;
-    rectToDraw.size.width = borderLeft;
-    rectToDraw.size.height = middleSize.height;
-    rectToDrawSource.pos.x = 0;
-    rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP];
-    rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT];
-    rectToDrawSource.size.height = middleSizeSource.height;
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, interSizeLeft, rectToDrawSource, 
-                        NS_STYLE_BORDER_IMAGE_STRETCH, aBorderStyle.mBorderImageVFill);
+  const PRInt32 splitX[3] = {
+    -innerRect.x + 0,
+    -innerRect.x + split.left,
+    -innerRect.x + imageSize.width - split.right,
+  };
+  const PRInt32 splitY[3] = {
+    -innerRect.y + 0,
+    -innerRect.y + split.top,
+    -innerRect.y + imageSize.height - split.bottom,
+  };
+  const PRInt32 splitWidth[3] = {
+    split.left,
+    imageSize.width - split.left - split.right,
+    split.right,
+  };
+  const PRInt32 splitHeight[3] = {
+    split.top,
+    imageSize.height - split.top - split.bottom,
+    split.bottom,
+  };
+
+  for (int i = LEFT; i <= RIGHT; i++) {
+    for (int j = TOP; j <= BOTTOM; j++) {
+      nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
+      nsIntRect subArea(splitX[i], splitY[j], splitWidth[i], splitHeight[j]);
 
-    // Draw middle
-    rectToDraw = clipRect;
-    rectToDraw.pos.x += borderLeft;
-    rectToDraw.pos.y += borderTop;
-    rectToDraw.size.width = middleSize.width;
-    rectToDraw.size.height = middleSize.height;
-    rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT];
-    rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP];
-    rectToDrawSource.size = middleSizeSource;
-    DrawBorderImageSide(thebesCtx, dc, image,
-                        rectToDraw, interSizeMiddle, rectToDrawSource,
-                        aBorderStyle.mBorderImageHFill, aBorderStyle.mBorderImageVFill);
+      // Corners are always stretched to fit the corner.
+      // Sides are always stretched to the thickness of their border.
+      PRUint8 fillStyleH = (i == 1
+                            ? aBorderStyle.mBorderImageHFill
+                            : NS_STYLE_BORDER_IMAGE_STRETCH);
+      PRUint8 fillStyleV = (j == 1
+                            ? aBorderStyle.mBorderImageVFill
+                            : NS_STYLE_BORDER_IMAGE_STRETCH);
 
-    thebesCtx->PopGroupToSource();
-    thebesCtx->SetOperator(gfxContext::OPERATOR_OVER);
-    thebesCtx->Paint();
-    thebesCtx->Restore();
+      // For everything except the middle, whichever axis is always
+      // stretched determines the size of the 'unit square' for tiling.
+      // The middle uses the special scale factors calculated above.
+      nsSize unitSize;
+      if (i == MIDDLE && j == MIDDLE) {
+        unitSize.width = splitWidth[i]*hFactor;
+        unitSize.height = splitHeight[j]*vFactor;
+      } else if (i == MIDDLE) {
+        unitSize.width = borderHeight[j];
+        unitSize.height = borderHeight[j];
+      } else if (j == MIDDLE) {
+        unitSize.width = borderWidth[i];
+        unitSize.height = borderWidth[i];
+      } else {
+        unitSize.width = borderWidth[i];
+        unitSize.height = borderHeight[j];
+      }
+
+      DrawBorderImageComponent(aRenderingContext, img, aDirtyRect,
+                               destArea, subArea,
+                               fillStyleH, fillStyleV, unitSize);
+    }
+  }
 }
 
 static void
-DrawBorderImageSide(gfxContext *aThebesContext,
-                    nsIDeviceContext* aDeviceContext,
-                    imgIContainer* aImage,
-                    gfxRect& aDestRect,
-                    gfxSize aInterSize, // non-ref to allow aliasing
-                    gfxRect& aSourceRect,
-                    PRUint8 aHFillType,
-                    PRUint8 aVFillType)
+DrawBorderImageComponent(nsIRenderingContext& aRenderingContext,
+                         nsIImage*            aImage,
+                         const nsRect&        aDirtyRect,
+                         const nsRect&        aFill,
+                         const nsIntRect&     aSrc,
+                         PRUint8              aHFill,
+                         PRUint8              aVFill,
+                         const nsSize&        aUnitSize)
 {
-  if (aDestRect.size.width < 1.0 || aDestRect.size.height < 1.0 ||
-      aSourceRect.size.width < 1.0 || aSourceRect.size.height < 1.0) {
+  if (aFill.IsEmpty() || aSrc.IsEmpty())
+    return;
+
+  nsCOMPtr<nsIImage> subImage;
+  if (NS_FAILED(aImage->Extract(aSrc, getter_AddRefs(subImage))))
+    return;
+
+  // If we have no tiling in either direction, we can skip the intermediate
+  // scaling step.
+  if (aHFill == NS_STYLE_BORDER_IMAGE_STRETCH &&
+      aVFill == NS_STYLE_BORDER_IMAGE_STRETCH) {
+    nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage,
+                                   aFill, aDirtyRect);
     return;
   }
 
-  gfxIntSize gfxSourceSize((PRInt32)aSourceRect.size.width,
-                           (PRInt32)aSourceRect.size.height);
-
-  // where the actual border ends up being rendered
-  if (aThebesContext->UserToDevicePixelSnapped(aDestRect))
-    aDestRect = aThebesContext->DeviceToUser(aDestRect);
-  if (aThebesContext->UserToDevicePixelSnapped(aSourceRect))
-    aSourceRect = aThebesContext->DeviceToUser(aSourceRect);
-
-  if (aDestRect.size.height < 1.0 ||
-     aDestRect.size.width < 1.0)
-    return;
-
-  if (aInterSize.width < 1.0 ||
-     aInterSize.height < 1.0)
-    return;
-
-  // Surface will hold just the part of the source image specified by the aSourceRect
-  // but at a different size
-  nsRefPtr<gfxASurface> interSurface =
-    gfxPlatform::GetPlatform()->CreateOffscreenSurface(
-        gfxSourceSize, gfxASurface::ImageFormatARGB32);
+  // Compute the scale and position of the master copy of the image.
+  nsRect tile;
+  switch (aHFill) {
+  case NS_STYLE_BORDER_IMAGE_STRETCH:
+    tile.x = aFill.x;
+    tile.width = aFill.width;
+    break;
+  case NS_STYLE_BORDER_IMAGE_REPEAT:
+    tile.x = aFill.x + aFill.width/2 - aUnitSize.width/2;
+    tile.width = aUnitSize.width;
+    break;
 
-  gfxMatrix srcMatrix;
-  // Adjust the matrix scale for Step 1 of the spec
-  srcMatrix.Scale(aSourceRect.size.width/aInterSize.width,
-                  aSourceRect.size.height/aInterSize.height);
-  {
-    nsCOMPtr<gfxIImageFrame> frame;
-    nsresult rv = aImage->GetCurrentFrame(getter_AddRefs(frame));
-    if(NS_FAILED(rv))
-      return;
-    nsCOMPtr<nsIImage> image;
-    image = do_GetInterface(frame);
-    if(!image)
-      return;
+  case NS_STYLE_BORDER_IMAGE_ROUND:
+    tile.x = aFill.x;
+    tile.width = aFill.width / ceil(gfxFloat(aFill.width)/aUnitSize.width);
+    break;
 
-    // surface for the whole image
-    nsRefPtr<gfxPattern> imagePattern;
-    rv = image->GetPattern(getter_AddRefs(imagePattern));
-    if(NS_FAILED(rv) || !imagePattern)
-      return;
-
-    gfxMatrix mat;
-    mat.Translate(aSourceRect.pos);
-    imagePattern->SetMatrix(mat);
-
-    // Straightforward blit - no resizing
-    nsRefPtr<gfxContext> srcCtx = new gfxContext(interSurface);
-    srcCtx->SetPattern(imagePattern);
-    srcCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
-    srcCtx->Paint();
-    srcCtx = nsnull;
-
+  default:
+    NS_NOTREACHED("unrecognized border-image fill style");
   }
 
-  // offset to make the middle tile centered in the middle of the border
-  gfxPoint renderOffset(0, 0);
-  gfxSize rectSize(aDestRect.size);
-
-  aThebesContext->Save();
-  aThebesContext->Clip(aDestRect);
-
-  gfxFloat hScale(1.0), vScale(1.0);
+  switch (aVFill) {
+  case NS_STYLE_BORDER_IMAGE_STRETCH:
+    tile.y = aFill.y;
+    tile.height = aFill.height;
+    break;
+  case NS_STYLE_BORDER_IMAGE_REPEAT:
+    tile.y = aFill.y + aFill.height/2 - aUnitSize.height/2;
+    tile.height = aUnitSize.height;
+    break;
 
-  nsRefPtr<gfxPattern> pattern = new gfxPattern(interSurface);
-  pattern->SetExtend(gfxPattern::EXTEND_PAD_EDGE);
-  switch (aHFillType) {
-    case NS_STYLE_BORDER_IMAGE_REPEAT:
-      renderOffset.x = (rectSize.width - aInterSize.width*NS_ceil(rectSize.width/aInterSize.width))*-0.5;
-      aDestRect.pos.x -= renderOffset.x;
-      pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
-      break;
-    case NS_STYLE_BORDER_IMAGE_ROUND:
-      hScale = aInterSize.width*(NS_ceil(aDestRect.size.width/aInterSize.width)/aDestRect.size.width);
-      pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
-      break;
-    case NS_STYLE_BORDER_IMAGE_STRETCH:
-    default:
-      hScale = aInterSize.width/aDestRect.size.width;
-      break;
+  case NS_STYLE_BORDER_IMAGE_ROUND:
+    tile.y = aFill.y;
+    tile.height = aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height);
+    break;
+
+  default:
+    NS_NOTREACHED("unrecognized border-image fill style");
   }
 
-  switch (aVFillType) {
-    case NS_STYLE_BORDER_IMAGE_REPEAT:
-      renderOffset.y = (rectSize.height - aInterSize.height*NS_ceil(rectSize.height/aInterSize.height))*-0.5;
-      aDestRect.pos.y -= renderOffset.y;
-      pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
-      break;
-    case NS_STYLE_BORDER_IMAGE_ROUND:
-      vScale = aInterSize.height*(NS_ceil(aDestRect.size.height/aInterSize.height)/aDestRect.size.height);
-      pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
-      break;
-    case NS_STYLE_BORDER_IMAGE_STRETCH:
-    default:
-      vScale = aInterSize.height/aDestRect.size.height;
-      break;
-  }
-
-  // Adjust the matrix scale for Step 2 of the spec
-  srcMatrix.Scale(hScale,vScale);
-  pattern->SetMatrix(srcMatrix);
-
-  // render
-  aThebesContext->Translate(aDestRect.pos);
-  aThebesContext->SetPattern(pattern);
-  aThebesContext->NewPath();
-  aThebesContext->Rectangle(gfxRect(renderOffset, rectSize));
-  aThebesContext->SetOperator(gfxContext::OPERATOR_ADD);
-  aThebesContext->Fill();
-  aThebesContext->Restore();
+  nsLayoutUtils::DrawImage(&aRenderingContext, subImage,
+                           tile, aFill, tile.TopLeft(), aDirtyRect);
 }
 
 // Begin table border-collapsing section
 // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
 // At some point, all functions should be unified to include the additional functionality that these provide
 
 static nscoord
 RoundIntToPixel(nscoord aValue, 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7276260655f82866cd4d23008c958fab41655a20
GIT binary patch
literal 282
zc%17D@N?(olHy`uVBq!ia0y~yV6+3Ww=*#V$#3&+u>&d20G|+728MJXv!S8kSIuk(
zAd9gi$S;_|;n|HeAZNd)i(^Q}y|=dyavpLJXnk0Jn9Fim%aln%Ha})<=rTzZUU{im
zslwPOVM6^n?)!Q^_vRRVSkua9GNXZy+u-6inZ45=Ogs?G)qQ+ws(wyWQ+)H<7pwQ}
zIj~5)>|jzsDhu0;M!~Rzv>U}L8X7+=XQ=GVV=eWbwAokCL{rT*ARyoZqqasy5DP0S
ztLz3d*9%K#h&TcP`<ev{JOxEWL`n|a5^<cI1XR$^{Hyu<!2_@MGE6sRspp@TU$iss
c`~L^BPm?_U*ClCK1HHoF>FVdQ&MBb@0PxIdB>(^b
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-image/center-scaling-1.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<style>
+body { margin: 5px }
+div {
+  border-width: 27px;
+  -moz-border-image: url("reticule.png") 27 round;
+  width: 270px;
+  height: 135px;
+}
+</style>
+<div></div>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b38c596c3de6374845029f8cea4bc5d01d168bfd
GIT binary patch
literal 288
zc%17D@N?(olHy`uVBq!ia0y~yV9W!uw=*#VN&l`Ge;~yf;1lA?z>p4PHZ(N+s+sKo
zWHFWm`2{mLJiCzw<Q(;MaSW-r_4c-5*5LpF*NfW8&EEQs-XDdoEIFCAXWkm4-h-US
zS{>~LANl-#XrOSU`s|hUJImHA+ZA$rSJ?Dr<@4hBYxsX0?}&W&!ziiPqNe#r=2`g{
zobiR9g=>s|tSv5GvD51M+F90nKC`qRe)ysOp#03gKewLxlXKCzP+o+u{cvmRhK9or
z3oK;#u8Wqs0x5B^Lrg$%pioTs6;Tis!NCm_Z~xEvZeE7@j}yChhTX9N*>r*Xom-Av
cP5lq^!#7?0rd=!w2YQIX)78&qol`;+0Eyjro&W#<
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-image/center-scaling-2.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<style>
+body { margin: 5px 2px }
+div {
+  border-width: 27px 54px;
+  -moz-border-image: url("reticule.png") 27 round;
+  width: 270px;
+  height: 135px;
+}
+</style>
+<div></div>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a8f5fbd5afdb2d90a9e90257c5406effd9f7290a
GIT binary patch
literal 285
zc%17D@N?(olHy`uVBq!ia0y~yV6+3WpEEH7$zKAOJ^(4s0G|+728MJXv!S8kSIuk(
zAd9gi$S;_|;n|HeAm@;$i(^Q|t+#g!xtarb*dB1~`cjggq5rUUSNS{5Y*{8&P2D4_
zzMP)m`a{9y-;N(WC$erXliF1K`Nh@-hX?ux9|lFNZm4~DLRNr-MNaBX*S6P{wi_~@
z$Z`B(|1d3gRq(d+@{ElR52Op~T>HN_?Xp|?ETCbMlBR=#K*m9)#z{;pOpRh|oEg2k
zmM}CrEMVnk@!|rS6t#hmRYMabYt9<I?SrJfzz&9Y{~m>k^uF0AC%|E2w&VYNX_tp5
d7=a3y<U{;ZpUHo+l>qvN!PC{xWt~$(69A>DXpR5?
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-image/center-scaling-3.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<style>
+body { margin: 2px 5px }
+div {
+  border-width: 54px 27px;
+  -moz-border-image: url("reticule.png") 27 round;
+  width: 270px;
+  height: 135px;
+}
+</style>
+<div></div>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7fe5da5e608699e970bf0b2602d3881aae7bc09d
GIT binary patch
literal 554
zc%17D@N?(olHy`uVBq!ia0vp^fgsGm3?%0U?qUH_2?0JKt_(21$jIO$&T!6_;hQJJ
ze;-uj%lO}q>3^Bh|Gm*rv@ZrHLe<+J`5^~rx>!k&UoglPrNq+4)~VY*{`+&jzM-*q
z%e_C}w!e%6N+f%_IEF+VemldO|BwMsOF!ofDXWUJ|Nl>)5_HQZN8NSK5rOr~-#^*&
z=EM$_!rtrMf|FUiwKP(Kddg1+q-^6U=GrOp_^gpgWkj1~t5eYC8tGM$*F?NDuT}pv
z4A~T-5fYRt`J^L3QcfgmWyBAa%rny-`Z-SAq|^LFQ0Fi|tFPC&yh%oBrwbM@@Y<c4
zyF<R;)p7bZ)r_{=hkUu(w&ZQe+o5-7S%T+jA)Uji5BXX%+HX7zXpyPk-`}!X?T-7Q
z*@}Czo!%;5ad+ZX|03z6ukt1P(ELM(=V)kr(LHN=MzbWh{;<K~3&pOp+B94HvhO6$
z2*|KERX5=}TXbP+qSM0AO;*M>8YZW*%G<12_kWphR1;iq`?BZZsZH0N{H6D4SmgfW
zKHS>$S+r0_wrj%U_unTLTx<F5QDNLt?z_Ua<(J2bhb?x_FIrpbe18P@M4p<l6pi?E
bUgs&N-b_EyiIa41f#TcK)z4*}Q$iB}xhVES
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-image/different-h-v-1.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html><head>
+<style>
+div { 
+  margin: 27px;
+  border-width: 1em;
+  -moz-border-image: url("diamonds.png") 27 stretch round;
+  width: 270px;
+  height: 135px;
+}
+</style>
+<body>
+<div></div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-image/different-h-v-2.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html><head>
+<style>
+div { 
+  margin: 27px;
+  border-width: 1em;
+  -moz-border-image: url("diamonds.png") 27 round stretch;
+  width: 270px;
+  height: 135px;
+}
+</style>
+<body>
+<div></div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-image/different-h-v-ref.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html><head>
+<style>
+div { 
+  margin: 27px;
+  border-width: 1em;
+  -moz-border-image: url("diamonds.png") 27 stretch;
+  width: 270px;
+  height: 135px;
+}
+</style>
+<body>
+<div></div>
+</body></html>
--- a/layout/reftests/border-image/reftest.list
+++ b/layout/reftests/border-image/reftest.list
@@ -1,9 +1,16 @@
 fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") == solid-image-1.html solid-image-1-ref.html # bug 445911
 == transparent-image-1.html transparent-image-1-ref.html
 fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") == solid-image-2.html solid-image-2-ref.html # bug 445911
+fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") == solid-image-2a.html solid-image-2-ref.html # bug 445911
 == multicolor-image-1.html multicolor-image-1-ref.html
 == multicolor-image-2.html multicolor-image-2-ref.html
 == multicolor-image-3.html multicolor-image-3-ref.html
 == multicolor-image-4.html multicolor-image-4-ref.html
 == multicolor-image-5.html multicolor-image-5-ref.html
 != repeat-image-1.html repeat-image-1-ref.html
+!= different-h-v-1.html different-h-v-ref.html
+!= different-h-v-2.html different-h-v-ref.html
+!= different-h-v-1.html different-h-v-2.html
+== center-scaling-1.html center-scaling-1-ref.png
+== center-scaling-2.html center-scaling-2-ref.png
+== center-scaling-3.html center-scaling-3-ref.png
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8992232af81acad28b144953eee54c6d4bb5fd8d
GIT binary patch
literal 173
zc%17D@N?(olHy`uVBq!ia0vp^fgsGp3?#2VT(lHOaR&H=xc>kDAINNIXz+|*xedr-
zED7=pW^j0RBMrz2@^o<wu{fQaus|vyB|$*KBJg5B2!o)gL+ELXAFh#taU6~vn-s2W
zy5K&gv(YgzrNgL+xmr}jhqaK&?XQ`Q>&xlw8=gO27%G*hAI$EY>95GZpqHb+<)v5u
Q1fb~*p00i_>zopr0R1yI@Bjb+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-image/solid-image-2a.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <title>test of -moz-border-image</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+  <meta http-equiv="Content-Style-Type" content="text/css">
+  <style type="text/css">
+
+  div {
+    -moz-border-image: url('3x3green-1DD813.png') 1 1 1 1 / 10px repeat;
+    -khtml-border-image: url('3x3green-1DD813.png') 1 1 1 1 / 10px repeat;
+    border-image: url('3x3green-1DD813.png') 1 1 1 1 / 10px repeat;
+    margin: 1px;
+  }
+
+  </style>
+</head>
+<body>
+<div style="width: 50px; height: 5px"></div>
+<div style="width: 51px; height: 5px"></div>
+<div style="width: 52px; height: 5px"></div>
+<div style="width: 53px; height: 5px"></div>
+<div style="width: 54px; height: 5px"></div>
+<div style="width: 55px; height: 5px"></div>
+<div style="width: 56px; height: 5px"></div>
+<div style="width: 57px; height: 5px"></div>
+<div style="width: 58px; height: 5px"></div>
+<div style="width: 59px; height: 5px"></div>
+<div style="width: 550px; height: 5px"></div>
+<div style="width: 551px; height: 5px"></div>
+<div style="width: 552px; height: 5px"></div>
+<div style="width: 553px; height: 5px"></div>
+<div style="width: 554px; height: 5px"></div>
+<div style="width: 555px; height: 5px"></div>
+<div style="width: 556px; height: 5px"></div>
+<div style="width: 557px; height: 5px"></div>
+<div style="width: 558px; height: 5px"></div>
+<div style="width: 559px; height: 5px"></div>
+</body>
+</html>