--- a/gfx/public/nsIImage.h
+++ b/gfx/public/nsIImage.h
@@ -34,22 +34,24 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsIImage_h___
#define nsIImage_h___
#include "nsISupports.h"
-#include "nsIRenderingContext.h"
+#include "nsMargin.h"
#include "nsRect.h"
-#include "gfxRect.h"
class gfxASurface;
class gfxPattern;
+class gfxMatrix;
+class gfxRect;
+class gfxContext;
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
@@ -67,20 +69,20 @@ typedef enum {
nsMaskRequirements_kNeeds8Bit
} nsMaskRequirements;
#define nsImageUpdateFlags_kColorMapChanged 0x1
#define nsImageUpdateFlags_kBitsChanged 0x2
// IID for the nsIImage interface
-// 96d9d7ce-e575-4265-8507-35555112a430
+// 455fc276-01de-488f-9f8f-19b85a6b112d
#define NS_IIMAGE_IID \
-{ 0x96d9d7ce, 0xe575, 0x4265, \
- { 0x85, 0x07, 0x35, 0x55, 0x51, 0x12, 0xa4, 0x30 } }
+ { 0x455fc276, 0x01de, 0x488f, \
+ { 0x9f, 0x8f, 0x19, 0xb8, 0x5a, 0x6b, 0x11, 0x2d } }
// Interface to Images
class nsIImage : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IIMAGE_IID)
@@ -184,29 +186,42 @@ public:
/**
* Get the colormap for the nsIImage
* @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
+ * BitBlit the nsIImage to a device, the source and dest can be scaled.
+ * @param aContext the destination
+ * @param aUserSpaceToImageSpace the transform that maps user-space
+ * coordinates to coordinates in (tiled, post-padding) image pixels
+ * @param aFill the area to fill with tiled images
+ * @param aPadding the padding to be added to this image before tiling,
+ * in image pixels
+ * @param aSubimage the subimage in padded+tiled image space that we're
+ * extracting the contents from. Pixels outside this rectangle must not
* be sampled.
- * @param aDestRect destination rectangle, in device pixels
+ *
+ * So this is supposed to
+ * -- add aPadding transparent pixels around the image
+ * -- use that image to tile the plane
+ * -- replace everything outside the aSubimage region with the nearest
+ * border pixel of that region (like EXTEND_PAD)
+ * -- fill aFill with the image, using aImageSpaceToDeviceSpace as the
+ * image-space-to-device-space transform
*/
- NS_IMETHOD Draw(nsIRenderingContext &aContext,
- const gfxRect &aSourceRect,
- const gfxRect &aSubimageRect,
- const gfxRect &aDestRect) = 0;
+ virtual void Draw(gfxContext* aContext,
+ const gfxMatrix& aUserSpaceToImageSpace,
+ const gfxRect& aFill,
+ const nsIntMargin& aPadding,
+ const nsIntRect& aSubimage) = 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;
/**
* Return information about the bits for this structure
--- a/gfx/public/nsIRenderingContext.h
+++ b/gfx/public/nsIRenderingContext.h
@@ -92,19 +92,20 @@ typedef enum
typedef enum
{
nsPenMode_kNone = 0,
nsPenMode_kInvert = 1
} nsPenMode;
// IID for the nsIRenderingContext interface
-// a67de6b9-fffa-465c-abea-d7b394588a07
+// 3a6209e8-d80d-42ab-ad6a-b8832f7fb09f
#define NS_IRENDERING_CONTEXT_IID \
- { 0xa67de6b9, 0xfffa, 0x465c,{0xab, 0xea, 0xd7, 0xb3, 0x94, 0x58, 0x8a, 0x07}}
+{ 0x3a6209e8, 0xd80d, 0x42ab, \
+ { 0xad, 0x6a, 0xb8, 0x83, 0x2f, 0x7f, 0xb0, 0x9f } }
//----------------------------------------------------------------------
// RenderingContext interface
class nsIRenderingContext : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IRENDERING_CONTEXT_IID)
@@ -581,31 +582,16 @@ public:
NS_IMETHOD SetRightToLeftText(PRBool aIsRTL) = 0;
/**
* This sets the direction of the text; all characters should be
* overridden to have this direction.
*/
virtual void SetTextRunRTL(PRBool aIsRTL) = 0;
- /*
- * Tiles an image over an area
- * @param aImage Image to tile
- * @param aXImageStart x location where the origin (0,0) of the image starts
- * @param aYImageStart y location where the origin (0,0) of the image starts
- * @param aTargetRect area to draw to
- * @param aSubimageRect the subimage (in tile space) which we expect to
- * sample from; may be null to indicate that the whole image is
- * OK to sample from
- */
- NS_IMETHOD DrawTile(imgIContainer *aImage,
- nscoord aXImageStart, nscoord aYImageStart,
- const nsRect * aTargetRect,
- const nsIntRect * aSubimageRect) = 0;
-
/**
* Find the closest cursor position for a given x coordinate.
*
* This will find the closest byte index for a given x coordinate.
* This takes into account grapheme clusters and bidi text.
*
* @param aText Text on which to operate.
* @param aLength Length of the text.
--- a/gfx/src/thebes/nsThebesImage.cpp
+++ b/gfx/src/thebes/nsThebesImage.cpp
@@ -426,421 +426,255 @@ nsThebesImage::UnlockImagePixels(PRBool
mOptSurface = nsnull;
#ifdef XP_MACOSX
if (mQuartzSurface)
mQuartzSurface->Flush();
#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)
+static PRBool
+IsSafeImageTransformComponent(gfxFloat aValue)
{
- if (NS_UNLIKELY(aDestRect.IsEmpty())) {
- NS_ERROR("nsThebesImage::Draw zero dest size - please fix caller.");
- return NS_OK;
- }
+ return aValue >= -32768 && aValue <= 32767;
+}
- nsThebesRenderingContext *thebesRC = static_cast<nsThebesRenderingContext*>(&aContext);
- gfxContext *ctx = thebesRC->ThebesContext();
+void
+nsThebesImage::Draw(gfxContext* aContext,
+ 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");
-#if 0
- fprintf (stderr, "nsThebesImage::Draw src [%f %f %f %f] dest [%f %f %f %f] trans: [%f %f] dec: [%f %f]\n",
- aSourceRect.pos.x, aSourceRect.pos.y, aSourceRect.size.width, aSourceRect.size.height,
- aDestRect.pos.x, aDestRect.pos.y, aDestRect.size.width, aDestRect.size.height,
- ctx->CurrentMatrix().GetTranslation().x, ctx->CurrentMatrix().GetTranslation().y,
- mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
-#endif
+ PRBool doPadding = aPadding != nsIntMargin(0,0,0,0);
+ PRBool doPartialDecode = !GetIsImageComplete();
+ gfxContext::GraphicsOperator op = aContext->CurrentOperator();
- if (mSinglePixel) {
+ if (mSinglePixel && !doPadding && !doPartialDecode) {
+ // Single-color fast path
// if a == 0, it's a noop
if (mSinglePixelColor.a == 0.0)
- return NS_OK;
-
- // otherwise
- gfxContext::GraphicsOperator op = ctx->CurrentOperator();
- if (op == gfxContext::OPERATOR_OVER && mSinglePixelColor.a == 1.0)
- ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+ return;
- ctx->SetDeviceColor(mSinglePixelColor);
- ctx->NewPath();
- ctx->Rectangle(aDestRect, PR_TRUE);
- ctx->Fill();
- ctx->SetOperator(op);
- return NS_OK;
- }
-
- gfxFloat xscale = aDestRect.size.width / aSourceRect.size.width;
- gfxFloat yscale = aDestRect.size.height / aSourceRect.size.height;
+ if (op == gfxContext::OPERATOR_OVER && mSinglePixelColor.a == 1.0)
+ aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
- gfxRect srcRect(aSourceRect);
- gfxRect subimageRect(aSubimageRect);
- gfxRect destRect(aDestRect);
-
- if (!GetIsImageComplete()) {
- 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;
-
- 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;
+ aContext->SetDeviceColor(mSinglePixelColor);
+ aContext->NewPath();
+ aContext->Rectangle(aFill);
+ aContext->Fill();
+ aContext->SetOperator(op);
+ aContext->SetDeviceColor(gfxRGBA(0,0,0,0));
+ return;
}
- // 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)
- {
- gfxIntSize dim(NS_lroundf(destRect.size.width),
- NS_lroundf(destRect.size.height));
-
- // nothing to do in this case
- if (dim.width == 0 || dim.height == 0)
- return NS_OK;
-
- nsRefPtr<gfxASurface> temp =
- gfxPlatform::GetPlatform()->CreateOffscreenSurface (dim, mFormat);
- if (!temp || temp->CairoStatus() != 0)
- return NS_ERROR_FAILURE;
-
- gfxContext tempctx(temp);
+ gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
+ gfxRect sourceRect = userSpaceToImageSpace.Transform(aFill);
+ gfxRect imageRect(0, 0, mWidth + aPadding.LeftRight(), mHeight + aPadding.TopBottom());
+ gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
+ gfxRect fill = aFill;
+ nsRefPtr<gfxASurface> surface;
+ gfxImageSurface::gfxImageFormat format;
- gfxMatrix mat;
- mat.Translate(srcRect.pos);
- mat.Scale(1.0 / xscale, 1.0 / yscale);
- pat->SetMatrix(mat);
-
- 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;
+ PRBool doTile = !imageRect.Contains(sourceRect);
+ if (doPadding || doPartialDecode) {
+ gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height) +
+ gfxPoint(aPadding.left, aPadding.top);
+
+ if (!doTile && !mSinglePixel) {
+ // 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.
+ sourceRect = sourceRect.Intersect(available);
+ gfxMatrix imageSpaceToUserSpace = userSpaceToImageSpace;
+ imageSpaceToUserSpace.Invert();
+ fill = imageSpaceToUserSpace.Transform(sourceRect);
+
+ surface = ThebesSurface();
+ format = mFormat;
+ subimage = subimage.Intersect(available) - gfxPoint(aPadding.left, aPadding.top);
+ userSpaceToImageSpace.Multiply(
+ gfxMatrix().Translate(gfxPoint(aPadding.left, aPadding.top)));
+ sourceRect = sourceRect - gfxPoint(aPadding.left, aPadding.top);
+ imageRect = gfxRect(0, 0, mWidth, mHeight);
+ } else {
+ // Create a temporary surface
+ gfxIntSize size(PRInt32(imageRect.Width()),
+ PRInt32(imageRect.Height()));
+ // Give this surface an alpha channel because there are
+ // transparent pixels in the padding or undecoded area
+ format = gfxASurface::ImageFormatARGB32;
+ surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(size,
+ format);
+ if (!surface || surface->CairoStatus() != 0)
+ return;
+
+ // Fill 'available' with whatever we've got
+ gfxContext tmpCtx(surface);
+ tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
+ if (mSinglePixel) {
+ tmpCtx.SetDeviceColor(mSinglePixelColor);
+ } else {
+ tmpCtx.SetSource(ThebesSurface(), gfxPoint(aPadding.left, aPadding.top));
+ }
+ tmpCtx.Rectangle(available);
+ tmpCtx.Fill();
+ }
+ } else {
+ NS_ASSERTION(!mSinglePixel, "This should already have been handled");
+ surface = ThebesSurface();
+ format = mFormat;
}
-
- 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);
+ // At this point, we've taken care of mSinglePixel images, images with
+ // aPadding, and partially-decoded images.
- pat->SetMatrix(mat);
-
- 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;
-
- 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;
+ if (!AllowedImageSize(fill.size.width + 1, fill.size.height + 1)) {
+ NS_WARNING("Destination area too large, bailing out");
+ return;
}
- gfxContext::GraphicsOperator op = ctx->CurrentOperator();
- if (op == gfxContext::OPERATOR_OVER && mFormat == gfxASurface::ImageFormatRGB24)
- ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
-
- ctx->NewPath();
- ctx->SetPattern(pat);
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
- ctx->Rectangle(destRect, PR_TRUE);
-#else
- ctx->Rectangle(destRect);
-#endif
- ctx->Fill();
-
- ctx->SetOperator(op);
- ctx->SetDeviceColor(gfxRGBA(0,0,0,0));
-
- return NS_OK;
-}
-
-nsresult
-nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
- nsIDeviceContext* dx,
- const gfxPoint& offset,
- const gfxRect& targetRect,
- const nsIntRect& aSubimageRect,
- const PRInt32 xPadding,
- const PRInt32 yPadding)
-{
- NS_ASSERTION(xPadding >= 0 && yPadding >= 0, "negative padding");
-
- if (targetRect.size.width <= 0.0 || targetRect.size.height <= 0.0)
- return NS_OK;
-
- // don't do anything if we have a transparent pixel source
- if (mSinglePixel && mSinglePixelColor.a == 0.0)
- return NS_OK;
-
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
- PRBool doSnap = PR_TRUE;
-#else
- PRBool doSnap = !(thebesContext->CurrentMatrix().HasNonTranslation());
-#endif
- PRBool hasPadding = ((xPadding != 0) || (yPadding != 0));
- gfxImageSurface::gfxImageFormat format = mFormat;
-
- gfxPoint tmpOffset = offset;
-
- if (mSinglePixel && !hasPadding) {
- thebesContext->SetDeviceColor(mSinglePixelColor);
- } else {
- nsRefPtr<gfxASurface> surface;
- PRInt32 width, height;
-
- if (hasPadding) {
- /* Ugh we have padding; create a temporary surface that's the size of the surface + pad area,
- * and render the image into it first. Then we'll tile that surface. */
- width = mWidth + xPadding;
- height = mHeight + yPadding;
-
- // Reject over-wide or over-tall images.
- if (!AllowedImageSize(width, height))
- return NS_ERROR_FAILURE;
-
- format = gfxASurface::ImageFormatARGB32;
- surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
- gfxIntSize(width, height), format);
- if (!surface || surface->CairoStatus()) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- gfxContext tmpContext(surface);
- if (mSinglePixel) {
- tmpContext.SetDeviceColor(mSinglePixelColor);
- } else {
- tmpContext.SetSource(ThebesSurface());
- }
- tmpContext.SetOperator(gfxContext::OPERATOR_SOURCE);
- tmpContext.Rectangle(gfxRect(0, 0, mWidth, mHeight));
- tmpContext.Fill();
- } else {
- width = mWidth;
- height = mHeight;
- surface = ThebesSurface();
- }
-
- // Scale factor to account for CSS pixels; note that the offset (and
- // therefore p0) is in device pixels, while the width and height are in
- // CSS pixels.
- gfxFloat scale = gfxFloat(dx->AppUnitsPerDevPixel()) /
- gfxFloat(nsIDeviceContext::AppUnitsPerCSSPixel());
-
- if ((aSubimageRect.width < width || aSubimageRect.height < height) &&
- (thebesContext->CurrentMatrix().HasNonTranslation() || scale != 1.0)) {
- // Some of the source image should not be drawn, and we're going
- // to be doing more than just translation, so we might accidentally
- // sample the non-drawn pixels. Avoid that by creating a
- // temporary image representing the portion that will be drawn,
- // with built-in padding since we can't use EXTEND_PAD and
- // EXTEND_REPEAT at the same time for different axes.
- PRInt32 padX = aSubimageRect.width < width ? 1 : 0;
- PRInt32 padY = aSubimageRect.height < height ? 1 : 0;
- PRInt32 tileWidth = PR_MIN(aSubimageRect.width, width);
- PRInt32 tileHeight = PR_MIN(aSubimageRect.height, height);
-
- // This tmpSurface will contain a snapshot of the repeated
- // tile image at (aSubimageRect.x, aSubimageRect.y,
- // tileWidth, tileHeight), with padX padding added to the left
- // and right sides and padY padding added to the top and bottom
- // sides.
- nsRefPtr<gfxASurface> tmpSurface;
- tmpSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
- gfxIntSize(tileWidth + 2*padX, tileHeight + 2*padY), format);
- if (!tmpSurface || tmpSurface->CairoStatus()) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- gfxContext tmpContext(tmpSurface);
- tmpContext.SetOperator(gfxContext::OPERATOR_SOURCE);
- gfxPattern pat(surface);
- pat.SetExtend(gfxPattern::EXTEND_REPEAT);
-
- // Copy the needed portion of the source image to the temporary
- // surface. We also copy over horizontal and/or vertical padding
- // strips one pixel wide, plus the corner pixels if necessary.
- // So in the most general case the temporary surface ends up
- // looking like
- // P P P ... P P P
- // P X X ... X X P
- // P X X ... X X P
- // ...............
- // P X X ... X X P
- // P X X ... X X P
- // P P P ... P P P
- // Where each P pixel has the color of its nearest source X
- // pixel. We implement this as a loop over all nine possible
- // areas, [padding, body, padding] x [padding, body, padding].
- // Note that we will not need padding on both axes unless
- // we are painting just a single tile, in which case this
- // will hardly ever get called since nsCSSRendering converts
- // the single-tile case to nsLayoutUtils::DrawImage. But this
- // could be called on other paths (XUL trees?) and it's simpler
- // and clearer to do it the general way.
- PRInt32 destY = 0;
- for (PRInt32 y = -1; y <= 1; ++y) {
- PRInt32 stripHeight = y == 0 ? tileHeight : padY;
- if (stripHeight == 0)
- continue;
- PRInt32 srcY = y == 1 ? aSubimageRect.YMost() - padY : aSubimageRect.y;
-
- PRInt32 destX = 0;
- for (PRInt32 x = -1; x <= 1; ++x) {
- PRInt32 stripWidth = x == 0 ? tileWidth : padX;
- if (stripWidth == 0)
- continue;
- PRInt32 srcX = x == 1 ? aSubimageRect.XMost() - padX : aSubimageRect.x;
-
- gfxMatrix patMat;
- patMat.Translate(gfxPoint(srcX - destX, srcY - destY));
- pat.SetMatrix(patMat);
- tmpContext.SetPattern(&pat);
- tmpContext.Rectangle(gfxRect(destX, destY, stripWidth, stripHeight));
- tmpContext.Fill();
- tmpContext.NewPath();
-
- destX += stripWidth;
- }
- destY += stripHeight;
- }
-
- // tmpOffset was the top-left of the old tile image. Make it
- // the top-left of the new tile image. Note that tmpOffset is
- // in destination coordinate space so we have to scale our
- // CSS pixels.
- tmpOffset += gfxPoint(aSubimageRect.x - padX, aSubimageRect.y - padY)/scale;
-
- surface = tmpSurface;
- }
-
- gfxMatrix patMat;
- gfxPoint p0;
-
- p0.x = - floor(tmpOffset.x + 0.5);
- p0.y = - floor(tmpOffset.y + 0.5);
- patMat.Scale(scale, scale);
- patMat.Translate(p0);
-
- gfxPattern pat(surface);
- pat.SetExtend(gfxPattern::EXTEND_REPEAT);
- pat.SetMatrix(patMat);
-
-#ifndef XP_MACOSX
- if (scale < 1.0) {
- // See bug 324698. This is a workaround. See comments
- // by the earlier SetFilter call.
- pat.SetFilter(0);
- }
-#endif
-
- thebesContext->SetPattern(&pat);
+ // BEGIN working around cairo/pixman bug (bug 364968)
+ // Compute device-space-to-image-space transform. We need to sanity-
+ // check it to work around a pixman bug :-(
+ // XXX should we only do this for certain surface types?
+ gfxFloat deviceX, deviceY;
+ nsRefPtr<gfxASurface> currentTarget =
+ aContext->CurrentSurface(&deviceX, &deviceY);
+ gfxMatrix currentMatrix = aContext->CurrentMatrix();
+ gfxMatrix deviceToUser = currentMatrix;
+ deviceToUser.Invert();
+ deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
+ gfxMatrix deviceToImage = deviceToUser;
+ deviceToImage.Multiply(userSpaceToImageSpace);
+
+ // Our device-space-to-image-space transform may not be acceptable to pixman.
+ if (!IsSafeImageTransformComponent(deviceToImage.xx) ||
+ !IsSafeImageTransformComponent(deviceToImage.xy) ||
+ !IsSafeImageTransformComponent(deviceToImage.yx) ||
+ !IsSafeImageTransformComponent(deviceToImage.yy)) {
+ NS_WARNING("Scaling up too much, bailing out");
+ return;
}
- gfxContext::GraphicsOperator op = thebesContext->CurrentOperator();
- if (op == gfxContext::OPERATOR_OVER && format == gfxASurface::ImageFormatRGB24)
- thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
+ PRBool pushedGroup = PR_FALSE;
+ if (!IsSafeImageTransformComponent(deviceToImage.x0) ||
+ !IsSafeImageTransformComponent(deviceToImage.y0)) {
+ // We'll push a group, which will hopefully reduce our transform's
+ // translation so it's in bounds
+ aContext->Save();
+
+ // Clip the rounded-out-to-device-pixels bounds of the
+ // transformed fill area. This is the area for the group we
+ // want to push.
+ aContext->IdentityMatrix();
+ gfxRect bounds = currentMatrix.TransformBounds(fill);
+ bounds.RoundOut();
+ aContext->Clip(bounds);
+ aContext->SetMatrix(currentMatrix);
+
+ aContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
+ aContext->SetOperator(gfxContext::OPERATOR_OVER);
+ pushedGroup = PR_TRUE;
+ }
+ // END working around cairo/pixman bug (bug 364968)
+
+ nsRefPtr<gfxPattern> pattern = new gfxPattern(surface);
+ pattern->SetMatrix(userSpaceToImageSpace);
- thebesContext->NewPath();
- thebesContext->Rectangle(targetRect, doSnap);
- thebesContext->Fill();
+ // 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() &&
+ !userSpaceToImageSpace.HasNonIntegerTranslation()) {
+ if (doTile) {
+ pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
+ }
+ } else {
+ if (doTile || !subimage.Contains(imageRect)) {
+ // 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
+ gfxRect needed = subimage.Intersect(sourceRect);
+ needed.RoundOut();
+ gfxIntSize size(PRInt32(needed.Width()), PRInt32(needed.Height()));
+ nsRefPtr<gfxASurface> temp =
+ gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, format);
+ if (temp && temp->CairoStatus() == 0) {
+ gfxContext tmpCtx(temp);
+ tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
+ nsRefPtr<gfxPattern> tmpPattern = new gfxPattern(surface);
+ if (tmpPattern) {
+ tmpPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
+ tmpPattern->SetMatrix(gfxMatrix().Translate(needed.pos));
+ tmpCtx.SetPattern(tmpPattern);
+ tmpCtx.Paint();
+ tmpPattern = new gfxPattern(temp);
+ if (tmpPattern) {
+ pattern.swap(tmpPattern);
+ pattern->SetMatrix(
+ gfxMatrix(userSpaceToImageSpace).Multiply(gfxMatrix().Translate(-needed.pos)));
+ }
+ }
+ }
+ }
+
+ // In theory we can handle this using cairo's EXTEND_PAD,
+ // but implementation limitations mean we have to consult
+ // the surface type.
+ switch (currentTarget->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 --- 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.
+ pattern->SetFilter(0);
+ break;
+
+ case gfxASurface::SurfaceTypeQuartz:
+ case gfxASurface::SurfaceTypeQuartzImage:
+ // Do nothing, Mac seems to be OK. Really?
+ break;
- thebesContext->SetOperator(op);
- thebesContext->SetDeviceColor(gfxRGBA(0,0,0,0));
+ default:
+ // turn on EXTEND_PAD.
+ // This is what we really want for all surface types, if the
+ // implementation was universally good.
+ pattern->SetExtend(gfxPattern::EXTEND_PAD);
+ break;
+ }
+ }
- return NS_OK;
+ if ((op == gfxContext::OPERATOR_OVER || pushedGroup) &&
+ format == gfxASurface::ImageFormatRGB24) {
+ aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
+ }
+
+ // Phew! Now we can actually draw this image
+ aContext->NewPath();
+ aContext->SetPattern(pattern);
+ aContext->Rectangle(fill);
+ aContext->Fill();
+
+ aContext->SetOperator(op);
+ if (pushedGroup) {
+ aContext->PopGroupToSource();
+ aContext->Paint();
+ aContext->Restore();
+ }
}
PRBool
nsThebesImage::ShouldUseImageSurfaces()
{
#ifdef XP_WIN
static const DWORD kGDIObjectsHighWaterMark = 7000;
--- a/gfx/src/thebes/nsThebesImage.h
+++ b/gfx/src/thebes/nsThebesImage.h
@@ -70,28 +70,21 @@ public:
virtual PRBool GetHasAlphaMask();
virtual PRUint8 *GetAlphaBits();
virtual PRInt32 GetAlphaLineStride();
virtual PRBool GetIsImageComplete();
virtual nsresult 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 nsIntRect& aSubimageRect,
- const PRInt32 aXPadding,
- const PRInt32 aYPadding);
+ virtual void Draw(gfxContext* aContext,
+ const gfxMatrix& aUserSpaceToImageSpace,
+ const gfxRect& aFill,
+ const nsIntMargin& aPadding,
+ const nsIntRect& aSubimage);
virtual PRInt8 GetAlphaDepth();
virtual void* GetBitInfo();
NS_IMETHOD LockImagePixels(PRBool aMaskPixels);
NS_IMETHOD UnlockImagePixels(PRBool aMaskPixels);
NS_IMETHOD GetSurface(gfxASurface **aSurface) {
*aSurface = ThebesSurface();
--- a/gfx/src/thebes/nsThebesRenderingContext.cpp
+++ b/gfx/src/thebes/nsThebesRenderingContext.cpp
@@ -755,86 +755,16 @@ nsThebesRenderingContext::PopFilter()
mThebes->Restore();
}
return NS_OK;
}
-NS_IMETHODIMP
-nsThebesRenderingContext::DrawTile(imgIContainer *aImage,
- nscoord twXOffset, nscoord twYOffset,
- const nsRect *twTargetRect,
- const nsIntRect *subimageRect)
-{
- PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawTile %p %f %f [%f,%f,%f,%f]\n",
- this, aImage, FROM_TWIPS(twXOffset), FROM_TWIPS(twYOffset),
- FROM_TWIPS(twTargetRect->x), FROM_TWIPS(twTargetRect->y),
- FROM_TWIPS(twTargetRect->width), FROM_TWIPS(twTargetRect->height)));
-
- nscoord containerWidth, containerHeight;
- aImage->GetWidth(&containerWidth);
- aImage->GetHeight(&containerHeight);
-
- nsCOMPtr<gfxIImageFrame> imgFrame;
- aImage->GetCurrentFrame(getter_AddRefs(imgFrame));
- if (!imgFrame) return NS_ERROR_FAILURE;
-
- nsRect imgFrameRect;
- imgFrame->GetRect(imgFrameRect);
-
- nsCOMPtr<nsIImage> img(do_GetInterface(imgFrame));
- if (!img) return NS_ERROR_FAILURE;
-
- nsThebesImage *thebesImage = static_cast<nsThebesImage*>((nsIImage*) img.get());
-
- /* Phase offset of the repeated image from the origin */
- gfxPoint phase(FROM_TWIPS(twXOffset), FROM_TWIPS(twYOffset));
-
- /* The image may be smaller than the container (bug 113561),
- * so we need to make sure that there is the right amount of padding
- * in between each tile of the nsIImage. This problem goes away
- * when we change the way the GIF decoder works to have it store
- * full frames that are ready to be composited.
- */
- PRInt32 xPadding = 0;
- PRInt32 yPadding = 0;
-
- nsIntRect tmpSubimageRect;
- if (subimageRect) {
- tmpSubimageRect = *subimageRect;
- } else {
- tmpSubimageRect = nsIntRect(0, 0, containerWidth, containerHeight);
- }
-
- if (imgFrameRect.width != containerWidth ||
- imgFrameRect.height != containerHeight)
- {
- xPadding = containerWidth - imgFrameRect.width;
- yPadding = containerHeight - imgFrameRect.height;
-
- // XXXroc shouldn't we be adding to 'phase' here? it's tbe origin
- // at which the image origin should be drawn, and ThebesDrawTile
- // just draws the origin of its "frame" there, so we should be
- // adding imgFrameRect.x/y. so that the imgFrame draws in the
- // right place.
- phase.x -= imgFrameRect.x;
- phase.y -= imgFrameRect.y;
-
- tmpSubimageRect.x -= imgFrameRect.x;
- tmpSubimageRect.y -= imgFrameRect.y;
- }
-
- return thebesImage->ThebesDrawTile (mThebes, mDeviceContext, phase,
- GFX_RECT_FROM_TWIPS_RECT(*twTargetRect),
- tmpSubimageRect,
- xPadding, yPadding);
-}
-
//
// text junk
//
NS_IMETHODIMP
nsThebesRenderingContext::SetRightToLeftText(PRBool aIsRTL)
{
return mFontMetrics->SetRightToLeftText(aIsRTL);
}
--- a/gfx/src/thebes/nsThebesRenderingContext.h
+++ b/gfx/src/thebes/nsThebesRenderingContext.h
@@ -178,18 +178,16 @@ public:
NS_IMETHOD PopFilter();
virtual void* GetNativeGraphicData(GraphicDataType aType);
NS_IMETHOD PushTranslation(PushedTranslation* aState);
NS_IMETHOD PopTranslation(PushedTranslation* aState);
NS_IMETHOD SetTranslation(nscoord aX, nscoord aY);
- NS_IMETHOD DrawTile(imgIContainer *aImage, nscoord aXOffset, nscoord aYOffset,
- const nsRect * aTargetRect, const nsIntRect * aSubimageRect);
NS_IMETHOD SetRightToLeftText(PRBool aIsRTL);
NS_IMETHOD GetRightToLeftText(PRBool* aIsRTL);
virtual void SetTextRunRTL(PRBool aIsRTL);
virtual PRInt32 GetPosition(const PRUnichar *aText,
PRUint32 aLength,
nsPoint aPt);
NS_IMETHOD GetRangeWidth(const PRUnichar *aText,
--- a/gfx/thebes/public/gfxContext.h
+++ b/gfx/thebes/public/gfxContext.h
@@ -334,16 +334,29 @@ public:
*
* If ignoreScale is PR_TRUE, then snapping will take place even if
* the CTM has a scale applied. Snapping never takes place if
* there is a rotation in the CTM.
*/
PRBool UserToDevicePixelSnapped(gfxRect& rect, PRBool ignoreScale = PR_FALSE) const;
/**
+ * Takes the given point and tries to align it to device pixels. If
+ * this succeeds, the method will return PR_TRUE, and the point will
+ * be in device coordinates (already transformed by the CTM). If it
+ * fails, the method will return PR_FALSE, and the point will not be
+ * changed.
+ *
+ * If ignoreScale is PR_TRUE, then snapping will take place even if
+ * the CTM has a scale applied. Snapping never takes place if
+ * there is a rotation in the CTM.
+ */
+ PRBool UserToDevicePixelSnapped(gfxPoint& pt, PRBool ignoreScale = PR_FALSE) const;
+
+ /**
* Attempts to pixel snap the rectangle, add it to the current
* path, and to set pattern as the current painting source. This
* should be used for drawing filled pixel-snapped rectangles (like
* images), because the CTM at the time of the SetPattern call needs
* to have a snapped translation, or you get smeared images.
*/
void PixelSnappedRectangleAndSetPattern(const gfxRect& rect, gfxPattern *pattern);
--- a/gfx/thebes/public/gfxMatrix.h
+++ b/gfx/thebes/public/gfxMatrix.h
@@ -37,16 +37,17 @@
#ifndef GFX_MATRIX_H
#define GFX_MATRIX_H
#include "gfxPoint.h"
#include "gfxTypes.h"
#include "gfxRect.h"
#include "gfxUtils.h"
+#include "nsMathUtils.h"
// XX - I don't think this class should use gfxFloat at all,
// but should use 'double' and be called gfxDoubleMatrix;
// we can then typedef that to gfxMatrix where we typedef
// double to be gfxFloat.
/**
* A matrix that represents an affine transformation. Projective
@@ -171,40 +172,50 @@ public:
/**
* Returns the translation component of this matrix.
*/
gfxPoint GetTranslation() const {
return gfxPoint(x0, y0);
}
/**
+ * Returns true if the matrix is anything other than a straight
+ * translation by integers.
+ */
+ PRBool HasNonIntegerTranslation() const {
+ return HasNonTranslation() ||
+ !gfxUtils::FuzzyEqual(x0, NS_floor(x0 + 0.5)) ||
+ !gfxUtils::FuzzyEqual(y0, NS_floor(y0 + 0.5));
+ }
+
+ /**
* Returns true if the matrix has any transform other
* than a straight translation
*/
- bool HasNonTranslation() const {
+ PRBool HasNonTranslation() const {
return !gfxUtils::FuzzyEqual(xx, 1.0) || !gfxUtils::FuzzyEqual(yy, 1.0) ||
!gfxUtils::FuzzyEqual(xy, 0.0) || !gfxUtils::FuzzyEqual(yx, 0.0);
}
/**
* Returns true if the matrix has any transform other
* than a translation or a -1 y scale (y axis flip)
*/
- bool HasNonTranslationOrFlip() const {
+ PRBool HasNonTranslationOrFlip() const {
return !gfxUtils::FuzzyEqual(xx, 1.0) ||
(!gfxUtils::FuzzyEqual(yy, 1.0) && !gfxUtils::FuzzyEqual(yy, -1.0)) ||
!gfxUtils::FuzzyEqual(xy, 0.0) || !gfxUtils::FuzzyEqual(yx, 0.0);
}
/**
* Returns true if the matrix has any transform other
* than a translation or scale; this is, if there is
* no rotation.
*/
- bool HasNonAxisAlignedTransform() const {
+ PRBool HasNonAxisAlignedTransform() const {
return !gfxUtils::FuzzyEqual(xy, 0.0) || !gfxUtils::FuzzyEqual(yx, 0.0);
}
/**
* Computes the determinant of this matrix.
*/
double Determinant() const {
return xx*yy - yx*xy;
--- a/gfx/thebes/public/gfxWindowsNativeDrawing.h
+++ b/gfx/thebes/public/gfxWindowsNativeDrawing.h
@@ -66,31 +66,19 @@ public:
/* If we have to do transforms with cairo, should we use nearest-neighbour filtering? */
DO_NEAREST_NEIGHBOR_FILTERING = 1 << 3,
DO_BILINEAR_FILTERING = 0 << 3
};
/* Create native win32 drawing for a rectangle bounded by
* nativeRect.
*
- * This class assumes that native drawing can take place only if
- * the destination surface has a content type of COLOR (that is,
- * RGB24), and that the transformation matrix consists of only a
- * translation (in which case the coordinates are munged directly)
- * or a translation and scale (in which case SetWorldTransform is used).
- *
- * If the destination is of a non-win32 surface type, a win32
- * surface of content COLOR_ALPHA, or if there is a complex
- * transform (i.e., one with rotation) set, then the native drawing
- * code will fall back to alpha recovery, but will still take advantage
- * of native axis-aligned scaling.
- *
* Typical usage looks like:
*
- * gfxWindowsNativeDrawing nativeDraw(ctx, destGfxRect);
+ * gfxWindowsNativeDrawing nativeDraw(ctx, destGfxRect, capabilities);
* do {
* HDC dc = nativeDraw.BeginNativeDrawing();
* if (!dc)
* return NS_ERROR_FAILURE;
*
* RECT winRect;
* nativeDraw.TransformToNativeRect(rect, winRect);
*
--- a/gfx/thebes/src/gfxContext.cpp
+++ b/gfx/thebes/src/gfxContext.cpp
@@ -422,16 +422,36 @@ gfxContext::UserToDevicePixelSnapped(gfx
gfxPoint pd = p2 - p1;
rect.pos = p1;
rect.size = gfxSize(pd.x, pd.y);
return PR_TRUE;
}
+PRBool
+gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, PRBool ignoreScale) const
+{
+ if (GetFlags() & FLAG_DISABLE_SNAPPING)
+ return PR_FALSE;
+
+ // if we're not at 1.0 scale, don't snap, unless we're
+ // ignoring the scale. If we're not -just- a scale,
+ // never snap.
+ cairo_matrix_t mat;
+ cairo_get_matrix(mCairo, &mat);
+ if ((!ignoreScale && (mat.xx != 1.0 || mat.yy != 1.0)) ||
+ (mat.xy != 0.0 || mat.yx != 0.0))
+ return PR_FALSE;
+
+ pt = UserToDevice(pt);
+ pt.Round();
+ return PR_TRUE;
+}
+
void
gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect,
gfxPattern *pattern)
{
gfxRect r(rect);
// Bob attempts to pixel-snap the rectangle, and returns true if
// the snapping succeeds. If it does, we need to set up an
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -783,118 +783,63 @@ nsCSSRendering::PaintFocus(nsPresContext
}
// Thebes Border Rendering Code End
//----------------------------------------------------------------------
//----------------------------------------------------------------------
-// Returns the anchor point to use for the background image. The
-// anchor point is the (x, y) location where the first tile should
-// be placed
-//
-// For repeated tiling, the anchor values are normalized wrt to the upper-left
-// edge of the bounds, and are always in the range:
-// -(aTileWidth - 1) <= anchor.x <= 0
-// -(aTileHeight - 1) <= anchor.y <= 0
-//
-// i.e., they are either 0 or a negative number whose absolute value is
-// less than the tile size in that dimension
-//
-// aOriginBounds is the box to which the tiling position should be relative
-// aClipBounds is the box in which the tiling will actually be done
-// They should correspond to 'background-origin' and 'background-clip',
-// except when painting on the canvas, in which case the origin bounds
-// should be the bounds of the root element's frame and the clip bounds
-// should be the bounds of the canvas frame.
+/**
+ * Computes the placement of a background image.
+ *
+ * @param aOriginBounds is the box to which the tiling position should be
+ * relative
+ * This should correspond to 'background-origin' for the frame,
+ * except when painting on the canvas, in which case the origin bounds
+ * should be the bounds of the root element's frame.
+ * @param aTopLeft the top-left corner where an image tile should be drawn
+ * @param aAnchorPoint a point which should be pixel-aligned by
+ * nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless CSS
+ * specifies a percentage (including 'right' or 'bottom'), in which case
+ * it's that percentage within of aOriginBounds. So 'right' would set
+ * aAnchorPoint.x to aOriginBounds.XMost().
+ *
+ * Points are returned relative to aOriginBounds.
+ */
static void
ComputeBackgroundAnchorPoint(const nsStyleBackground& aColor,
- const nsRect& aOriginBounds,
- const nsRect& aClipBounds,
- nscoord aTileWidth, nscoord aTileHeight,
- nsPoint& aResult)
+ const nsSize& aOriginBounds,
+ const nsSize& aImageSize,
+ nsPoint* aTopLeft,
+ nsPoint* aAnchorPoint)
{
- nscoord x;
if (NS_STYLE_BG_X_POSITION_LENGTH & aColor.mBackgroundFlags) {
- x = aColor.mBackgroundXPosition.mCoord;
+ aTopLeft->x = aAnchorPoint->x = aColor.mBackgroundXPosition.mCoord;
}
else if (NS_STYLE_BG_X_POSITION_PERCENT & aColor.mBackgroundFlags) {
- PRFloat64 percent = PRFloat64(aColor.mBackgroundXPosition.mFloat);
- nscoord tilePos = nscoord(percent * PRFloat64(aTileWidth));
- nscoord boxPos = nscoord(percent * PRFloat64(aOriginBounds.width));
- x = boxPos - tilePos;
+ double percent = aColor.mBackgroundXPosition.mFloat;
+ aAnchorPoint->x = NSToCoordRound(percent*aOriginBounds.width);
+ aTopLeft->x = NSToCoordRound(percent*(aOriginBounds.width - aImageSize.width));
}
else {
- x = 0;
+ aTopLeft->x = aAnchorPoint->x = 0;
}
- x += aOriginBounds.x - aClipBounds.x;
- if (NS_STYLE_BG_REPEAT_X & aColor.mBackgroundRepeat) {
- // When we are tiling in the x direction the loop will run from
- // the left edge of the box to the right edge of the box. We need
- // to adjust the starting coordinate to lie within the band being
- // rendered.
- if (x < 0) {
- x = -x;
- if (x < 0) {
- // Some joker gave us max-negative-integer.
- x = 0;
- }
- x %= aTileWidth;
- x = -x;
- }
- else if (x != 0) {
- x %= aTileWidth;
- if (x > 0) {
- x = x - aTileWidth;
- }
- }
- NS_POSTCONDITION((x >= -(aTileWidth - 1)) && (x <= 0), "bad computed anchor value");
+ if (NS_STYLE_BG_Y_POSITION_LENGTH & aColor.mBackgroundFlags) {
+ aTopLeft->y = aAnchorPoint->y = aColor.mBackgroundYPosition.mCoord;
}
- aResult.x = x;
-
- nscoord y;
- if (NS_STYLE_BG_Y_POSITION_LENGTH & aColor.mBackgroundFlags) {
- y = aColor.mBackgroundYPosition.mCoord;
- }
- else if (NS_STYLE_BG_Y_POSITION_PERCENT & aColor.mBackgroundFlags){
- PRFloat64 percent = PRFloat64(aColor.mBackgroundYPosition.mFloat);
- nscoord tilePos = nscoord(percent * PRFloat64(aTileHeight));
- nscoord boxPos = nscoord(percent * PRFloat64(aOriginBounds.height));
- y = boxPos - tilePos;
+ else if (NS_STYLE_BG_Y_POSITION_PERCENT & aColor.mBackgroundFlags) {
+ double percent = aColor.mBackgroundYPosition.mFloat;
+ aAnchorPoint->y = NSToCoordRound(percent*aOriginBounds.height);
+ aTopLeft->y = NSToCoordRound(percent*(aOriginBounds.height - aImageSize.height));
}
else {
- y = 0;
+ aTopLeft->y = aAnchorPoint->y = 0;
}
- y += aOriginBounds.y - aClipBounds.y;
- if (NS_STYLE_BG_REPEAT_Y & aColor.mBackgroundRepeat) {
- // When we are tiling in the y direction the loop will run from
- // the top edge of the box to the bottom edge of the box. We need
- // to adjust the starting coordinate to lie within the band being
- // rendered.
- if (y < 0) {
- y = -y;
- if (y < 0) {
- // Some joker gave us max-negative-integer.
- y = 0;
- }
- y %= aTileHeight;
- y = -y;
- }
- else if (y != 0) {
- y %= aTileHeight;
- if (y > 0) {
- y = y - aTileHeight;
- }
- }
-
- NS_POSTCONDITION((y >= -(aTileHeight - 1)) && (y <= 0), "bad computed anchor value");
- }
- aResult.y = y;
}
const nsStyleBackground*
nsCSSRendering::FindNonTransparentBackground(nsStyleContext* aContext,
PRBool aStartAtParent /*= PR_FALSE*/)
{
NS_ASSERTION(aContext, "Cannot find NonTransparentBackground in a null context" );
@@ -1260,94 +1205,16 @@ nsCSSRendering::PaintBackground(nsPresCo
vm->SetDefaultBackgroundColor(canvasColor.mBackgroundColor);
PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
aDirtyRect, aBorderArea, canvasColor,
*border, aUsePrintSettings, aBGClipRect);
}
-inline nscoord IntDivFloor(nscoord aDividend, nscoord aDivisor)
-{
- NS_PRECONDITION(aDivisor > 0,
- "this function only works for positive divisors");
- // ANSI C, ISO 9899:1999 section 6.5.5 defines integer division as
- // truncation of the result towards zero. Earlier C standards, as
- // well as the C++ standards (1998 and 2003) do not, but we depend
- // on it elsewhere.
- return (aDividend < 0 ? (aDividend - aDivisor + 1) : aDividend) / aDivisor;
-}
-
-inline nscoord IntDivCeil(nscoord aDividend, nscoord aDivisor)
-{
- NS_PRECONDITION(aDivisor > 0,
- "this function only works for positive divisors");
- // ANSI C, ISO 9899:1999 section 6.5.5 defines integer division as
- // truncation of the result towards zero. Earlier C standards, as
- // well as the C++ standards (1998 and 2003) do not, but we depend
- // on it elsewhere.
- return (aDividend > 0 ? (aDividend + aDivisor - 1) : aDividend) / aDivisor;
-}
-
-/**
- * Return the largest 'v' such that v = aTileOffset + N*aTileSize, for some
- * integer N, and v <= aDirtyStart.
- */
-static nscoord
-FindTileStart(nscoord aDirtyStart, nscoord aTileOffset, nscoord aTileSize)
-{
- // Find largest integer N such that aTileOffset + N*aTileSize <= aDirtyStart
- return aTileOffset +
- IntDivFloor(aDirtyStart - aTileOffset, aTileSize) * aTileSize;
-}
-
-/**
- * Return the smallest 'v' such that v = aTileOffset + N*aTileSize, for some
- * integer N, and v >= aDirtyEnd.
- */
-static nscoord
-FindTileEnd(nscoord aDirtyEnd, nscoord aTileOffset, nscoord aTileSize)
-{
- // Find smallest integer N such that aTileOffset + N*aTileSize >= aDirtyEnd
- return aTileOffset +
- IntDivCeil(aDirtyEnd - aTileOffset, aTileSize) * aTileSize;
-}
-
-static void
-PixelSnapRectangle(gfxContext* aContext, nsIDeviceContext *aDC, nsRect& aRect)
-{
- gfxRect tmpRect;
- tmpRect.pos.x = aDC->AppUnitsToGfxUnits(aRect.x);
- tmpRect.pos.y = aDC->AppUnitsToGfxUnits(aRect.y);
- tmpRect.size.width = aDC->AppUnitsToGfxUnits(aRect.width);
- tmpRect.size.height = aDC->AppUnitsToGfxUnits(aRect.height);
- if (aContext->UserToDevicePixelSnapped(tmpRect)) {
- tmpRect = aContext->DeviceToUser(tmpRect);
- aRect.x = aDC->GfxUnitsToAppUnits(tmpRect.pos.x);
- aRect.y = aDC->GfxUnitsToAppUnits(tmpRect.pos.y);
- aRect.width = aDC->GfxUnitsToAppUnits(tmpRect.XMost()) - aRect.x;
- aRect.height = aDC->GfxUnitsToAppUnits(tmpRect.YMost()) - aRect.y;
- }
-}
-
-static void
-PixelSnapPoint(gfxContext* aContext, nsIDeviceContext *aDC, nsPoint& aPoint)
-{
- gfxRect tmpRect;
- tmpRect.pos.x = aDC->AppUnitsToGfxUnits(aPoint.x);
- tmpRect.pos.y = aDC->AppUnitsToGfxUnits(aPoint.y);
- tmpRect.size.width = 0;
- tmpRect.size.height = 0;
- if (aContext->UserToDevicePixelSnapped(tmpRect)) {
- tmpRect = aContext->DeviceToUser(tmpRect);
- aPoint.x = aDC->GfxUnitsToAppUnits(tmpRect.pos.x);
- aPoint.y = aDC->GfxUnitsToAppUnits(tmpRect.pos.y);
- }
-}
-
static PRBool
IsSolidBorderEdge(const nsStyleBorder& aBorder, PRUint32 aSide)
{
if (aBorder.GetActualBorder().side(aSide) == 0)
return PR_TRUE;
if (aBorder.GetBorderStyle(aSide) != NS_STYLE_BORDER_STYLE_SOLID)
return PR_FALSE;
@@ -1417,16 +1284,17 @@ nsCSSRendering::PaintBackgroundWithSC(ns
nsRect dirty;
dirty.IntersectRect(aDirtyRect, aBorderArea);
theme->DrawWidgetBackground(&aRenderingContext, aForFrame,
displayData->mAppearance, aBorderArea, dirty);
return;
}
}
+ // Same coordinate space as aBorderArea
nsRect bgClipArea;
if (aBGClipRect) {
bgClipArea = *aBGClipRect;
}
else {
// The background is rendered over the 'background-clip' area.
bgClipArea = aBorderArea;
// If the border is solid, then clip the background to the padding-box
@@ -1434,24 +1302,18 @@ nsCSSRendering::PaintBackgroundWithSC(ns
if (aColor.mBackgroundClip != NS_STYLE_BG_CLIP_BORDER ||
IsSolidBorder(aBorder)) {
nsMargin border = aForFrame->GetUsedBorder();
aForFrame->ApplySkipSides(border);
bgClipArea.Deflate(border);
}
}
- nsIDeviceContext *dc = aPresContext->DeviceContext();
gfxContext *ctx = aRenderingContext.ThebesContext();
- // Snap bgClipArea to device pixel boundaries. (We have to snap
- // bgOriginArea below; if we don't do this as well then we could make
- // incorrect decisions about various optimizations.)
- PixelSnapRectangle(ctx, dc, bgClipArea);
-
// The actual dirty rect is the intersection of the 'background-clip'
// area and the dirty rect we were given
nsRect dirtyRect;
if (!dirtyRect.IntersectRect(bgClipArea, aDirtyRect)) {
// Nothing to paint
return;
}
@@ -1485,66 +1347,63 @@ nsCSSRendering::PaintBackgroundWithSC(ns
image->GetWidth(&imageSize.width);
image->GetHeight(&imageSize.height);
imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageSize.width);
imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageSize.height);
req = nsnull;
- nsRect bgOriginArea;
+ // relative to aBorderArea
+ nsRect bgOriginRect;
nsIAtom* frameType = aForFrame->GetType();
+ nsIFrame* geometryFrame = aForFrame;
if (frameType == nsGkAtoms::inlineFrame ||
frameType == nsGkAtoms::positionedInlineFrame) {
switch (aColor.mBackgroundInlinePolicy) {
case NS_STYLE_BG_INLINE_POLICY_EACH_BOX:
- bgOriginArea = aBorderArea;
+ bgOriginRect = nsRect(nsPoint(0,0), aBorderArea.Size());
break;
case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX:
- bgOriginArea = gInlineBGData->GetBoundingRect(aForFrame) +
- aBorderArea.TopLeft();
+ bgOriginRect = gInlineBGData->GetBoundingRect(aForFrame);
break;
default:
NS_ERROR("Unknown background-inline-policy value! "
"Please, teach me what to do.");
case NS_STYLE_BG_INLINE_POLICY_CONTINUOUS:
- bgOriginArea = gInlineBGData->GetContinuousRect(aForFrame) +
- aBorderArea.TopLeft();
+ bgOriginRect = gInlineBGData->GetContinuousRect(aForFrame);
break;
}
- }
- else {
- bgOriginArea = aBorderArea;
+ } else if (frameType == nsGkAtoms::canvasFrame) {
+ geometryFrame = aForFrame->GetFirstChild(nsnull);
+ NS_ASSERTION(geometryFrame, "A canvas with a background "
+ "image had no child frame, which is impossible according to CSS. "
+ "Make sure there isn't a background image specified on the "
+ "|:viewport| pseudo-element in |html.css|.");
+ bgOriginRect = geometryFrame->GetRect();
+ } else {
+ bgOriginRect = nsRect(nsPoint(0,0), aBorderArea.Size());
}
// Background images are tiled over the 'background-clip' area
// but the origin of the tiling is based on the 'background-origin' area
if (aColor.mBackgroundOrigin != NS_STYLE_BG_ORIGIN_BORDER) {
- nsMargin border = aForFrame->GetUsedBorder();
- aForFrame->ApplySkipSides(border);
- bgOriginArea.Deflate(border);
+ nsMargin border = geometryFrame->GetUsedBorder();
+ geometryFrame->ApplySkipSides(border);
+ bgOriginRect.Deflate(border);
if (aColor.mBackgroundOrigin != NS_STYLE_BG_ORIGIN_PADDING) {
- nsMargin padding = aForFrame->GetUsedPadding();
- aForFrame->ApplySkipSides(padding);
- bgOriginArea.Deflate(padding);
+ nsMargin padding = geometryFrame->GetUsedPadding();
+ geometryFrame->ApplySkipSides(padding);
+ bgOriginRect.Deflate(padding);
NS_ASSERTION(aColor.mBackgroundOrigin == NS_STYLE_BG_ORIGIN_CONTENT,
"unknown background-origin value");
}
}
- // Snap bgOriginArea to device pixel boundaries to avoid variations in
- // tiling when the subpixel position of the element changes.
- PixelSnapRectangle(ctx, dc, bgOriginArea);
-
- // Based on the repeat setting, compute how many tiles we should
- // lay down for each axis. The value computed is the maximum based
- // on the dirty rect before accounting for the background-position.
- nscoord tileWidth = imageSize.width;
- nscoord tileHeight = imageSize.height;
PRBool needBackgroundColor = NS_GET_A(aColor.mBackgroundColor) > 0;
PRIntn repeat = aColor.mBackgroundRepeat;
switch (repeat) {
case NS_STYLE_BG_REPEAT_X:
break;
case NS_STYLE_BG_REPEAT_Y:
break;
@@ -1577,31 +1436,21 @@ nsCSSRendering::PaintBackgroundWithSC(ns
}
// The background color is rendered over the 'background-clip' area
if (needBackgroundColor) {
PaintBackgroundColor(aPresContext, aRenderingContext, aForFrame, bgClipArea,
aColor, aBorder, canDrawBackgroundColor);
}
- if ((tileWidth == 0) || (tileHeight == 0) || dirtyRect.IsEmpty()) {
- // Nothing left to paint
- return;
- }
-
- nsPoint borderAreaOriginSnapped = aBorderArea.TopLeft();
- PixelSnapPoint(ctx, dc, borderAreaOriginSnapped);
-
// Compute the anchor point.
//
- // When tiling, the anchor coordinate values will be negative offsets
- // from the background-origin area.
-
- // relative to the origin of aForFrame
- nsPoint anchor;
+ // relative to aBorderArea.TopLeft() (which is where the top-left
+ // of aForFrame's border-box will be rendered)
+ nsPoint imageTopLeft, anchor;
if (NS_STYLE_BG_ATTACHMENT_FIXED == aColor.mBackgroundAttachment) {
// If it's a fixed background attachment, then the image is placed
// relative to the viewport, which is the area of the root frame
// in a screen context or the page content frame in a print context.
// Remember that we've drawn position-varying content in this prescontext
aPresContext->SetRenderedPositionVaryingContent();
@@ -1613,275 +1462,82 @@ nsCSSRendering::PaintBackgroundWithSC(ns
pageContentFrame =
nsLayoutUtils::GetClosestFrameOfType(aForFrame, nsGkAtoms::pageContentFrame);
if (pageContentFrame) {
topFrame = pageContentFrame;
}
// else this is an embedded shell and its root frame is what we want
}
- nsRect viewportArea = topFrame->GetRect();
+ nsRect viewportArea(nsPoint(0, 0), topFrame->GetSize());
if (!pageContentFrame) {
// Subtract the size of scrollbars.
nsIScrollableFrame* scrollableFrame =
aPresContext->PresShell()->GetRootScrollFrameAsScrollable();
if (scrollableFrame) {
nsMargin scrollbars = scrollableFrame->GetActualScrollbarSizes();
viewportArea.Deflate(scrollbars);
}
}
// Get the anchor point, relative to the viewport.
- ComputeBackgroundAnchorPoint(aColor, viewportArea, viewportArea, tileWidth, tileHeight, anchor);
+ ComputeBackgroundAnchorPoint(aColor, viewportArea.Size(), imageSize,
+ &imageTopLeft, &anchor);
// Convert the anchor point from viewport coordinates to aForFrame
// coordinates.
- anchor -= aForFrame->GetOffsetTo(topFrame);
+ nsPoint offset = viewportArea.TopLeft() - aForFrame->GetOffsetTo(topFrame);
+ imageTopLeft += offset;
+ anchor += offset;
} else {
- if (frameType == nsGkAtoms::canvasFrame) {
- // If the frame is the canvas, the image is placed relative to
- // the root element's (first) frame (see bug 46446)
- nsRect firstRootElementFrameArea;
- nsIFrame* firstRootElementFrame = aForFrame->GetFirstChild(nsnull);
- NS_ASSERTION(firstRootElementFrame, "A canvas with a background "
- "image had no child frame, which is impossible according to CSS. "
- "Make sure there isn't a background image specified on the "
- "|:viewport| pseudo-element in |html.css|.");
-
- // temporary null check -- see bug 97226
- if (firstRootElementFrame) {
- firstRootElementFrameArea = firstRootElementFrame->GetRect();
-
- // Take the border out of the frame's rect
- const nsStyleBorder* borderStyle = firstRootElementFrame->GetStyleBorder();
- firstRootElementFrameArea.Deflate(borderStyle->GetActualBorder());
-
- // Get the anchor point
- ComputeBackgroundAnchorPoint(aColor, firstRootElementFrameArea +
- aBorderArea.TopLeft(), bgClipArea, tileWidth, tileHeight, anchor);
- } else {
- ComputeBackgroundAnchorPoint(aColor, bgOriginArea, bgClipArea, tileWidth, tileHeight, anchor);
- }
- } else {
- // Otherwise, it is the normal case, and the background is
- // simply placed relative to the frame's background-clip area
- ComputeBackgroundAnchorPoint(aColor, bgOriginArea, bgClipArea, tileWidth, tileHeight, anchor);
- }
-
- // For scrolling attachment, the anchor is within the 'background-clip'
- anchor.x += bgClipArea.x - borderAreaOriginSnapped.x;
- anchor.y += bgClipArea.y - borderAreaOriginSnapped.y;
+ ComputeBackgroundAnchorPoint(aColor, bgOriginRect.Size(), imageSize,
+ &imageTopLeft, &anchor);
+ imageTopLeft += bgOriginRect.TopLeft();
+ anchor += bgOriginRect.TopLeft();
}
- // Pixel-snap the anchor point so that we don't end up with blurry
- // images due to subpixel positions. But round 0.5 down rather than
- // up, since that's what we've always done. (And do that by just
- // snapping the negative of the point.)
- anchor.x = -anchor.x; anchor.y = -anchor.y;
- PixelSnapPoint(ctx, dc, anchor);
- anchor.x = -anchor.x; anchor.y = -anchor.y;
-
ctx->Save();
- nscoord appUnitsPerPixel = aPresContext->DevPixelsToAppUnits(1);
-
- ctx->NewPath();
- ctx->Rectangle(RectToGfxRect(dirtyRect, appUnitsPerPixel), PR_TRUE);
- ctx->Clip();
-
nscoord borderRadii[8];
PRBool haveRadius = GetBorderRadiusTwips(aBorder.mBorderRadius,
aForFrame->GetSize().width,
borderRadii);
-
if (haveRadius) {
+ nscoord appUnitsPerPixel = aPresContext->DevPixelsToAppUnits(1);
gfxCornerSizes radii;
ComputePixelRadii(borderRadii, bgClipArea,
aForFrame ? aForFrame->GetSkipSides() : 0,
appUnitsPerPixel, &radii);
gfxRect oRect(RectToGfxRect(bgClipArea, appUnitsPerPixel));
oRect.Round();
oRect.Condition();
ctx->NewPath();
ctx->RoundedRectangle(oRect, radii);
ctx->Clip();
- }
-
- // Compute the x and y starting points and limits for tiling
-
- /* An Overview Of The Following Logic
-
- A........ . . . . . . . . . . . . . .
- : +---:-------.-------.-------.---- /|\
- : | : . . . | nh
- :.......: . . . x . . . . . . . . . . \|/
- . | . . . .
- . | . . ########### .
- . . . . . . . . . .#. . . . .#. . . .
- . | . . ########### . /|\
- . | . . . . | h
- . . | . . . . . . . . . . . . . z . . \|/
- . | . . . .
- |<-----nw------>| |<--w-->|
-
- ---- = the background clip area edge. The painting is done within
- to this area. If the background is positioned relative to the
- viewport ('fixed') then this is the viewport edge.
-
- .... = the primary tile.
-
- . . = the other tiles.
-
- #### = the dirtyRect. This is the minimum region we want to cover.
-
- A = The anchor point. This is the point at which the tile should
- start. Always negative or zero.
-
- x = x0 and y0 in the code. The point at which tiling must start
- so that the fewest tiles are laid out while completely
- covering the dirtyRect area.
-
- z = x1 and y1 in the code. The point at which tiling must end so
- that the fewest tiles are laid out while completely covering
- the dirtyRect area.
-
- w = the width of the tile (tileWidth).
-
- h = the height of the tile (tileHeight).
-
- n = the number of whole tiles that fit between 'A' and 'x'.
- (the vertical n and the horizontal n are different)
-
-
- Therefore,
-
- x0 = bgClipArea.x + anchor.x + n * tileWidth;
-
- ...where n is an integer greater or equal to 0 fitting:
-
- n * tileWidth <=
- dirtyRect.x - (bgClipArea.x + anchor.x) <=
- (n+1) * tileWidth
-
- ...i.e.,
+ }
- n <= (dirtyRect.x - (bgClipArea.x + anchor.x)) / tileWidth < n + 1
-
- ...which, treating the division as an integer divide rounding down, gives:
-
- n = (dirtyRect.x - (bgClipArea.x + anchor.x)) / tileWidth
-
- Substituting into the original expression for x0:
-
- x0 = bgClipArea.x + anchor.x +
- ((dirtyRect.x - (bgClipArea.x + anchor.x)) / tileWidth) *
- tileWidth;
-
- From this x1 is determined,
-
- x1 = x0 + m * tileWidth;
-
- ...where m is an integer greater than 0 fitting:
-
- (m - 1) * tileWidth <
- dirtyRect.x + dirtyRect.width - x0 <=
- m * tileWidth
-
- ...i.e.,
-
- m - 1 < (dirtyRect.x + dirtyRect.width - x0) / tileWidth <= m
-
- ...which, treating the division as an integer divide, and making it
- round up, gives:
-
- m = (dirtyRect.x + dirtyRect.width - x0 + tileWidth - 1) / tileWidth
-
- Substituting into the original expression for x1:
-
- x1 = x0 + ((dirtyRect.x + dirtyRect.width - x0 + tileWidth - 1) /
- tileWidth) * tileWidth
-
- The vertical case is analogous. If the background is fixed, then
- bgClipArea.x and bgClipArea.y are set to zero when finding the parent
- viewport, above.
-
- */
-
- // relative to aBorderArea.TopLeft()
- // ... but pixel-snapped, so that it comes out correctly relative to
- // all the other pixel-snapped things
- nsRect tileRect(anchor, nsSize(tileWidth, tileHeight));
- // Whether we take the single-image path or the tile path should not
- // depend on the dirty rect. So decide now which path to take. We
- // can take the single image path if the anchored image tile
- // contains the total background area.
- PRBool useSingleImagePath =
- tileRect.Contains(bgClipArea - borderAreaOriginSnapped);
-
+ nsRect destArea(imageTopLeft + aBorderArea.TopLeft(), imageSize);
+ nsRect fillArea;
+ fillArea.IntersectRect(destArea, bgClipArea);
if (repeat & NS_STYLE_BG_REPEAT_X) {
- // When tiling in the x direction, adjust the starting position of the
- // tile to account for dirtyRect.x. When tiling in x, the anchor.x value
- // will be a negative value used to adjust the starting coordinate.
- nscoord x0 = FindTileStart(dirtyRect.x - borderAreaOriginSnapped.x, anchor.x, tileWidth);
- nscoord x1 = FindTileEnd(dirtyRect.XMost() - borderAreaOriginSnapped.x, anchor.x, tileWidth);
- tileRect.x = x0;
- tileRect.width = x1 - x0;
+ fillArea.x = bgClipArea.x;
+ fillArea.width = bgClipArea.width;
}
if (repeat & NS_STYLE_BG_REPEAT_Y) {
- // When tiling in the y direction, adjust the starting position of the
- // tile to account for dirtyRect.y. When tiling in y, the anchor.y value
- // will be a negative value used to adjust the starting coordinate.
- nscoord y0 = FindTileStart(dirtyRect.y - borderAreaOriginSnapped.y, anchor.y, tileHeight);
- nscoord y1 = FindTileEnd(dirtyRect.YMost() - borderAreaOriginSnapped.y, anchor.y, tileHeight);
- tileRect.y = y0;
- tileRect.height = y1 - y0;
+ fillArea.y = bgClipArea.y;
+ fillArea.height = bgClipArea.height;
}
- // Take the intersection again to paint only the required area.
- nsRect absTileRect = tileRect + borderAreaOriginSnapped;
-
- nsRect drawRect;
- if (drawRect.IntersectRect(absTileRect, dirtyRect)) {
- // Note that due to the way FindTileStart works we're guaranteed
- // that drawRect overlaps the top-left-most tile when repeating.
- NS_ASSERTION(drawRect.x >= absTileRect.x && drawRect.y >= absTileRect.y,
- "Bogus intersection");
- NS_ASSERTION(drawRect.x < absTileRect.x + tileWidth,
- "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();
- // Compute 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 - borderAreaOriginSnapped - tileRect.TopLeft();
- if (useSingleImagePath) {
- NS_ASSERTION(sourceRect.XMost() <= tileWidth && sourceRect.YMost() <= tileHeight,
- "We shouldn't need to tile here");
- // The entire drawRect is contained inside a single tile; just
- // draw the corresponding part of the image once.
- nsLayoutUtils::DrawImage(&aRenderingContext, image,
- destRect, drawRect, &subimageRect);
- } else {
- // Note that the subimage is in tile space so it may cover
- // multiple tiles of the image.
- subimageRect.ScaleRoundOutInverse(nsIDeviceContext::AppUnitsPerCSSPixel());
- aRenderingContext.DrawTile(image, absTileRect.x, absTileRect.y,
- &drawRect, &subimageRect);
- }
- }
+ nsLayoutUtils::DrawImage(&aRenderingContext, image,
+ destArea, fillArea, anchor + aBorderArea.TopLeft(), dirtyRect);
ctx->Restore();
-
}
static void
DrawBorderImage(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nsIFrame* aForFrame, const nsRect& aBorderArea,
const nsStyleBorder& aBorderStyle)
{
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2657,160 +2657,195 @@ nsLayoutUtils::GetClosestLayer(nsIFrame*
layer->GetParent()->GetType() == nsGkAtoms::scrollFrame))
break;
}
if (layer)
return layer;
return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();
}
+static gfxPoint
+MapToFloatImagePixels(const nsIntSize& aSize,
+ const nsRect& aDest, const nsPoint& aPt)
+{
+ return gfxPoint((gfxFloat(aPt.x - aDest.x)*aSize.width)/aDest.width,
+ (gfxFloat(aPt.y - aDest.y)*aSize.height)/aDest.height);
+}
+
/* static */ nsresult
nsLayoutUtils::DrawImage(nsIRenderingContext* aRenderingContext,
- imgIContainer* aImage,
- const nsRect& aDestRect,
- const nsRect& aDirtyRect,
- const nsRect* aSourceRect)
+ imgIContainer* aImage,
+ const nsRect& aDest,
+ const nsRect& aFill,
+ const nsPoint& aAnchor,
+ const nsRect& aDirty)
{
- nsRect dirtyRect;
- dirtyRect.IntersectRect(aDirtyRect, aDestRect);
- if (dirtyRect.IsEmpty())
+ if (aDest.IsEmpty())
return NS_OK;
+ nsCOMPtr<nsIDeviceContext> dc;
+ aRenderingContext->GetDeviceContext(*getter_AddRefs(dc));
+ gfxFloat appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
+ gfxContext *ctx = aRenderingContext->ThebesContext();
+
+ // Compute the pixel-snapped area that should be drawn
+ gfxRect fill(aFill.x/appUnitsPerDevPixel,
+ aFill.y/appUnitsPerDevPixel,
+ aFill.width/appUnitsPerDevPixel,
+ aFill.height/appUnitsPerDevPixel);
+ PRBool ignoreScale = PR_FALSE;
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ ignoreScale = PR_TRUE;
+#endif
+ PRBool didSnap = ctx->UserToDevicePixelSnapped(fill, ignoreScale);
+
+ // Compute dirty rect in gfx space
+ gfxRect dirty(aDirty.x/appUnitsPerDevPixel,
+ aDirty.y/appUnitsPerDevPixel,
+ aDirty.width/appUnitsPerDevPixel,
+ aDirty.height/appUnitsPerDevPixel);
+
nsCOMPtr<gfxIImageFrame> imgFrame;
aImage->GetCurrentFrame(getter_AddRefs(imgFrame));
if (!imgFrame) return NS_ERROR_FAILURE;
nsCOMPtr<nsIImage> img(do_GetInterface(imgFrame));
if (!img) return NS_ERROR_FAILURE;
- // twSrcRect is always in appunits (twips),
- // and has nothing to do with the current transform (it's a region
- // of the image)
- gfxRect pxSrc;
- if (aSourceRect) {
- pxSrc.pos.x = nsIDeviceContext::AppUnitsToGfxCSSPixels(aSourceRect->x);
- pxSrc.pos.y = nsIDeviceContext::AppUnitsToGfxCSSPixels(aSourceRect->y);
- pxSrc.size.width = nsIDeviceContext::AppUnitsToGfxCSSPixels(aSourceRect->width);
- pxSrc.size.height = nsIDeviceContext::AppUnitsToGfxCSSPixels(aSourceRect->height);
- } 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);
+ nsIntSize imageSize;
+ aImage->GetWidth(&imageSize.width);
+ aImage->GetHeight(&imageSize.height);
+ if (imageSize.width == 0 || imageSize.height == 0)
+ return NS_OK;
+
+ // Compute the set of pixels that would be sampled by an ideal rendering
+ gfxPoint subimageTopLeft =
+ MapToFloatImagePixels(imageSize, aDest, aFill.TopLeft());
+ gfxPoint subimageBottomRight =
+ MapToFloatImagePixels(imageSize, aDest, aFill.BottomRight());
+ nsIntRect intSubimage;
+ intSubimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
+ NSToIntFloor(subimageTopLeft.y));
+ intSubimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - intSubimage.x,
+ NSToIntCeil(subimageBottomRight.y) - intSubimage.y);
+
+ // Compute the anchor point and compute final fill rect.
+ // This code assumes that pixel-based devices have one pixel per
+ // device unit!
+ gfxPoint anchorPoint(aAnchor.x/appUnitsPerDevPixel,
+ aAnchor.y/appUnitsPerDevPixel);
+ gfxMatrix currentMatrix = ctx->CurrentMatrix();
+ gfxRect finalFillRect = fill;
+ if (didSnap) {
+ ctx->UserToDevicePixelSnapped(anchorPoint, ignoreScale);
+
+ // This form of Transform is safe to call since non-axis-aligned
+ // transforms wouldn't be snapped.
+ dirty = currentMatrix.Transform(dirty);
+ dirty.RoundOut();
+ finalFillRect = fill.Intersect(dirty);
+ if (finalFillRect.IsEmpty())
+ return NS_OK;
+
+ ctx->IdentityMatrix();
}
- gfxRect pxSubimage = pxSrc;
-
- nsCOMPtr<nsIDeviceContext> dc;
- aRenderingContext->GetDeviceContext(*getter_AddRefs(dc));
-
- 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.
-
- // Snap the edges of where layout wants the image to the nearest
- // pixel, but then convert back to gfxFloats for the rest of the math.
- gfxRect pxDest;
- {
- pxDest.pos.x = dc->AppUnitsToGfxUnits(aDestRect.x);
- pxDest.pos.y = dc->AppUnitsToGfxUnits(aDestRect.y);
- pxDest.size.width = dc->AppUnitsToGfxUnits(aDestRect.width);
- pxDest.size.height = dc->AppUnitsToGfxUnits(aDestRect.height);
- if (ctx->UserToDevicePixelSnapped(pxDest))
- pxDest = ctx->DeviceToUser(pxDest);
- }
-
- // And likewise for the dirty rect. (Is should be OK to round to
- // nearest rather than outwards, since any dirty rects coming from the
- // OS should be on pixel boundaries; the rest is other things it's
- // been intersected with, and we should be rounding those consistently.)
- gfxRect pxDirty;
- {
- pxDirty.pos.x = dc->AppUnitsToGfxUnits(dirtyRect.x);
- pxDirty.pos.y = dc->AppUnitsToGfxUnits(dirtyRect.y);
- pxDirty.size.width = dc->AppUnitsToGfxUnits(dirtyRect.width);
- pxDirty.size.height = dc->AppUnitsToGfxUnits(dirtyRect.height);
- if (ctx->UserToDevicePixelSnapped(pxDirty))
- pxDirty = ctx->DeviceToUser(pxDirty);
- }
-
- // Reduce the src rect to what's needed for the dirty rect.
- if (pxDirty.size.width != pxDest.size.width) {
- const gfxFloat ratio = pxSrc.size.width / pxDest.size.width;
- pxSrc.pos.x += (pxDirty.pos.x - pxDest.pos.x) * ratio;
- pxSrc.size.width = pxDirty.size.width * ratio;
+ // If we're not snapping, then we ignore the dirty rect. It's hard
+ // to correctly use it with arbitrary transforms --- it really *has*
+ // to be aligned perfectly with pixel boundaries or the choice of
+ // dirty rect will affect the values of rendered pixels.
+
+ gfxPoint imageSpaceAnchorPoint =
+ MapToFloatImagePixels(imageSize, aDest, aAnchor);
+ imageSpaceAnchorPoint.Round();
+ gfxFloat scaleX = imageSize.width*appUnitsPerDevPixel/aDest.width;
+ gfxFloat scaleY = imageSize.height*appUnitsPerDevPixel/aDest.height;
+ if (didSnap) {
+ // ctx now has the identity matrix, so we need to adjust our
+ // scales to match
+ scaleX /= currentMatrix.xx;
+ scaleY /= currentMatrix.yy;
}
- if (pxDirty.size.height != pxDest.size.height) {
- const gfxFloat ratio = pxSrc.size.height / pxDest.size.height;
- pxSrc.pos.y += (pxDirty.pos.y - pxDest.pos.y) * ratio;
- pxSrc.size.height = pxDirty.size.height * ratio;
- }
-
- // If we were asked to draw a 0-width or 0-height image,
- // as either the src or dst, just bail; we can't do anything
- // useful with this.
- if (pxSrc.IsEmpty() || pxDirty.IsEmpty())
- {
- return NS_OK;
+ gfxFloat translateX = imageSpaceAnchorPoint.x - anchorPoint.x*scaleX;
+ gfxFloat translateY = imageSpaceAnchorPoint.y - anchorPoint.y*scaleY;
+ gfxMatrix transform(scaleX, 0, 0, scaleY, translateX, translateY);
+
+ nsIntRect innerRect;
+ imgFrame->GetRect(innerRect);
+ nsIntMargin padding(innerRect.x, innerRect.y,
+ imageSize.width - innerRect.XMost(), imageSize.height - innerRect.YMost());
+ img->Draw(ctx, transform, finalFillRect, padding, intSubimage);
+ ctx->SetMatrix(currentMatrix);
+ return NS_OK;
+}
+
+/* static */ nsresult
+nsLayoutUtils::DrawSingleUnscaledImage(nsIRenderingContext* aRenderingContext,
+ imgIContainer* aImage,
+ const nsPoint& aDest,
+ const nsRect& aDirty,
+ const nsRect* aSourceArea)
+{
+ nsIntSize size;
+ aImage->GetWidth(&size.width);
+ aImage->GetHeight(&size.height);
+
+ nscoord appUnitsPerCSSPixel = nsIDeviceContext::AppUnitsPerCSSPixel();
+ nsRect source;
+ if (aSourceArea) {
+ source = *aSourceArea;
+ } else {
+ source.SizeTo(size.width*appUnitsPerCSSPixel, size.height*appUnitsPerCSSPixel);
}
- // For Bug 87819
- // imgFrame may want image to start at different position, so adjust
- nsIntRect pxImgFrameRect;
- imgFrame->GetRect(pxImgFrameRect);
-
- if (pxImgFrameRect.x > 0) {
- 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;
- pxSrc.size.width += pxSrc.pos.x;
- pxDirty.size.width += scaled_x;
- if (pxSrc.size.width <= 0.0 || pxDirty.size.width <= 0.0)
- return NS_OK;
- pxSrc.pos.x = 0.0;
- }
- }
- if (pxSrc.pos.x > gfxFloat(pxImgFrameRect.width)) {
+ nsRect dest(aDest - source.TopLeft(),
+ nsSize(size.width*appUnitsPerCSSPixel, size.height*appUnitsPerCSSPixel));
+ nsRect fill(aDest, source.Size());
+ return DrawImage(aRenderingContext, aImage, dest, fill, aDest, aDirty);
+}
+
+/* static */ nsresult
+nsLayoutUtils::DrawSingleImage(nsIRenderingContext* aRenderingContext,
+ imgIContainer* aImage,
+ const nsRect& aDest,
+ const nsRect& aDirty,
+ const nsRect* aSourceArea)
+{
+ nsIntSize size;
+ aImage->GetWidth(&size.width);
+ aImage->GetHeight(&size.height);
+
+ if (size.width == 0 || size.height == 0)
return NS_OK;
+
+ nscoord appUnitsPerCSSPixel = nsIDeviceContext::AppUnitsPerCSSPixel();
+ nsRect source;
+ if (aSourceArea) {
+ source = *aSourceArea;
+ } else {
+ source.SizeTo(size.width*appUnitsPerCSSPixel, size.height*appUnitsPerCSSPixel);
}
- if (pxImgFrameRect.y > 0) {
- 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;
- pxSrc.size.height += pxSrc.pos.y;
- pxDirty.size.height += scaled_y;
- if (pxSrc.size.height <= 0.0 || pxDirty.size.height <= 0.0)
- return NS_OK;
- pxSrc.pos.y = 0.0;
- }
- }
- if (pxSrc.pos.y > gfxFloat(pxImgFrameRect.height)) {
- return NS_OK;
- }
-
- return img->Draw(*aRenderingContext, pxSrc, pxSubimage, pxDirty);
+ nsRect dest = GetWholeImageDestination(size, source, aDest);
+ return DrawImage(aRenderingContext, aImage, dest, aDest, aDest.TopLeft(), aDirty);
+}
+
+/* static */ nsRect
+nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize,
+ const nsRect& aImageSourceArea,
+ const nsRect& aDestArea)
+{
+ double scaleX = double(aDestArea.width)/aImageSourceArea.width;
+ double scaleY = double(aDestArea.height)/aImageSourceArea.height;
+ nscoord destOffsetX = NSToCoordRound(aImageSourceArea.x*scaleX);
+ nscoord destOffsetY = NSToCoordRound(aImageSourceArea.y*scaleY);
+ nscoord appUnitsPerCSSPixel = nsIDeviceContext::AppUnitsPerCSSPixel();
+ nscoord wholeSizeX = NSToCoordRound(aWholeImageSize.width*appUnitsPerCSSPixel*scaleX);
+ nscoord wholeSizeY = NSToCoordRound(aWholeImageSize.height*appUnitsPerCSSPixel*scaleY);
+ return nsRect(aDestArea.TopLeft() - nsPoint(destOffsetX, destOffsetY),
+ nsSize(wholeSizeX, wholeSizeY));
}
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
@@ -789,37 +789,86 @@ public:
* definition. This is the nearest frame that is either positioned or scrolled
* (the child of a scroll frame). In Gecko terms, it's approximately
* equivalent to having a view, at least for simple HTML. However, views are
* going away, so this is a cleaner definition.
*/
static nsIFrame* GetClosestLayer(nsIFrame* aFrame);
/**
- * Draw a single image.
+ * Draw an image.
+ * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
+ * @param aRenderingContext Where to draw the image, set up with an
+ * appropriate scale and transform for drawing in
+ * app units.
+ * @param aImage The image.
+ * @param aDest Where one copy of the image should mapped to.
+ * @param aFill The area to be filled with copies of the
+ * image.
+ * @param aAnchor A point in aFill which we will ensure is
+ * pixel-aligned in the output.
+ * @param aDirty Pixels outside this area may be skipped.
+ */
+ static nsresult DrawImage(nsIRenderingContext* aRenderingContext,
+ imgIContainer* aImage,
+ const nsRect& aDest,
+ const nsRect& aFill,
+ const nsPoint& aAnchor,
+ const nsRect& aDirty);
+
+ /**
+ * Draw a whole image without scaling or tiling.
+ *
* @param aRenderingContext Where to draw the image, set up with an
* appropriate scale and transform for drawing in
- * app units (aDestRect).
+ * app units.
+ * @param aImage The image.
+ * @param aDest The top-left where the image should be drawn
+ * @param aDirty Pixels outside this area may be skipped.
+ * @param aSourceArea If non-null, this area is extracted from
+ * the image and drawn at aDest. It's
+ * in appunits. For best results it should
+ * be aligned with image pixels.
+ */
+ static nsresult DrawSingleUnscaledImage(nsIRenderingContext* aRenderingContext,
+ imgIContainer* aImage,
+ const nsPoint& aDest,
+ const nsRect& aDirty,
+ const nsRect* aSourceArea = nsnull);
+
+ /**
+ * Draw a whole image without tiling.
+ *
+ * @param aRenderingContext Where to draw the image, set up with an
+ * appropriate scale and transform for drawing in
+ * app units.
* @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 (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).
+ * @param aDest The area that the image should fill
+ * @param aDirty Pixels outside this area may be skipped.
+ * @param aSourceArea If non-null, this area is extracted from
+ * the image and drawn in aDest. It's
+ * in appunits. For best results it should
+ * be aligned with image pixels.
*/
- static nsresult DrawImage(nsIRenderingContext* aRenderingContext,
- imgIContainer* aImage,
- const nsRect& aDestRect,
- const nsRect& aDirtyRect,
- const nsRect* aSourceRect = nsnull);
+ static nsresult DrawSingleImage(nsIRenderingContext* aRenderingContext,
+ imgIContainer* aImage,
+ const nsRect& aDest,
+ const nsRect& aDirty,
+ const nsRect* aSourceArea = nsnull);
+
+ /**
+ * Given a source area of an image (in appunits) and a destination area
+ * that we want to map that source area too, computes the area that
+ * would be covered by the whole image. This is useful for passing to
+ * the aDest parameter of DrawImage, when we want to draw a subimage
+ * of an overall image.
+ */
+ static nsRect GetWholeImageDestination(const nsIntSize& aWholeImageSize,
+ const nsRect& aImageSourceArea,
+ const nsRect& aDestArea);
/**
* Set the font on aRC based on the style in aSC
*/
static void SetFontFromStyle(nsIRenderingContext* aRC, nsStyleContext* aSC);
/**
* Determine if any corner radius is of nonzero size
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -234,18 +234,18 @@ nsBulletFrame::PaintBullet(nsIRenderingC
if (status & imgIRequest::STATUS_LOAD_COMPLETE &&
!(status & imgIRequest::STATUS_ERROR)) {
nsCOMPtr<imgIContainer> imageCon;
mImageRequest->GetImage(getter_AddRefs(imageCon));
if (imageCon) {
nsRect dest(mPadding.left, mPadding.top,
mRect.width - (mPadding.left + mPadding.right),
mRect.height - (mPadding.top + mPadding.bottom));
- nsLayoutUtils::DrawImage(&aRenderingContext, imageCon,
- dest + aPt, aDirtyRect);
+ nsLayoutUtils::DrawSingleImage(&aRenderingContext, imageCon,
+ dest + aPt, aDirtyRect);
return;
}
}
}
const nsStyleColor* myColor = GetStyleColor();
nsCOMPtr<nsIFontMetrics> fm;
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1064,17 +1064,17 @@ nsImageFrame::DisplayAltFeedback(nsIRend
if (aRequest) {
aRequest->GetImage(getter_AddRefs(imgCon));
}
if (imgCon) {
// draw it
nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
inner.XMost() - size : inner.x,
inner.y, size, size);
- nsLayoutUtils::DrawImage(&aRenderingContext, imgCon, dest, aDirtyRect);
+ nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon, dest, aDirtyRect);
iconUsed = PR_TRUE;
}
}
// if we could not draw the image, then just draw some graffiti
if (!iconUsed) {
nscolor oldColor;
nscoord iconXPos = (vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
@@ -1169,23 +1169,20 @@ nsDisplayImage::Paint(nsDisplayListBuild
void
nsImageFrame::PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect, imgIContainer* aImage)
{
// Render the image into our content area (the area inside
// the borders and padding)
NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
nsRect inner = GetInnerArea() + aPt;
- nsRect clip;
- clip.IntersectRect(inner, aDirtyRect);
-
nsRect dest(inner.TopLeft(), mComputedSize);
dest.y -= GetContinuationOffset();
- nsLayoutUtils::DrawImage(&aRenderingContext, aImage, dest, clip);
+ nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage, dest, aDirtyRect);
nsPresContext* presContext = PresContext();
nsImageMap* map = GetImageMap(presContext);
if (nsnull != map) {
aRenderingContext.PushState();
aRenderingContext.SetColor(NS_RGB(0, 0, 0));
aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
aRenderingContext.Translate(inner.x, inner.y);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/364968-1-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+div { position:absolute; width:7px; height:7px; }
+.r { top:0; left:0; background:red; }
+.g { top:0; left:9px; background:lime; }
+.b { top:9px; left:0; background:blue; }
+.y { top:9px; left:9px; background:yellow; }
+</style>
+</head>
+<body>
+<div style="left:256px; top:0;">
+ <div class="r"></div>
+ <div class="g"></div>
+ <div class="b"></div>
+ <div class="y"></div>
+</div>
+<div style="left:272px; top:0;">
+ <div class="r"></div>
+ <div class="g"></div>
+ <div class="b"></div>
+ <div class="y"></div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/364968-1.xul
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<stack>
+ <hbox flex="1">
+ <spacer width="256"/>
+ <image width="16" height="16" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACAAAAAAgCAYAAACG5JhhAAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH1wUdBDEnzSKcAAAAAXpJREFUeJzt3MEJwDAMBEE5pP+WnSKCMSwzFdxXsGjtmT0AAABA2nL9AwAAQN5zewAAAAAAAAAA8J8AAAAAAAAAAAACBAAAAAAAAAAAECAAAAAAAAAAAIAAAQAAAAAAAAAABAgAAAAAAAAAACBAAAAAAAAAAAAAAQIAAAAAAAAAAAgQAAAAAAAAAABAgAAAAAAAAAAAAAIEAAAAAAAAAAAQIAAAAAAAAAAAgAABAAAAAAAAAAAECAAAAAAAAAAAIEAAAAAAAAAAAAAB75p9ewMAAABw2J51ewIAAABwmA8AAAAAAAAAABAgAAAAAAAAAACAAAEAAAAAAAAAAAQIAAAAAAAAAAAgQAAAAAAAAAAAAAECAAAAAAAAAAAIEAAAAAAAAAAAQIAAAAAAAAAAAAACBAAAAAAAAAAAECAAAAAAAAAAAIAAAQAAAAAAAAAABAgAAAAAAAAAACBAAAAAAAAAAAAAAQIAAAAAAAAAAAgQAAAAAAAAAABAwAdutQVA2w3ITgAAAABJRU5ErkJggg=="/>
+ <image width="16" height="16" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACAAAAAAgCAYAAACG5JhhAAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH1wUdBDEnzSKcAAAAAXpJREFUeJzt3MEJwDAMBEE5pP+WnSKCMSwzFdxXsGjtmT0AAABA2nL9AwAAQN5zewAAAAAAAAAA8J8AAAAAAAAAAAACBAAAAAAAAAAAECAAAAAAAAAAAIAAAQAAAAAAAAAABAgAAAAAAAAAACBAAAAAAAAAAAAAAQIAAAAAAAAAAAgQAAAAAAAAAABAgAAAAAAAAAAAAAIEAAAAAAAAAAAQIAAAAAAAAAAAgAABAAAAAAAAAAAECAAAAAAAAAAAIEAAAAAAAAAAAAAB75p9ewMAAABw2J51ewIAAABwmA8AAAAAAAAAABAgAAAAAAAAAACAAAEAAAAAAAAAAAQIAAAAAAAAAAAgQAAAAAAAAAAAAAECAAAAAAAAAAAIEAAAAAAAAAAAQIAAAAAAAAAAAAACBAAAAAAAAAAAECAAAAAAAAAAAIAAAQAAAAAAAAAABAgAAAAAAAAAACBAAAAAAAAAAAAAAQIAAAAAAAAAAAgQAAAAAAAAAABAwAdutQVA2w3ITgAAAABJRU5ErkJggg=="/>
+ <hbox flex="1"/>
+ </hbox>
+ <!-- overdraw the troublesome junctions between colored boxes -->
+ <spacer left="263" top="0" height="16" width="2" style="background:white;"/>
+ <spacer left="279" top="0" height="16" width="2" style="background:white;"/>
+ <spacer left="256" top="7" height="2" width="32" style="background:white;"/>
+</stack>
+</window>
--- a/layout/reftests/bugs/433640-1-ref.html
+++ b/layout/reftests/bugs/433640-1-ref.html
@@ -14,32 +14,32 @@
body > div { height:100px; }
p { height:20px; }
</style>
</head><body>
<div>
-<div class="cell"><p>31 x 32</p><div style="width:31px; background-position:-17px -16px;" class="image"></div></div>
-<div class="cell"><p>31.1 x 32</p><div style="width:31px; background-position:-17px -16px;" class="image"></div></div>
+<div class="cell"><p>31 x 32</p><div style="width:31px; background-position:-16px -16px;" class="image"></div></div>
+<div class="cell"><p>31.1 x 32</p><div style="width:31px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>31.5 x 32</p><div style="width:32px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>31.8 x 32</p><div style="width:32px; background-position:-16px -16px;" class="image"></div></div>
</div>
<div>
<div class="cell"><p>32 x 32</p><div style="width:32px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>32.1 x 32</p><div style="width:32px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>32.5 x 32</p><div style="width:33px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>32.8 x 32</p><div style="width:33px; background-position:-16px -16px;" class="image"></div></div>
</div>
<div>
-<div class="cell"><p>32 x 31 </p><div style="height:31px; background-position:-16px -17px;" class="image"></div></div>
-<div class="cell"><p>32 x 31.1 </p><div style="height:31px; background-position:-16px -17px;" class="image"></div></div>
+<div class="cell"><p>32 x 31 </p><div style="height:31px; background-position:-16px -16px;" class="image"></div></div>
+<div class="cell"><p>32 x 31.1 </p><div style="height:31px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>32 x 31.5 </p><div style="height:32px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>32 x 31.8 </p><div style="height:32px; background-position:-16px -16px;" class="image"></div></div>
</div>
<div>
<div class="cell"><p>32 x 32 </p><div style="height:32px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>32 x 32.1 </p><div style="height:32px; background-position:-16px -16px;" class="image"></div></div>
<div class="cell"><p>32 x 32.5 </p><div style="height:33px; background-position:-16px -16px;" class="image"></div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1a.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="1.1">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1b.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="1.5">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1c.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="1.7">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1d.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="2.0">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1e.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="0.8">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1f.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="0.67">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1g.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="0.5">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/446100-1h.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html reftest-zoom="0.3">
+<head>
+<style>
+div { margin:1em; }
+/* A 7x7px image, black with a 5x5 transparent box centered in it */
+div.box { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAFklEQVQImWNgYGD4jwdDCCxgQCWxYgBX8hfpeym4dwAAAABJRU5ErkJggg==); }
+/* A 7x5px image, black with a 5x5 transparent box centered in it */
+div.vstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAAFElEQVQImWNgYGD4z4Ad/GcYAEkAw+kJ94z5rSYAAAAASUVORK5CYII=); }
+/* A 5x7px image, black with a 5x5 transparent box centered in it */
+div.hstrip { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAHCAYAAADAp4fuAAAAE0lEQVQImWNgYGD4jwXTD2DYDgDN4Qn3yMcPlwAAAABJRU5ErkJggg==); }
+</style>
+</head>
+<body>
+<div class="box" style="background-position:-1px -1px; width:5px; height:5px;"></div>
+<div class="vstrip" style="background-position:-1px 0px; width:5px; height:22px;"></div>
+<div class="hstrip" style="background-position:0px -1px; width:22px; height:5px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="position:absolute; top:30px; left:30px;
+ background: url(mozilla-banner.gif) no-repeat; width:600px; height:100px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1a.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="margin:30px; border:30px solid rgba(0,0,0,0);
+ background: url(mozilla-banner.gif) no-repeat;
+ -moz-background-origin:border; height:100px;">
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1b.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="border:30px solid rgba(0,0,0,0);
+ background: url(mozilla-banner.gif) no-repeat;
+ -moz-background-origin:padding; height:100px;">
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1c.html
@@ -0,0 +1,4 @@
+<!DOCTYPE HTML>
+<html style="padding:30px; background: url(mozilla-banner.gif) no-repeat;
+ -moz-background-origin:content; height:100px;">
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1d.html
@@ -0,0 +1,4 @@
+<!DOCTYPE HTML>
+<html style="background: url(mozilla-banner.gif) no-repeat;
+ background-position:100% 30px; height:100px; width:630px;">
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1e.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="margin:30px; border:30px solid rgba(0,0,0,0); height:100px;">
+<body style="background: url(mozilla-banner.gif) no-repeat; -moz-background-origin:border;">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1f.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="border:30px solid rgba(0,0,0,0); height:100px;">
+<body style="background: url(mozilla-banner.gif) no-repeat; -moz-background-origin:padding;">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1g.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="padding:30px; height:100px;">
+<body style="background: url(mozilla-banner.gif) no-repeat; -moz-background-origin:content;">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-1h.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="height:100px; width:630px;">
+<body style="background: url(mozilla-banner.gif) no-repeat; background-position:100% 30px;">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-2-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+div { width:198px; overflow:hidden; background: url(mozilla-banner.gif) no-repeat; height:100px; border:1px solid; }
+</style>
+</head>
+<body>
+<div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-2.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+div { width:198.2px; background: url(mozilla-banner.gif) no-repeat; height:100px; border:1px solid; }
+</style>
+</head>
+<body>
+<div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-3-iframe.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body style="margin:0;">
+<img src="mozilla-banner.gif">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-3-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<img src="mozilla-banner.gif" style="margin-left:100px;">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-3.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<iframe style="margin-left:100px; border:none; width:650px; height:150px;" src="458487-3-iframe.html"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-4-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE HTML>
+<html style="background: url(mozilla-banner.gif) no-repeat;">
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-4a.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="margin:30px; border:30px solid rgba(0,0,0,0);
+ background: url(mozilla-banner.gif) no-repeat fixed;
+ -moz-background-origin:border; height:10px;">
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-4b.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="border:30px solid rgba(0,0,0,0);
+ background: url(mozilla-banner.gif) no-repeat fixed;
+ -moz-background-origin:padding; height:10px;">
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-4c.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html style="margin:30px; border:30px solid rgba(0,0,0,0); height:10px;">
+<body style="background: url(mozilla-banner.gif) no-repeat fixed;
+ -moz-background-origin:border;">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-5-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="position:absolute; left:20px; top:20px; height:20px; width:600px;
+ background: url(mozilla-banner.gif) no-repeat;
+ background-position:-20px -20px;">
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-5a.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<body style="margin:0">
+<div style="margin:20px; border:10px solid rgba(0,0,0,0);
+ background: url(mozilla-banner.gif) no-repeat fixed;
+ -moz-background-origin:border; height:0px;">
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/458487-5b.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<body style="margin:0">
+<div style="margin:20px; border:10px solid rgba(0,0,0,0);
+ background: url(mozilla-banner.gif) no-repeat fixed;
+ -moz-background-origin:padding; height:0px;">
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -442,16 +442,17 @@ random-if(MOZ_WIDGET_TOOLKIT=="gtk2") ==
== 363858-6b.html 363858-6-ref.html
== 363874.html 363874-ref.html
== 363874-max-width.html 363874-max-width-ref.html
== 364066-1.html 364066-1-ref.html
== 364318-1.xhtml 364318-1-ref.xhtml
== 364079-1.html 364079-1-ref.html
== 364861-1.html 364861-1-ref.html
== 364862-1.html 364862-1-ref.html
+== 364968-1.xul 364968-1-ref.html
== 365173-1.html 365173-1-ref.html
== 366207-1.xul 366207-1-ref.xul
== 366616-1.xul 366616-1-ref.xul
== 367220-1.html 367220-1-ref.html
== 367247-s-visible.html 367247-s-hidden.html
== 367247-s-hidden.html 367247-s-auto.html
!= 367247-s-auto.html 367247-s-scroll.html
!= 367247-l-visible.html 367247-l-hidden.html
@@ -912,24 +913,47 @@ random == 429849-1.html 429849-1-ref.htm
== 439910.html 439910-ref.html
== 441259-1.html 441259-1-ref.html
fails == 441259-2.html 441259-2-ref.html # bug 441400
== 442542-1.html 442542-1-ref.html
== 444015-1.html 444015-1-ref.html
== 444928-1.html 444928-1-ref.html
== 444928-2.html 444928-2-ref.html
!= 444928-3.html 444928-3-notref.html
+== 446100-1a.html about:blank
+== 446100-1b.html about:blank
+== 446100-1c.html about:blank
+== 446100-1d.html about:blank
+== 446100-1e.html about:blank
+== 446100-1f.html about:blank
+== 446100-1g.html about:blank
+== 446100-1h.html about:blank
# == 448193.html 448193-ref.html # Fails due to 2 small single-pixel differences
# == 448987.html 448987-ref.html # Disabled for now - it needs privileges
== 449171-1.html 449171-ref.html
== 449519-1.html 449519-1-ref.html
# == 449653-1.html 449653-1-ref.html # Disabled for now - it needs privileges
== 450670-1.html 450670-1-ref.html
== 451168-1.html 451168-1-ref.html
== 452964-1.html 452964-1-ref.html
== 454361.html about:blank
== 455105-1.html 455105-ref.html
== 455105-2.html 455105-ref.html
== 455280-1.xhtml 455280-1-ref.xhtml
fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") == 456147.xul 456147-ref.html # bug 456147, but not caused by it
== 456484-1.html 456484-1-ref.html
+== 458487-1a.html 458487-1-ref.html
+== 458487-1b.html 458487-1-ref.html
+== 458487-1c.html 458487-1-ref.html
+== 458487-1d.html 458487-1-ref.html
+== 458487-1e.html 458487-1-ref.html
+== 458487-1f.html 458487-1-ref.html
+== 458487-1g.html 458487-1-ref.html
+== 458487-1h.html 458487-1-ref.html
+== 458487-2.html 458487-2-ref.html
+== 458487-3.html 458487-3-ref.html
+== 458487-4a.html 458487-4-ref.html
+== 458487-4b.html 458487-4-ref.html
+== 458487-4c.html 458487-4-ref.html
+== 458487-5a.html 458487-5-ref.html
+== 458487-5b.html 458487-5-ref.html
== 461266-1.html 461266-1-ref.html
fails == 461512-1.html 461512-1-ref.html # Bug 461512
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -373,18 +373,18 @@ nsImageBoxFrame::PaintImage(nsIRendering
if (!dirty.IntersectRect(aDirtyRect, rect))
return;
nsCOMPtr<imgIContainer> imgCon;
mImageRequest->GetImage(getter_AddRefs(imgCon));
if (imgCon) {
PRBool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
- nsLayoutUtils::DrawImage(&aRenderingContext, imgCon,
- rect, dirty, hasSubRect ? &mSubRect : nsnull);
+ nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
+ rect, dirty, hasSubRect ? &mSubRect : nsnull);
}
}
//
// DidSetStyleContext
//
// When the style context changes, make sure that all of our image is up to date.
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -3366,28 +3366,28 @@ nsTreeBodyFrame::PaintTwisty(PRInt32
twistyRect.Deflate(bp);
imageSize.Deflate(bp);
// Get the image for drawing.
nsCOMPtr<imgIContainer> image;
PRBool useImageRegion = PR_TRUE;
GetImage(aRowIndex, aColumn, PR_TRUE, twistyContext, useImageRegion, getter_AddRefs(image));
if (image) {
- nsRect r(twistyRect.x, twistyRect.y, imageSize.width, imageSize.height);
+ nsPoint pt = twistyRect.TopLeft();
// Center the image. XXX Obey vertical-align style prop?
if (imageSize.height < twistyRect.height) {
- r.y += (twistyRect.height - imageSize.height)/2;
+ pt.y += (twistyRect.height - imageSize.height)/2;
}
// Paint the image.
- nsLayoutUtils::DrawImage(&aRenderingContext, image,
- r, aDirtyRect, &imageSize);
+ nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
+ pt, aDirtyRect, &imageSize);
}
- }
+ }
}
}
void
nsTreeBodyFrame::PaintImage(PRInt32 aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aImageRect,
nsPresContext* aPresContext,
@@ -3485,32 +3485,33 @@ nsTreeBodyFrame::PaintImage(PRInt32
// Deflate destRect for the border and padding.
destRect.Deflate(bp);
// Get the image source rectangle - the rectangle containing the part of
// the image that we are going to display.
// sourceRect will be passed as the aSrcRect argument in the DrawImage method.
nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image);
- // If destRect width/height was adjusted to fit within the cell
- // width/height, we need to make corresponding adjustments to the
- // sourceRect width/height.
- // Here's an explanation. Let's say that the image is 100 pixels tall and
+ // Let's say that the image is 100 pixels tall and
// that the CSS has specified that the destination height should be 50
// pixels tall. Let's say that the cell height is only 20 pixels. So, in
// those 20 visible pixels, we want to see the top 20/50ths of the image.
// So, the sourceRect.height should be 100 * 20 / 50, which is 40 pixels.
// Essentially, we are scaling the image as dictated by the CSS destination
// height and width, and we are then clipping the scaled image by the cell
// width and height.
- nsRect clip;
- clip.IntersectRect(aDirtyRect, destRect);
+ nsIntSize rawImageSize;
+ image->GetWidth(&rawImageSize.width);
+ image->GetHeight(&rawImageSize.height);
+ nsRect wholeImageDest =
+ nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect,
+ nsRect(destRect.TopLeft(), imageDestSize));
+
nsLayoutUtils::DrawImage(&aRenderingContext, image,
- nsRect(destRect.TopLeft(), imageDestSize),
- clip, &sourceRect);
+ wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect);
}
// Update the aRemainingWidth and aCurrX values.
imageRect.Inflate(imageMargin);
aRemainingWidth -= imageRect.width;
aCurrX += imageRect.width;
}
@@ -3648,29 +3649,29 @@ nsTreeBodyFrame::PaintCheckbox(PRInt32
GetBorderPadding(checkboxContext, bp);
checkboxRect.Deflate(bp);
// Get the image for drawing.
nsCOMPtr<imgIContainer> image;
PRBool useImageRegion = PR_TRUE;
GetImage(aRowIndex, aColumn, PR_TRUE, checkboxContext, useImageRegion, getter_AddRefs(image));
if (image) {
- nsRect r(checkboxRect.x, checkboxRect.y, imageSize.width, imageSize.height);
+ nsPoint pt = checkboxRect.TopLeft();
if (imageSize.height < checkboxRect.height) {
- r.y += (checkboxRect.height - imageSize.height)/2;
+ pt.y += (checkboxRect.height - imageSize.height)/2;
}
if (imageSize.width < checkboxRect.width) {
- r.x += (checkboxRect.width - imageSize.width)/2;
+ pt.x += (checkboxRect.width - imageSize.width)/2;
}
// Paint the image.
- nsLayoutUtils::DrawImage(&aRenderingContext, image,
- r, aDirtyRect, &imageSize);
+ nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
+ pt, aDirtyRect, &imageSize);
}
}
void
nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
nsTreeColumn* aColumn,
const nsRect& aProgressMeterRect,
nsPresContext* aPresContext,
@@ -3714,30 +3715,44 @@ nsTreeBodyFrame::PaintProgressMeter(PRIn
intValue = 0;
else if (intValue > 100)
intValue = 100;
meterRect.width = NSToCoordRound((float)intValue / 100 * meterRect.width);
PRBool useImageRegion = PR_TRUE;
nsCOMPtr<imgIContainer> image;
GetImage(aRowIndex, aColumn, PR_TRUE, meterContext, useImageRegion, getter_AddRefs(image));
- if (image)
- aRenderingContext.DrawTile(image, 0, 0, &meterRect, nsnull);
- else
+ if (image) {
+ PRInt32 width, height;
+ image->GetWidth(&width);
+ image->GetHeight(&height);
+ nsSize size(width*nsIDeviceContext::AppUnitsPerCSSPixel(),
+ height*nsIDeviceContext::AppUnitsPerCSSPixel());
+ nsLayoutUtils::DrawImage(&aRenderingContext, image,
+ nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), aDirtyRect);
+ } else {
aRenderingContext.FillRect(meterRect);
+ }
}
else if (state == nsITreeView::PROGRESS_UNDETERMINED) {
// Adjust the rect for its border and padding.
AdjustForBorderPadding(meterContext, meterRect);
PRBool useImageRegion = PR_TRUE;
nsCOMPtr<imgIContainer> image;
GetImage(aRowIndex, aColumn, PR_TRUE, meterContext, useImageRegion, getter_AddRefs(image));
- if (image)
- aRenderingContext.DrawTile(image, 0, 0, &meterRect, nsnull);
+ if (image) {
+ PRInt32 width, height;
+ image->GetWidth(&width);
+ image->GetHeight(&height);
+ nsSize size(width*nsIDeviceContext::AppUnitsPerCSSPixel(),
+ height*nsIDeviceContext::AppUnitsPerCSSPixel());
+ nsLayoutUtils::DrawImage(&aRenderingContext, image,
+ nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), aDirtyRect);
+ }
}
}
void
nsTreeBodyFrame::PaintDropFeedback(const nsRect& aDropFeedbackRect,
nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -554,72 +554,67 @@ nsBaseDragService::DrawDragForImage(nsPr
else {
NS_ASSERTION(aCanvas, "both image and canvas are null");
PRUint32 width, height;
aCanvas->GetSize(&width, &height);
aScreenDragRect->width = width;
aScreenDragRect->height = height;
}
- nsRect srcRect = *aScreenDragRect;
- srcRect.MoveTo(0, 0);
- nsRect destRect = srcRect;
+ nsSize srcSize = aScreenDragRect->Size();
+ nsSize destSize = srcSize;
- if (destRect.width == 0 || destRect.height == 0)
+ if (destSize.width == 0 || destSize.height == 0)
return NS_ERROR_FAILURE;
// if the image is larger than half the screen size, scale it down. This
// scaling algorithm is the same as is used in nsPresShell::PaintRangePaintInfo
nsIDeviceContext* deviceContext = aPresContext->DeviceContext();
nsRect maxSize;
deviceContext->GetClientRect(maxSize);
nscoord maxWidth = aPresContext->AppUnitsToDevPixels(maxSize.width >> 1);
nscoord maxHeight = aPresContext->AppUnitsToDevPixels(maxSize.height >> 1);
- if (destRect.width > maxWidth || destRect.height > maxHeight) {
+ if (destSize.width > maxWidth || destSize.height > maxHeight) {
float scale = 1.0;
- if (destRect.width > maxWidth)
- scale = PR_MIN(scale, float(maxWidth) / destRect.width);
- if (destRect.height > maxHeight)
- scale = PR_MIN(scale, float(maxHeight) / destRect.height);
+ if (destSize.width > maxWidth)
+ scale = PR_MIN(scale, float(maxWidth) / destSize.width);
+ if (destSize.height > maxHeight)
+ scale = PR_MIN(scale, float(maxHeight) / destSize.height);
- destRect.width = NSToIntFloor(float(destRect.width) * scale);
- destRect.height = NSToIntFloor(float(destRect.height) * scale);
+ destSize.width = NSToIntFloor(float(destSize.width) * scale);
+ destSize.height = NSToIntFloor(float(destSize.height) * scale);
aScreenDragRect->x = NSToIntFloor(aScreenX - float(mImageX) * scale);
aScreenDragRect->y = NSToIntFloor(aScreenY - float(mImageY) * scale);
- aScreenDragRect->width = destRect.width;
- aScreenDragRect->height = destRect.height;
+ aScreenDragRect->width = destSize.width;
+ aScreenDragRect->height = destSize.height;
}
nsRefPtr<gfxImageSurface> surface =
- new gfxImageSurface(gfxIntSize(destRect.width, destRect.height),
+ new gfxImageSurface(gfxIntSize(destSize.width, destSize.height),
gfxImageSurface::ImageFormatARGB32);
if (!surface)
return NS_ERROR_FAILURE;
+ nsRefPtr<gfxContext> ctx = new gfxContext(surface);
+ if (!ctx)
+ return NS_ERROR_FAILURE;
+
*aSurface = surface;
NS_ADDREF(*aSurface);
- nsCOMPtr<nsIRenderingContext> rc;
- deviceContext->CreateRenderingContextInstance(*getter_AddRefs(rc));
- rc->Init(deviceContext, surface);
-
if (aImageLoader) {
- // 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, inRect, outRect);
- }
- else {
- return aCanvas->RenderContexts(rc->ThebesContext());
+ gfxRect outRect(0, 0, destSize.width, destSize.height);
+ gfxMatrix scale =
+ gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height());
+ img->Draw(ctx, scale, outRect, nsIntMargin(0,0,0,0),
+ nsIntRect(0, 0, srcSize.width, srcSize.height));
+ return NS_OK;
+ } else {
+ return aCanvas->RenderContexts(ctx);
}
}
void
nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
PRInt32* aScreenX, PRInt32* aScreenY)
{
PRInt32 adj = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel();