Bug 458487 - 'Rework image snapping logic'. r=vlad+joedrew, sr=dbaron

This commit is contained in:
Robert O'Callahan 2008-11-04 14:01:21 -08:00
Родитель d0bae9678a
Коммит 71861fbdae
51 изменённых файлов: 1034 добавлений и 1151 удалений

Просмотреть файл

@ -39,12 +39,14 @@
#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;
@ -72,10 +74,10 @@ typedef enum {
#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
@ -189,19 +191,32 @@ public:
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

Просмотреть файл

@ -97,9 +97,10 @@ typedef enum
// 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 } }
//----------------------------------------------------------------------
@ -586,21 +587,6 @@ public:
*/
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.
*

Просмотреть файл

@ -431,411 +431,245 @@ nsThebesImage::UnlockImagePixels(PRBool aMaskPixels)
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;
}
nsThebesRenderingContext *thebesRC = static_cast<nsThebesRenderingContext*>(&aContext);
gfxContext *ctx = thebesRC->ThebesContext();
#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
if (mSinglePixel) {
// 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);
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;
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;
}
// 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 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;
}
gfxMatrix mat;
mat.Translate(srcRect.pos);
mat.Scale(1.0/xscale, 1.0/yscale);
/* Translate the start point of the image (srcRect.pos)
* to coincide with the destination rectangle origin
*/
mat.Translate(-destRect.pos);
pat->SetMatrix(mat);
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;
}
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;
return aValue >= -32768 && aValue <= 32767;
}
nsresult
nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
nsIDeviceContext* dx,
const gfxPoint& offset,
const gfxRect& targetRect,
const nsIntRect& aSubimageRect,
const PRInt32 xPadding,
const PRInt32 yPadding)
void
nsThebesImage::Draw(gfxContext* aContext,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntMargin& aPadding,
const nsIntRect& aSubimage)
{
NS_ASSERTION(xPadding >= 0 && yPadding >= 0, "negative padding");
NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
if (targetRect.size.width <= 0.0 || targetRect.size.height <= 0.0)
return NS_OK;
PRBool doPadding = aPadding != nsIntMargin(0,0,0,0);
PRBool doPartialDecode = !GetIsImageComplete();
gfxContext::GraphicsOperator op = aContext->CurrentOperator();
// don't do anything if we have a transparent pixel source
if (mSinglePixel && mSinglePixelColor.a == 0.0)
return NS_OK;
if (mSinglePixel && !doPadding && !doPartialDecode) {
// Single-color fast path
// if a == 0, it's a noop
if (mSinglePixelColor.a == 0.0)
return;
#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 (op == gfxContext::OPERATOR_OVER && mSinglePixelColor.a == 1.0)
aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
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);
aContext->SetDeviceColor(mSinglePixelColor);
aContext->NewPath();
aContext->Rectangle(aFill);
aContext->Fill();
aContext->SetOperator(op);
aContext->SetDeviceColor(gfxRGBA(0,0,0,0));
return;
}
gfxContext::GraphicsOperator op = thebesContext->CurrentOperator();
if (op == gfxContext::OPERATOR_OVER && format == gfxASurface::ImageFormatRGB24)
thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
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;
thebesContext->NewPath();
thebesContext->Rectangle(targetRect, doSnap);
thebesContext->Fill();
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;
}
// At this point, we've taken care of mSinglePixel images, images with
// aPadding, and partially-decoded images.
thebesContext->SetOperator(op);
thebesContext->SetDeviceColor(gfxRGBA(0,0,0,0));
if (!AllowedImageSize(fill.size.width + 1, fill.size.height + 1)) {
NS_WARNING("Destination area too large, bailing out");
return;
}
return NS_OK;
// 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;
}
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);
// 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;
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;
}
}
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

Просмотреть файл

@ -75,18 +75,11 @@ public:
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();

Просмотреть файл

@ -760,76 +760,6 @@ nsThebesRenderingContext::PopFilter()
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
//

Просмотреть файл

@ -183,8 +183,6 @@ public:
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);

Просмотреть файл

@ -338,6 +338,19 @@ public:
*/
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

Просмотреть файл

@ -42,6 +42,7 @@
#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;
@ -175,11 +176,21 @@ public:
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);
}
@ -188,7 +199,7 @@ public:
* 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);
@ -199,7 +210,7 @@ public:
* 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);
}

Просмотреть файл

