mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 421885. Make tiled image drawing sample only the correct subimage by manually padding if necessary. r=vlad
This commit is contained in:
parent
6ecfeccd3a
commit
421ea5a76e
@ -598,11 +598,14 @@ public:
|
||||
* @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) = 0;
|
||||
|
||||
const nsRect * aTargetRect,
|
||||
const nsIntRect * aSubimageRect) = 0;
|
||||
|
||||
/**
|
||||
* Get cluster details for a chunk of text.
|
||||
|
@ -177,6 +177,10 @@ struct NS_GFX nsRect {
|
||||
// Scale by aScale, converting coordinates to integers so that the result
|
||||
// is the smallest integer-coordinate rectangle containing the unrounded result
|
||||
nsRect& ScaleRoundOut(float aScale);
|
||||
// Scale by the inverse of aScale, converting coordinates to integers so that the result
|
||||
// is the smallest integer-coordinate rectangle containing the unrounded result.
|
||||
// More accurate than ScaleRoundOut(1.0/aScale).
|
||||
nsRect& ScaleRoundOutInverse(float aScale);
|
||||
// Scale by aScale, converting coordinates to integers so that the result
|
||||
// is the larges integer-coordinate rectangle contained in the unrounded result
|
||||
nsRect& ScaleRoundIn(float aScale);
|
||||
|
@ -185,6 +185,17 @@ nsRect& nsRect::ScaleRoundOut(float aScale)
|
||||
return *this;
|
||||
}
|
||||
|
||||
nsRect& nsRect::ScaleRoundOutInverse(float aScale)
|
||||
{
|
||||
nscoord right = NSToCoordCeil(float(XMost()) / aScale);
|
||||
nscoord bottom = NSToCoordCeil(float(YMost()) / aScale);
|
||||
x = NSToCoordFloor(float(x) / aScale);
|
||||
y = NSToCoordFloor(float(y) / aScale);
|
||||
width = (right - x);
|
||||
height = (bottom - y);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// scale the rect but round to largest contained rect
|
||||
nsRect& nsRect::ScaleRoundIn(float aScale)
|
||||
{
|
||||
|
@ -620,6 +620,7 @@ nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
|
||||
nsIDeviceContext* dx,
|
||||
const gfxPoint& offset,
|
||||
const gfxRect& targetRect,
|
||||
const nsIntRect& aSubimageRect,
|
||||
const PRInt32 xPadding,
|
||||
const PRInt32 yPadding)
|
||||
{
|
||||
@ -634,8 +635,9 @@ nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
|
||||
|
||||
PRBool doSnap = !(thebesContext->CurrentMatrix().HasNonTranslation());
|
||||
PRBool hasPadding = ((xPadding != 0) || (yPadding != 0));
|
||||
|
||||
nsRefPtr<gfxASurface> tmpSurfaceGrip;
|
||||
gfxImageSurface::gfxImageFormat format = mFormat;
|
||||
|
||||
gfxPoint tmpOffset = offset;
|
||||
|
||||
if (mSinglePixel && !hasPadding) {
|
||||
thebesContext->SetColor(mSinglePixelColor);
|
||||
@ -653,14 +655,13 @@ nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
|
||||
if (!AllowedImageSize(width, height))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
surface = new gfxImageSurface(gfxIntSize(width, height),
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
format = gfxASurface::ImageFormatARGB32;
|
||||
surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
|
||||
gfxIntSize(width, height), format);
|
||||
if (!surface || surface->CairoStatus()) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
tmpSurfaceGrip = surface;
|
||||
|
||||
gfxContext tmpContext(surface);
|
||||
if (mSinglePixel) {
|
||||
tmpContext.SetColor(mSinglePixelColor);
|
||||
@ -675,17 +676,105 @@ nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
|
||||
height = mHeight;
|
||||
surface = ThebesSurface();
|
||||
}
|
||||
|
||||
gfxMatrix patMat;
|
||||
gfxPoint p0;
|
||||
|
||||
p0.x = - floor(offset.x + 0.5);
|
||||
p0.y = - floor(offset.y + 0.5);
|
||||
|
||||
// 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);
|
||||
|
||||
@ -705,7 +794,7 @@ nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
|
||||
}
|
||||
|
||||
gfxContext::GraphicsOperator op = thebesContext->CurrentOperator();
|
||||
if (op == gfxContext::OPERATOR_OVER && mFormat == gfxASurface::ImageFormatRGB24)
|
||||
if (op == gfxContext::OPERATOR_OVER && format == gfxASurface::ImageFormatRGB24)
|
||||
thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
|
||||
thebesContext->NewPath();
|
||||
|
@ -84,6 +84,7 @@ public:
|
||||
nsIDeviceContext* dx,
|
||||
const gfxPoint& aOffset,
|
||||
const gfxRect& aTileRect,
|
||||
const nsIntRect& aSubimageRect,
|
||||
const PRInt32 aXPadding,
|
||||
const PRInt32 aYPadding);
|
||||
|
||||
|
@ -778,7 +778,8 @@ nsThebesRenderingContext::PopFilter()
|
||||
NS_IMETHODIMP
|
||||
nsThebesRenderingContext::DrawTile(imgIContainer *aImage,
|
||||
nscoord twXOffset, nscoord twYOffset,
|
||||
const nsRect *twTargetRect)
|
||||
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),
|
||||
@ -813,18 +814,34 @@ nsThebesRenderingContext::DrawTile(imgIContainer *aImage,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ public:
|
||||
NS_IMETHOD SetTranslation(nscoord aX, nscoord aY);
|
||||
|
||||
NS_IMETHOD DrawTile(imgIContainer *aImage, nscoord aXOffset, nscoord aYOffset,
|
||||
const nsRect * aTargetRect);
|
||||
const nsRect * aTargetRect, const nsIntRect * aSubimageRect);
|
||||
NS_IMETHOD SetRightToLeftText(PRBool aIsRTL);
|
||||
NS_IMETHOD GetRightToLeftText(PRBool* aIsRTL);
|
||||
virtual void SetTextRunRTL(PRBool aIsRTL);
|
||||
|
@ -120,6 +120,11 @@ struct THEBES_API gfxPoint {
|
||||
int operator!=(const gfxPoint& p) const {
|
||||
return ((x != p.x) || (y != p.y));
|
||||
}
|
||||
const gfxPoint& operator+=(const gfxPoint& p) {
|
||||
x += p.x;
|
||||
y += p.y;
|
||||
return *this;
|
||||
}
|
||||
gfxPoint operator+(const gfxPoint& p) const {
|
||||
return gfxPoint(x + p.x, y + p.y);
|
||||
}
|
||||
|
@ -3904,19 +3904,23 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
|
||||
"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 - aBorderArea.TopLeft() - tileRect.TopLeft();
|
||||
if (sourceRect.XMost() <= tileWidth && sourceRect.YMost() <= tileHeight) {
|
||||
// The entire drawRect is contained inside a single tile; just
|
||||
// draw the corresponding part of the image once.
|
||||
// Pass in the subimage rectangle that we expect to be sampled.
|
||||
// This is the tile rectangle, clipped to the bgClipArea, and then
|
||||
// passed in relative to the image top-left.
|
||||
nsRect destRect; // The rectangle we would draw ignoring dirty-rect
|
||||
destRect.IntersectRect(absTileRect, bgClipArea);
|
||||
nsRect subimageRect = destRect - aBorderArea.TopLeft() - tileRect.TopLeft();
|
||||
nsLayoutUtils::DrawImage(&aRenderingContext, image,
|
||||
destRect, drawRect, &subimageRect);
|
||||
} else {
|
||||
aRenderingContext.DrawTile(image, absTileRect.x, absTileRect.y, &drawRect);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,6 +773,7 @@ fails == 413027-3.html 413027-3-ref.html
|
||||
== 421069-ref.html 421069-ref2.html
|
||||
== 421234-1.html 421234-1-ref.html
|
||||
== 421419-1.html 421419-1-ref.html
|
||||
== 421885-1.xml 421885-1-ref.xml
|
||||
== 421955-1.html 421955-1-ref.html
|
||||
== 422394-1.html 422394-1-ref.html
|
||||
== 423130-1.html 423130-1-ref.html
|
||||
|
BIN
layout/reftests/bugs/square-left-right-32x32.png
Normal file
BIN
layout/reftests/bugs/square-left-right-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 158 B |
BIN
layout/reftests/bugs/square-top-bottom-32x32.png
Normal file
BIN
layout/reftests/bugs/square-top-bottom-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 161 B |
@ -3657,7 +3657,7 @@ nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
GetImage(aRowIndex, aColumn, PR_TRUE, meterContext, useImageRegion, getter_AddRefs(image));
|
||||
if (image)
|
||||
aRenderingContext.DrawTile(image, 0, 0, &meterRect);
|
||||
aRenderingContext.DrawTile(image, 0, 0, &meterRect, nsnull);
|
||||
else
|
||||
aRenderingContext.FillRect(meterRect);
|
||||
}
|
||||
@ -3669,7 +3669,7 @@ nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
GetImage(aRowIndex, aColumn, PR_TRUE, meterContext, useImageRegion, getter_AddRefs(image));
|
||||
if (image)
|
||||
aRenderingContext.DrawTile(image, 0, 0, &meterRect);
|
||||
aRenderingContext.DrawTile(image, 0, 0, &meterRect, nsnull);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user