Bug 458031. Take dirty rect into account to limit box-shadow computation. r+sr=roc
authorMichael Ventnor <ventnor.bugzilla@gmail.com>
Wed, 03 Dec 2008 10:16:22 +1300
changeset 22183 ce72e9a5dca03ddcb5902b74c2e97c528da72ece
parent 22182 3f5a6da199fcfb3620265f2abfac3ed7f26394ea
child 22184 31e5958cb97a21bb27bff4c9438ea659791b6b9b
push id3843
push userrocallahan@mozilla.com
push dateTue, 02 Dec 2008 21:22:28 +0000
treeherdermozilla-central@31e5958cb97a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs458031
milestone1.9.2a1pre
Bug 458031. Take dirty rect into account to limit box-shadow computation. r+sr=roc
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/base/nsDisplayList.cpp
layout/generic/nsHTMLContainerFrame.cpp
layout/generic/nsTextFrameThebes.cpp
layout/xul/base/src/nsTextBoxFrame.cpp
layout/xul/base/src/nsTextBoxFrame.h
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1049,17 +1049,18 @@ GetBorderRadiusTwips(const nsStyleCorner
   }
   return result;
 }
 
 void
 nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext,
                                nsIRenderingContext& aRenderingContext,
                                nsIFrame* aForFrame,