@ -71,21 +71,9 @@ public:
/* 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)

Просмотреть файл

@ -427,6 +427,26 @@ gfxContext::UserToDevicePixelSnapped(gfxRect& rect, PRBool ignoreScale) const
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)

Просмотреть файл

@ -788,108 +788,53 @@ nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
//----------------------------------------------------------------------
// 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");
}
aResult.x = x;
nscoord y;
if (NS_STYLE_BG_Y_POSITION_LENGTH & aColor.mBackgroundFlags) {
y = aColor.mBackgroundYPosition.mCoord;
aTopLeft->y = aAnchorPoint->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*
@ -1265,84 +1210,6 @@ nsCSSRendering::PaintBackground(nsPresContext* aPresContext,
*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)
{
@ -1422,6 +1289,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
}
}
// Same coordinate space as aBorderArea
nsRect bgClipArea;
if (aBGClipRect) {
bgClipArea = *aBGClipRect;
@ -1439,14 +1307,8 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
}
}
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;
@ -1490,56 +1352,53 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
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;
@ -1582,21 +1441,11 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
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
@ -1618,7 +1467,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
// 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.
@ -1631,69 +1480,29 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
}
// 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,
@ -1706,177 +1515,24 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
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

Просмотреть файл

@ -2662,18 +2662,47 @@ nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame)
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;
@ -2681,131 +2710,137 @@ nsLayoutUtils::DrawImage(nsIRenderingContext* aRenderingContext,
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);
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();
}
// 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;
}
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 {
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);
source.SizeTo(size.width*appUnitsPerCSSPixel, size.height*appUnitsPerCSSPixel);
}
gfxRect pxSubimage = pxSrc;
nsCOMPtr<nsIDeviceContext> dc;
aRenderingContext->GetDeviceContext(*getter_AddRefs(dc));
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);
}
gfxContext *ctx = aRenderingContext->ThebesContext();
/* 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);
// the dest rect is affected by the current transform; that'll be
// handled by Image::Draw(), when we actually set up the rectangle.
if (size.width == 0 || size.height == 0)
return NS_OK;
// 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);
nscoord appUnitsPerCSSPixel = nsIDeviceContext::AppUnitsPerCSSPixel();
nsRect source;
if (aSourceArea) {
source = *aSourceArea;
} else {
source.SizeTo(size.width*appUnitsPerCSSPixel, size.height*appUnitsPerCSSPixel);
}
// 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);
}
nsRect dest = GetWholeImageDestination(size, source, aDest);
return DrawImage(aRenderingContext, aImage, dest, aDest, aDest.TopLeft(), aDirty);
}
// 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 (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;
}
// 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)) {
return NS_OK;
}
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);
/* 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

Просмотреть файл

@ -794,27 +794,76 @@ public:
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 (aDestRect).
* 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 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& aDestRect,
const nsRect& aDirtyRect,
const nsRect* aSourceRect = nsnull);
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.
* @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 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 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

Просмотреть файл

@ -239,8 +239,8 @@ nsBulletFrame::PaintBullet(nsIRenderingContext& aRenderingContext, nsPoint aPt,
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;
}
}

Просмотреть файл

@ -1069,7 +1069,7 @@ nsImageFrame::DisplayAltFeedback(nsIRenderingContext& aRenderingContext,
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;
}
}
@ -1174,13 +1174,10 @@ nsImageFrame::PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
// 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);

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -19,8 +19,8 @@
</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>
@ -33,8 +33,8 @@
</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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -0,0 +1,4 @@
<!DOCTYPE HTML>
<html style="padding:30px; background: url(mozilla-banner.gif) no-repeat;
-moz-background-origin:content; height:100px;">
</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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -0,0 +1,6 @@
<!DOCTYPE HTML>
<html>
<body style="margin:0;">
<img src="mozilla-banner.gif">
</body>
</html>

Просмотреть файл

@ -0,0 +1,6 @@
<!DOCTYPE HTML>
<html>
<body>
<img src="mozilla-banner.gif" style="margin-left:100px;">
</body>
</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>

Просмотреть файл

@ -0,0 +1,3 @@
<!DOCTYPE HTML>
<html style="background: url(mozilla-banner.gif) no-repeat;">
</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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -447,6 +447,7 @@ random-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 333970-1.html 333970-1-ref.html # bug 3
== 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
@ -917,6 +918,14 @@ fails == 441259-2.html 441259-2-ref.html # bug 441400
== 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
@ -931,5 +940,20 @@ fails == 441259-2.html 441259-2-ref.html # bug 441400
== 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

Просмотреть файл

@ -378,8 +378,8 @@ nsImageBoxFrame::PaintImage(nsIRenderingContext& aRenderingContext,
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);
}
}

Просмотреть файл

@ -3371,18 +3371,18 @@ nsTreeBodyFrame::PaintTwisty(PRInt32 aRowIndex,
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);
}
}
}
}
}
@ -3490,10 +3490,7 @@ nsTreeBodyFrame::PaintImage(PRInt32 aRowIndex,
// 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.
@ -3501,11 +3498,15 @@ nsTreeBodyFrame::PaintImage(PRInt32 aRowIndex,
// 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.
@ -3653,19 +3654,19 @@ nsTreeBodyFrame::PaintCheckbox(PRInt32 aRowIndex,
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);
}
}
@ -3719,10 +3720,17 @@ nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
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.
@ -3731,8 +3739,15 @@ nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
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);
}
}
}

Просмотреть файл

@ -559,11 +559,10 @@ nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
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
@ -573,48 +572,44 @@ nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
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);
}
}