From 1976d96d55519ca9cd1e92f9588c462ee5cbdc36 Mon Sep 17 00:00:00 2001 From: Michael Ventnor Date: Wed, 3 Dec 2008 10:16:22 +1300 Subject: [PATCH] Bug 458031. Take dirty rect into account to limit box-shadow computation. r+sr=roc --- layout/base/nsCSSRendering.cpp | 35 +++++++++++++++++++++---- layout/base/nsCSSRendering.h | 11 ++++++-- layout/base/nsDisplayList.cpp | 2 +- layout/generic/nsHTMLContainerFrame.cpp | 4 ++- layout/generic/nsTextFrameThebes.cpp | 2 +- layout/xul/base/src/nsTextBoxFrame.cpp | 9 ++++--- layout/xul/base/src/nsTextBoxFrame.h | 3 ++- 7 files changed, 52 insertions(+), 14 deletions(-) diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 5a7ecca678e9..0a343b3555f9 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1054,7 +1054,8 @@ void nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIFrame* aForFrame, - const nsPoint& aForFramePt) + const nsPoint& aForFramePt, + const nsRect& aDirtyRect) { nsMargin borderValues; PRIntn sidesToSkip; @@ -1076,6 +1077,8 @@ nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext, twipsPerPixel, &borderRadii); gfxRect frameGfxRect = RectToGfxRect(frameRect, twipsPerPixel); + gfxRect dirtyGfxRect = RectToGfxRect(aDirtyRect, twipsPerPixel); + for (PRUint32 i = styleBorder->mBoxShadow->Length(); i > 0; --i) { nsCSSShadowItem* shadowItem = styleBorder->mBoxShadow->ShadowAt(i - 1); gfxRect shadowRect(frameRect.x, frameRect.y, frameRect.width, frameRect.height); @@ -1097,9 +1100,9 @@ nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext, nsRefPtr shadowContext; nsContextBoxBlur blurringArea; - // shadowRect has already been converted to device pixels, pass 1 as the appunits/pixel value + // shadowRect is already in device pixels, pass 1 as the appunits/pixel value blurRadius /= twipsPerPixel; - shadowContext = blurringArea.Init(shadowRect, blurRadius, 1, renderContext); + shadowContext = blurringArea.Init(shadowRect, blurRadius, 1, renderContext, dirtyGfxRect); if (!shadowContext) continue; @@ -2540,7 +2543,8 @@ GetTextDecorationRectInternal(const gfxPoint& aPt, gfxContext* nsContextBoxBlur::Init(const gfxRect& aRect, nscoord aBlurRadius, PRInt32 aAppUnitsPerDevPixel, - gfxContext* aDestinationCtx) + gfxContext* aDestinationCtx, + const gfxRect& aDirtyRect) { mDestinationCtx = aDestinationCtx; @@ -2561,9 +2565,24 @@ nsContextBoxBlur::Init(const gfxRect& aRect, nscoord aBlurRadius, return mContext; } + 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; - mContext = blur.Init(rect, gfxIntSize(blurRadius, blurRadius)); + // 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)); return mContext; } @@ -2573,7 +2592,12 @@ nsContextBoxBlur::DoPaint() if (mContext == mDestinationCtx) return; + mDestinationCtx->Save(); + mDestinationCtx->NewPath(); + mDestinationCtx->Rectangle(mRequiredShadowArea); + mDestinationCtx->Clip(); blur.Paint(mDestinationCtx); + mDestinationCtx->Restore(); } gfxContext* @@ -2581,3 +2605,4 @@ nsContextBoxBlur::GetContext() { return mContext; } + diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index 609bf70b7f50..909b6735abc2 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -64,7 +64,8 @@ struct nsCSSRendering { static void PaintBoxShadow(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIFrame* aForFrame, - const nsPoint& aForFramePt); + const nsPoint& aForFramePt, + const nsRect& aDirtyRect); /** * Render the border for an element using css rendering rules @@ -288,6 +289,9 @@ public: * set the color on this context before * calling Init(). * + * @param aDirtyRect The absolute dirty rect in app units. Used to + * optimize the temporary surface size and speed up blur. + * * @return A blank 8-bit alpha-channel-only graphics context to * draw on, or null on error. Must not be freed. The * context has a device offset applied to it given by @@ -302,7 +306,8 @@ public: * directly on it instead of any temporary surface created in this class. */ gfxContext* Init(const gfxRect& aRect, nscoord aBlurRadius, - PRInt32 aAppUnitsPerDevPixel, gfxContext* aDestinationCtx); + PRInt32 aAppUnitsPerDevPixel, gfxContext* aDestinationCtx, + const gfxRect& aDirtyRect); /** * Does the actual blurring and mask applying. Users of this object *must* @@ -322,6 +327,8 @@ protected: gfxAlphaBoxBlur blur; nsRefPtr mContext; gfxContext* mDestinationCtx; + + gfxRect mRequiredShadowArea; }; diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 5fb515c2e345..226660b8ec47 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -651,7 +651,7 @@ nsDisplayBoxShadow::Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx, const nsRect& aDirtyRect) { nsPoint offset = aBuilder->ToReferenceFrame(mFrame); nsCSSRendering::PaintBoxShadow(mFrame->PresContext(), *aCtx, - mFrame, offset); + mFrame, offset, aDirtyRect); } nsRect diff --git a/layout/generic/nsHTMLContainerFrame.cpp b/layout/generic/nsHTMLContainerFrame.cpp index d1e74ee37889..dc1ce2ae6bb7 100644 --- a/layout/generic/nsHTMLContainerFrame.cpp +++ b/layout/generic/nsHTMLContainerFrame.cpp @@ -202,10 +202,12 @@ nsDisplayTextShadow::Paint(nsDisplayListBuilder* aBuilder, gfxRect shadowRect = gfxRect(pt.x, pt.y, innerWidthInAppUnits, mFrame->GetSize().height); gfxContext* thebesCtx = aCtx->ThebesContext(); + gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); + nsContextBoxBlur contextBoxBlur; gfxContext* shadowCtx = contextBoxBlur.Init(shadowRect, mBlurRadius, mFrame->PresContext()->AppUnitsPerDevPixel(), - thebesCtx); + thebesCtx, dirtyRect); if (!shadowCtx) return; diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 82080431f924..9a18f14cb538 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -4150,7 +4150,7 @@ nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength, nsContextBoxBlur contextBoxBlur; gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, blurRadius, PresContext()->AppUnitsPerDevPixel(), - aCtx); + aCtx, aDirtyRect); if (!shadowContext) return; diff --git a/layout/xul/base/src/nsTextBoxFrame.cpp b/layout/xul/base/src/nsTextBoxFrame.cpp index 8f0dcd1b1f40..f9f2ef0aa83a 100644 --- a/layout/xul/base/src/nsTextBoxFrame.cpp +++ b/layout/xul/base/src/nsTextBoxFrame.cpp @@ -387,7 +387,8 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext, PaintOneShadow(aRenderingContext.ThebesContext(), textRect, textStyle->mTextShadow->ShadowAt(i - 1), - GetStyleColor()->mColor); + GetStyleColor()->mColor, + aDirtyRect); } } @@ -563,7 +564,8 @@ nsTextBoxFrame::DrawText(nsIRenderingContext& aRenderingContext, void nsTextBoxFrame::PaintOneShadow(gfxContext* aCtx, const nsRect& aTextRect, nsCSSShadowItem* aShadowDetails, - const nscolor& aForegroundColor) { + const nscolor& aForegroundColor, + const nsRect& aDirtyRect) { nsPoint shadowOffset(aShadowDetails->mXOffset, aShadowDetails->mYOffset); nscoord blurRadius = PR_MAX(aShadowDetails->mRadius, 0); @@ -572,11 +574,12 @@ void nsTextBoxFrame::PaintOneShadow(gfxContext* aCtx, shadowRect.MoveBy(shadowOffset); gfxRect shadowRectGFX(shadowRect.x, shadowRect.y, shadowRect.width, shadowRect.height); + gfxRect dirtyRectGFX(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); nsContextBoxBlur contextBoxBlur; gfxContext* shadowContext = contextBoxBlur.Init(shadowRectGFX, blurRadius, PresContext()->AppUnitsPerDevPixel(), - aCtx); + aCtx, dirtyRectGFX); if (!shadowContext) return; diff --git a/layout/xul/base/src/nsTextBoxFrame.h b/layout/xul/base/src/nsTextBoxFrame.h index 1604504afff2..7024c2d2da16 100644 --- a/layout/xul/base/src/nsTextBoxFrame.h +++ b/layout/xul/base/src/nsTextBoxFrame.h @@ -134,7 +134,8 @@ private: void PaintOneShadow(gfxContext * aCtx, const nsRect& aTextRect, nsCSSShadowItem* aShadowDetails, - const nscolor& aForegroundColor); + const nscolor& aForegroundColor, + const nsRect& aDirtyRect); CroppingStyle mCropType;