Bug 458487 - 'Rework image snapping logic'. r=vlad+joedrew, sr=dbaron
This commit is contained in:
Родитель
d0bae9678a
Коммит
71861fbdae
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче