Bug 468018 - Optimize box-shadow rendering even further by doing more intersections; r=vladimir

This commit is contained in:
Michael Ventnor 2008-12-06 19:54:36 +01:00
parent a5672f5802
commit e758b80cd9
5 changed files with 42 additions and 25 deletions

View File

@ -1468,7 +1468,7 @@ nsCanvasRenderingContext2D::ShadowInitialize(const gfxRect& extents, gfxAlphaBox
blurRadius.height, blurRadius.width);
drawExtents = drawExtents.Intersect(clipExtents - CurrentState().shadowOffset);
gfxContext* ctx = blur.Init(drawExtents, blurRadius);
gfxContext* ctx = blur.Init(drawExtents, blurRadius, nsnull);
if (!ctx)
return nsnull;

View File

@ -66,9 +66,13 @@ public:
* @param aRect The coordinates of the surface to create in device units.
*
* @param aBlurRadius The blur radius in pixels
*
* @param aDirtyRect A pointer to a dirty rect, measured in device units, if available.
* This will be used for optimizing the blur operation. It is safe to pass NULL here.
*/
gfxContext* Init(const gfxRect& aRect,
const gfxIntSize& aBlurRadius);
const gfxIntSize& aBlurRadius,
const gfxRect* aDirtyRect);
/**
* Returns the context that should be drawn to supply the alpha mask to be
@ -116,6 +120,13 @@ protected:
* The temporary alpha surface.
*/
nsRefPtr<gfxImageSurface> mImageSurface;
/**
* A copy of the dirty rect passed to Init(). This will only be valid if
* mHasDirtyRect is TRUE.
*/
gfxRect mDirtyRect;
PRBool mHasDirtyRect;
};
#endif /* GFX_BLUR_H */

View File

@ -54,12 +54,12 @@ gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
gfxContext*
gfxAlphaBoxBlur::Init(const gfxRect& aRect,
const gfxIntSize& aBlurRadius)
const gfxIntSize& aBlurRadius,
const gfxRect* aDirtyRect)
{
mBlurRadius = aBlurRadius;
gfxRect rect(aRect);
rect.Outset(aBlurRadius.height, aBlurRadius.width,
aBlurRadius.height, aBlurRadius.width);
rect.RoundOut();
@ -67,6 +67,19 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
if (rect.IsEmpty())
return nsnull;
if (aDirtyRect) {
// If we get passed a dirty rect from layout, we can minimize the
// shadow size and make painting faster.
mHasDirtyRect = PR_TRUE;
mDirtyRect = *aDirtyRect;
gfxRect requiredBlurArea = mDirtyRect.Intersect(rect);
requiredBlurArea.Outset(aBlurRadius.height, aBlurRadius.width,
aBlurRadius.height, aBlurRadius.width);
rect = requiredBlurArea.Intersect(rect);
} else {
mHasDirtyRect = PR_FALSE;
}
// Make an alpha-only surface to draw on. We will play with the data after
// everything is drawn to create a blur effect.
mImageSurface = new gfxImageSurface(gfxIntSize(static_cast<PRInt32>(rect.Width()), static_cast<PRInt32>(rect.Height())),
@ -214,7 +227,18 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
}
}
aDestinationCtx->Mask(mImageSurface, offset);
// Avoid a semi-expensive clip operation if we can, otherwise
// clip to the dirty rect
if (mHasDirtyRect) {
aDestinationCtx->Save();
aDestinationCtx->NewPath();
aDestinationCtx->Rectangle(mDirtyRect);
aDestinationCtx->Clip();
aDestinationCtx->Mask(mImageSurface, offset);
aDestinationCtx->Restore();
} else {
aDestinationCtx->Mask(mImageSurface, offset);
}
}
static const gfxFloat GAUSSIAN_SCALE_FACTOR = 3 * sqrt(2 * M_PI) / 4;

View File

@ -2567,22 +2567,11 @@ nsContextBoxBlur::Init(const gfxRect& aRect, nscoord aBlurRadius,
gfxRect dirtyRect = aDirtyRect;
dirtyRect.ScaleInverse(aAppUnitsPerDevPixel);
gfxRect rectWithBlur = rect;
rectWithBlur.Outset(blurRadius);
// Determine the area of the shadow we need.
mRequiredShadowArea = dirtyRect.Intersect(rectWithBlur);
mDestinationCtx = aDestinationCtx;
// XXX the temporary surface will be the mRequiredShadowArea inflated by
// blurRadius in each direction so that the required shadow pixels are computed
// correctly. We could actually use a smaller temporary surface by observing
// that where the temporary surface is outside the rectWithBlur, the pixel
// values are guaranteed to be fully transparent, so we could intersect the
// inflated mRequiredShadowArea with rectWithBlur to compute the temporary
// surface area. But we're not doing that right now because it's more complex to do.
mContext = blur.Init(mRequiredShadowArea, gfxIntSize(blurRadius, blurRadius));
// Create the temporary surface for blurring
mContext = blur.Init(rect, gfxIntSize(blurRadius, blurRadius), &dirtyRect);
return mContext;
}
@ -2592,12 +2581,7 @@ nsContextBoxBlur::DoPaint()
if (mContext == mDestinationCtx)
return;
mDestinationCtx->Save();
mDestinationCtx->NewPath();
mDestinationCtx->Rectangle(mRequiredShadowArea);
mDestinationCtx->Clip();
blur.Paint(mDestinationCtx);
mDestinationCtx->Restore();
}
gfxContext*

View File

@ -327,8 +327,6 @@ protected:
gfxAlphaBoxBlur blur;
nsRefPtr<gfxContext> mContext;
gfxContext* mDestinationCtx;
gfxRect mRequiredShadowArea;
};