-                               const nsPoint& aForFramePt)
+                               const nsPoint& aForFramePt,
+                               const nsRect& aDirtyRect)
 {
   nsMargin      borderValues;
   PRIntn        sidesToSkip;
   nsRect        frameRect;
 
   const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
   borderValues = styleBorder->GetActualBorder();
   sidesToSkip = aForFrame->GetSkipSides();
@@ -1071,16 +1072,18 @@ nsCSSRendering::PaintBoxShadow(nsPresCon
                                                 frameRect.width, twipsRadii);
   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
 
   gfxCornerSizes borderRadii;
   ComputePixelRadii(twipsRadii, frameRect, sidesToSkip,
                     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);
     shadowRect.MoveBy(gfxPoint(shadowItem->mXOffset, shadowItem->mYOffset));
     shadowRect.Outset(shadowItem->mSpread);
 
     gfxRect shadowRectPlusBlur = shadowRect;
     shadowRect.ScaleInverse(twipsPerPixel);
@@ -1092,19 +1095,19 @@ nsCSSRendering::PaintBoxShadow(nsPresCon
     shadowRectPlusBlur.Outset(blurRadius);
     shadowRectPlusBlur.ScaleInverse(twipsPerPixel);
     shadowRectPlusBlur.RoundOut();
 
     gfxContext* renderContext = aRenderingContext.ThebesContext();
     nsRefPtr<gfxContext> 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;
 
     // Set the shadow color; if not specified, use the foreground color
     nscolor shadowColor;
     if (shadowItem->mHasColor)
       shadowColor = shadowItem->mColor;
     else
@@ -2535,17 +2538,18 @@ GetTextDecorationRectInternal(const gfxP
 }
 
 // -----
 // nsContextBoxBlur
 // -----
 gfxContext*
 nsContextBoxBlur::Init(const gfxRect& aRect, nscoord aBlurRadius,
                        PRInt32 aAppUnitsPerDevPixel,
-                       gfxContext* aDestinationCtx)
+                       gfxContext* aDestinationCtx,
+                       const gfxRect& aDirtyRect)
 {
   mDestinationCtx = aDestinationCtx;
 
   PRInt32 blurRadius = static_cast<PRInt32>(aBlurRadius / aAppUnitsPerDevPixel);
 
   // if not blurring, draw directly onto the destination device
   if (blurRadius <= 0) {
     mContext = aDestinationCtx;
@@ -2556,28 +2560,49 @@ nsContextBoxBlur::Init(const gfxRect& aR
   gfxRect rect = aRect;
   rect.ScaleInverse(aAppUnitsPerDevPixel);
 
   if (rect.IsEmpty()) {
     mContext = aDestinationCtx;
     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;
 }
 
 void
 nsContextBoxBlur::DoPaint()
 {
   if (mContext == mDestinationCtx)
     return;
 
+  mDestinationCtx->Save();
+  mDestinationCtx->NewPath();
+  mDestinationCtx->Rectangle(mRequiredShadowArea);
+  mDestinationCtx->Clip();
   blur.Paint(mDestinationCtx);
+  mDestinationCtx->Restore();
 }
 
 gfxContext*
 nsContextBoxBlur::GetContext()
 {
   return mContext;
 }
+
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -59,17 +59,18 @@ struct nsCSSRendering {
   /**
    * Clean up any static variables used by nsCSSRendering.
    */
   static void Shutdown();
   
   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
    * for borders. aSkipSides is a bitmask of the sides to skip
    * when rendering. If 0 then no sides are skipped.
    *
    * Both aDirtyRect and aBorderArea are in the local coordinate space
    * of aForFrame
@@ -283,31 +284,35 @@ public:
    * @param aDestinationCtx      The graphics context to apply the blurred
    *                             mask to when you call DoPaint(). Make sure
    *                             it is not destroyed before you call
    *                             DoPaint(). To set the color of the
    *                             resulting blurred graphic mask, you must
    *                             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
    *                    aRect. This means you can use coordinates as if it
    *                    were at the desired position at aRect and you don't
    *                    need to worry about translating any coordinates to
    *                    draw on this temporary surface.
    *
    * If aBlurRadius is 0, the returned context is aDestinationCtx and
    * DoPaint() does nothing, because no blurring is required. Therefore, you
    * should prepare the destination context as if you were going to draw
    * 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*
    * have called Init() first, then have drawn whatever they want to be
    * blurred onto the internal gfxContext before calling this.
    */
   void DoPaint();
 
@@ -317,12 +322,14 @@ public:
    * constructed at that point.
    */
   gfxContext* GetContext();
 
 protected:
   gfxAlphaBoxBlur blur;
   nsRefPtr<gfxContext> mContext;
   gfxContext* mDestinationCtx;
+
+  gfxRect mRequiredShadowArea;
   
 };
 
 #endif /* nsCSSRendering_h___ */
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -646,17 +646,17 @@ nsDisplayBorder::Paint(nsDisplayListBuil
                               mFrame->GetSkipSides());
 }
 
 void
 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
 nsDisplayBoxShadow::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsIFrame* aFrame, nsDisplayList* aList)
--- a/layout/generic/nsHTMLContainerFrame.cpp
+++ b/layout/generic/nsHTMLContainerFrame.cpp
@@ -197,20 +197,22 @@ nsDisplayTextShadow::Paint(nsDisplayList
 
   nsHTMLContainerFrame* f = static_cast<nsHTMLContainerFrame*>(mFrame);
   nsMargin bp = f->GetUsedBorderAndPadding();
   nscoord innerWidthInAppUnits = (mFrame->GetSize().width - bp.LeftRight());
 
   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;
 
   thebesCtx->Save();
   thebesCtx->NewPath();
   thebesCtx->SetColor(gfxRGBA(mColor));
 
   if (mDecorationFlags & NS_STYLE_TEXT_DECORATION_UNDERLINE) {
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -4145,17 +4145,17 @@ nsTextFrame::PaintOneShadow(PRUint32 aOf
   // The origin of mBoundingBox is the text baseline left, so we must translate it by
   // that much in order to make the origin the top-left corner of the text bounding box.
   gfxRect shadowRect = shadowMetrics.mBoundingBox +
     gfxPoint(aFramePt.x, aTextBaselinePt.y) + shadowOffset;
 
   nsContextBoxBlur contextBoxBlur;
   gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, blurRadius,
                                                   PresContext()->AppUnitsPerDevPixel(),
-                                                  aCtx);
+                                                  aCtx, aDirtyRect);
   if (!shadowContext)
     return;
 
   nscolor shadowColor;
   if (aShadowDetails->mHasColor)
     shadowColor = aShadowDetails->mColor;
   else
     shadowColor = aForegroundColor;
--- a/layout/xul/base/src/nsTextBoxFrame.cpp
+++ b/layout/xul/base/src/nsTextBoxFrame.cpp
@@ -382,17 +382,18 @@ nsTextBoxFrame::PaintTitle(nsIRenderingC
     const nsStyleText* textStyle = GetStyleText();
     if (textStyle->mTextShadow) {
       // Text shadow happens with the last value being painted at the back,
       // ie. it is painted first.
       for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
         PaintOneShadow(aRenderingContext.ThebesContext(),
                        textRect,
                        textStyle->mTextShadow->ShadowAt(i - 1),
-                       GetStyleColor()->mColor);
+                       GetStyleColor()->mColor,
+                       aDirtyRect);
       }
     }
 
     DrawText(aRenderingContext, textRect, nsnull);
 }
 
 void
 nsTextBoxFrame::DrawText(nsIRenderingContext& aRenderingContext,
@@ -558,30 +559,32 @@ nsTextBoxFrame::DrawText(nsIRenderingCon
                                           NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
                                           NS_STYLE_BORDER_STYLE_SOLID);
     }
 }
 
 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);
 
   nsRect shadowRect(aTextRect);
   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;
 
   nscolor shadowColor;
   if (aShadowDetails->mHasColor)
     shadowColor = aShadowDetails->mColor;
   else
--- a/layout/xul/base/src/nsTextBoxFrame.h
+++ b/layout/xul/base/src/nsTextBoxFrame.h
@@ -129,17 +129,18 @@ private:
 
   void DrawText(nsIRenderingContext& aRenderingContext,
                          const nsRect&        aTextRect,
                          const nscolor*       aOverrideColor);
 
   void PaintOneShadow(gfxContext *     aCtx,
                       const nsRect&    aTextRect,
                       nsCSSShadowItem* aShadowDetails,
-                      const nscolor&   aForegroundColor);
+                      const nscolor&   aForegroundColor,
+                      const nsRect&    aDirtyRect);
 
 
   CroppingStyle mCropType;
   nsString mTitle;
   nsString mCroppedTitle;
   nsString mAccessKey;
   nscoord mTitleWidth;
   nsAccessKeyInfo* mAccessKeyInfo;