Bug 1090614 - Make nsCSSRendering::PaintBoxShadowOuter construct a Moz2D Path to do its clipping. r=mattwoodrow
authorJonathan Watt <jwatt@jwatt.org>
Fri, 31 Oct 2014 11:16:27 +0000
changeset 239809 e5eef92b2548f18c8ee50711f5ba98e79f27005c
parent 239808 85aa67127933ec6867a1116a024a9eba71ca2b28
child 239810 920fc43b861f5335d32e830b96e4db793399bd56
push id660
push userraliiev@mozilla.com
push dateWed, 18 Feb 2015 20:30:48 +0000
treeherdermozilla-release@49e493494178 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1090614
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1090614 - Make nsCSSRendering::PaintBoxShadowOuter construct a Moz2D Path to do its clipping. r=mattwoodrow
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/base/nsCSSRenderingBorders.cpp
layout/base/nsCSSRenderingBorders.h
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -8,16 +8,17 @@
 
 #include <ctime>
 
 #include "gfx2DGlue.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "nsStyleConsts.h"
 #include "nsPresContext.h"
 #include "nsIFrame.h"
 #include "nsPoint.h"
 #include "nsRect.h"
@@ -572,16 +573,35 @@ nsCSSRendering::ComputePixelRadii(const 
   (*oBorderRadii)[C_TR] = gfxSize(radii[NS_CORNER_TOP_RIGHT_X],
                                   radii[NS_CORNER_TOP_RIGHT_Y]);
   (*oBorderRadii)[C_BR] = gfxSize(radii[NS_CORNER_BOTTOM_RIGHT_X],
                                   radii[NS_CORNER_BOTTOM_RIGHT_Y]);
   (*oBorderRadii)[C_BL] = gfxSize(radii[NS_CORNER_BOTTOM_LEFT_X],
                                   radii[NS_CORNER_BOTTOM_LEFT_Y]);
 }
 
+/* static */ void
+nsCSSRendering::ComputePixelRadii(const nscoord *aAppUnitsRadii,
+                                  nscoord aAppUnitsPerPixel,
+                                  RectCornerRadii *oBorderRadii)
+{
+  Float radii[8];
+  NS_FOR_CSS_HALF_CORNERS(corner)
+    radii[corner] = Float(aAppUnitsRadii[corner]) / aAppUnitsPerPixel;
+
+  (*oBorderRadii)[C_TL] = Size(radii[NS_CORNER_TOP_LEFT_X],
+                               radii[NS_CORNER_TOP_LEFT_Y]);
+  (*oBorderRadii)[C_TR] = Size(radii[NS_CORNER_TOP_RIGHT_X],
+                               radii[NS_CORNER_TOP_RIGHT_Y]);
+  (*oBorderRadii)[C_BR] = Size(radii[NS_CORNER_BOTTOM_RIGHT_X],
+                               radii[NS_CORNER_BOTTOM_RIGHT_Y]);
+  (*oBorderRadii)[C_BL] = Size(radii[NS_CORNER_BOTTOM_LEFT_X],
+                               radii[NS_CORNER_BOTTOM_LEFT_Y]);
+}
+
 void
 nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
                             nsRenderingContext& aRenderingContext,
                             nsIFrame* aForFrame,
                             const nsRect& aDirtyRect,
                             const nsRect& aBorderArea,
                             nsStyleContext* aStyleContext,
                             Sides aSkipSides)
@@ -1189,16 +1209,17 @@ nsCSSRendering::EndFrameTreesLocked()
 void
 nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
                                     nsRenderingContext& aRenderingContext,
                                     nsIFrame* aForFrame,
                                     const nsRect& aFrameArea,
                                     const nsRect& aDirtyRect,
                                     float aOpacity)
 {
+  DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
   const nsStyleBorder* styleBorder = aForFrame->StyleBorder();
   nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
   if (!shadows)
     return;
 
   gfxContextAutoSaveRestore gfxStateRestorer;
   bool hasBorderRadius;
   bool nativeTheme; // mutually exclusive with hasBorderRadius
@@ -1218,35 +1239,35 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
   nsRect frameRect = nativeTheme ?
     aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
     aFrameArea;
   Sides skipSides = aForFrame->GetSkipSides();
   frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
 
   // Get any border radius, since box-shadow must also have rounded corners if
   // the frame does.
-  gfxCornerSizes borderRadii;
+  RectCornerRadii borderRadii;
   const nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
   if (hasBorderRadius) {
     nscoord twipsRadii[8];
     NS_ASSERTION(aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(),
                  "unexpected size");
     nsSize sz = frameRect.Size();
     hasBorderRadius = aForFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
     if (hasBorderRadius) {
       ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
     }
   }
 
-  gfxRect frameGfxRect(nsLayoutUtils::RectToGfxRect(frameRect, twipsPerPixel));
+  Rect frameGfxRect = NSRectToRect(frameRect, twipsPerPixel);
   frameGfxRect.Round();
 
   // We don't show anything that intersects with the frame we're blurring on. So tell the
   // blurrer not to do unnecessary work there.
-  gfxRect skipGfxRect = frameGfxRect;
+  gfxRect skipGfxRect = ThebesRect(frameGfxRect);
   bool useSkipGfxRect = true;
   if (nativeTheme) {
     // Optimize non-leaf native-themed frames by skipping computing pixels
     // in the padding-box. We assume the padding-box is going to be painted
     // opaquely for non-leaf frames.
     // XXX this may not be a safe assumption; we should make this go away
     // by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
     useSkipGfxRect = !aForFrame->IsLeaf();
@@ -1254,16 +1275,18 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
       aForFrame->GetPaddingRect() - aForFrame->GetPosition() + aFrameArea.TopLeft();
     skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
   } else if (hasBorderRadius) {
     skipGfxRect.Deflate(gfxMargin(
         std::max(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
         std::max(borderRadii[C_BL].height, borderRadii[C_BR].height), 0));
   }
 
+  gfxContext* renderContext = aRenderingContext.ThebesContext();
+
   for (uint32_t i = shadows->Length(); i > 0; --i) {
     nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
     if (shadowItem->mInset)
       continue;
 
     nsRect shadowRect = frameRect;
     shadowRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
     if (!nativeTheme) {
@@ -1272,47 +1295,46 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
 
     // shadowRect won't include the blur, so make an extra rect here that includes the blur
     // for use in the even-odd rule below.
     nsRect shadowRectPlusBlur = shadowRect;
     nscoord blurRadius = shadowItem->mRadius;
     shadowRectPlusBlur.Inflate(
       nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel));
 
-    gfxRect shadowGfxRectPlusBlur =
-      nsLayoutUtils::RectToGfxRect(shadowRectPlusBlur, twipsPerPixel);
+    Rect shadowGfxRectPlusBlur =
+      NSRectToRect(shadowRectPlusBlur, twipsPerPixel);
     shadowGfxRectPlusBlur.RoundOut();
+    MaybeSnapToDevicePixels(shadowGfxRectPlusBlur, aDrawTarget, true);
 
     // Set the shadow color; if not specified, use the foreground color
     nscolor shadowColor;
     if (shadowItem->mHasColor)
       shadowColor = shadowItem->mColor;
     else
       shadowColor = aForFrame->StyleColor()->mColor;
 
     gfxRGBA gfxShadowColor(shadowColor);
     gfxShadowColor.a *= aOpacity;
 
-    gfxContext* renderContext = aRenderingContext.ThebesContext();
     if (nativeTheme) {
       nsContextBoxBlur blurringArea;
 
       // When getting the widget shape from the native theme, we're going
       // to draw the widget into the shadow surface to create a mask.
       // We need to ensure that there actually *is* a shadow surface
       // and that we're not going to draw directly into renderContext.
       gfxContext* shadowContext =
         blurringArea.Init(shadowRect, shadowItem->mSpread,
                           blurRadius, twipsPerPixel, renderContext, aDirtyRect,
                           useSkipGfxRect ? &skipGfxRect : nullptr,
                           nsContextBoxBlur::FORCE_MASK);
       if (!shadowContext)
         continue;
 
-      // shadowContext is owned by either blurringArea or aRenderingContext.
       MOZ_ASSERT(shadowContext == blurringArea.GetContext());
 
       renderContext->Save();
       renderContext->SetColor(gfxShadowColor);
 
       // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
       // doesn't make any temporary surfaces if blur is 0 and it just returns the original
       // surface? If we have no blur, we're painting this fill on the actual content surface
@@ -1337,29 +1359,32 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
       nativeRect.IntersectRect(frameRect, aDirtyRect);
       aPresContext->GetTheme()->DrawWidgetBackground(wrapperCtx, aForFrame,
           styleDisplay->mAppearance, aFrameArea, nativeRect);
 
       blurringArea.DoPaint();
       renderContext->Restore();
     } else {
       renderContext->Save();
-      // Clip out the area of the actual frame so the shadow is not shown within
-      // the frame.
-      renderContext->NewPath();
-      renderContext->Rectangle(shadowGfxRectPlusBlur);
-      if (hasBorderRadius) {
-        renderContext->RoundedRectangle(frameGfxRect, borderRadii);
-      } else {
-        renderContext->Rectangle(frameGfxRect);
+
+      {
+        // Clip out the interior of the frame's border edge so that the shadow
+        // is only painted outside that area.
+        RefPtr<PathBuilder> builder =
+          aDrawTarget.CreatePathBuilder(FillRule::FILL_EVEN_ODD);
+        AppendRectToPath(builder, shadowGfxRectPlusBlur);
+        if (hasBorderRadius) {
+          AppendRoundedRectToPath(builder, frameGfxRect, borderRadii);
+        } else {
+          AppendRectToPath(builder, frameGfxRect);
+        }
+        RefPtr<Path> path = builder->Finish();
+        renderContext->Clip(path);
       }
 
-      renderContext->SetFillRule(FillRule::FILL_EVEN_ODD);
-      renderContext->Clip();
-
       // Clip the shadow so that we only get the part that applies to aForFrame.
       nsRect fragmentClip = shadowRectPlusBlur;
       if (!skipSides.IsEmpty()) {
         if (skipSides.Left()) {
           nscoord xmost = fragmentClip.XMost();
           fragmentClip.x = aFrameArea.x;
           fragmentClip.width = xmost - fragmentClip.x;
         }
@@ -1378,26 +1403,26 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
         if (skipSides.Bottom()) {
           nscoord ymost = fragmentClip.YMost();
           nscoord overflow = ymost - aFrameArea.YMost();
           if (overflow > 0) {
             fragmentClip.height -= overflow;
           }
         }
       }
-      aRenderingContext.ThebesContext()->
+      renderContext->
         Clip(NSRectToSnappedRect(fragmentClip,
                                  aForFrame->PresContext()->AppUnitsPerDevPixel(),
-                                 *aRenderingContext.GetDrawTarget()));
+                                 aDrawTarget));
 
       gfxCornerSizes clipRectRadii;
       if (hasBorderRadius) {
-        gfxFloat spreadDistance = shadowItem->mSpread / twipsPerPixel;
-
-        gfxFloat borderSizes[4];
+        Float spreadDistance = shadowItem->mSpread / twipsPerPixel;
+
+        Float borderSizes[4];
 
         borderSizes[NS_SIDE_LEFT] = spreadDistance;
         borderSizes[NS_SIDE_TOP] = spreadDistance;
         borderSizes[NS_SIDE_RIGHT] = spreadDistance;
         borderSizes[NS_SIDE_BOTTOM] = spreadDistance;
 
         nsCSSBorderRenderer::ComputeOuterRadii(borderRadii, borderSizes,
             &clipRectRadii);
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -19,16 +19,17 @@ class gfxDrawable;
 class nsStyleContext;
 class nsPresContext;
 class nsRenderingContext;
 
 namespace mozilla {
 
 namespace gfx {
 class DrawTarget;
+struct RectCornerRadii;
 }
 
 namespace layers {
 class ImageContainer;
 }
 
 // A CSSSizeOrRatio represents a (possibly partially specified) size for use
 // in computing image sizes. Either or both of the width and height might be
@@ -314,16 +315,17 @@ struct nsBackgroundLayerState {
    */
   gfxContext::GraphicsOperator mCompositingOp;
 };
 
 struct nsCSSRendering {
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Rect Rect;
+  typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
   typedef nsIFrame::Sides Sides;
 
   /**
    * Initialize any static variables used by nsCSSRendering.
    */
   static void Init();
   
   /**
@@ -341,16 +343,20 @@ struct nsCSSRendering {
                                   nsRenderingContext& aRenderingContext,
                                   nsIFrame* aForFrame,
                                   const nsRect& aFrameArea,
                                   const nsRect& aDirtyRect,
                                   float aOpacity = 1.0);
 
   static void ComputePixelRadii(const nscoord *aAppUnitsRadii,
                                 nscoord aAppUnitsPerPixel,
+                                RectCornerRadii *oBorderRadii);
+
+  static void ComputePixelRadii(const nscoord *aAppUnitsRadii,
+                                nscoord aAppUnitsPerPixel,
                                 gfxCornerSizes *oBorderRadii);
 
   /**
    * Render the border for an element using css rendering rules
    * for borders. aSkipSides says which sides to skip
    * when rendering, the default is to skip none.
    */
   static void PaintBorder(nsPresContext* aPresContext,
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -2,16 +2,18 @@
 // vim:cindent:ts=2:et:sw=2:
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsCSSRenderingBorders.h"
 
 #include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
 #include "nsStyleConsts.h"
 #include "nsCSSColorUtils.h"
 #include "GeckoProfiler.h"
 #include "nsExpirationTracker.h"
 #include "RoundedRect.h"
 #include "nsClassHashtable.h"
 #include "nsStyleStruct.h"
 #include "gfxContext.h"
@@ -168,44 +170,44 @@ nsCSSBorderRenderer::ComputeInnerRadii(c
   iRadii[C_BR].width = std::max(0.0, aRadii[C_BR].width - aBorderSizes[NS_SIDE_RIGHT]);
   iRadii[C_BR].height = std::max(0.0, aRadii[C_BR].height - aBorderSizes[NS_SIDE_BOTTOM]);
 
   iRadii[C_BL].width = std::max(0.0, aRadii[C_BL].width - aBorderSizes[NS_SIDE_LEFT]);
   iRadii[C_BL].height = std::max(0.0, aRadii[C_BL].height - aBorderSizes[NS_SIDE_BOTTOM]);
 }
 
 /* static */ void
-nsCSSBorderRenderer::ComputeOuterRadii(const gfxCornerSizes& aRadii,
-                                       const gfxFloat *aBorderSizes,
+nsCSSBorderRenderer::ComputeOuterRadii(const RectCornerRadii& aRadii,
+                                       const Float *aBorderSizes,
                                        gfxCornerSizes *aOuterRadiiRet)
 {
   gfxCornerSizes& oRadii = *aOuterRadiiRet;
 
   // default all corners to sharp corners
   oRadii = gfxCornerSizes(0.0);
 
   // round the edges that have radii > 0.0 to start with
-  if (aRadii[C_TL].width > 0.0 && aRadii[C_TL].height > 0.0) {
-    oRadii[C_TL].width = std::max(0.0, aRadii[C_TL].width + aBorderSizes[NS_SIDE_LEFT]);
-    oRadii[C_TL].height = std::max(0.0, aRadii[C_TL].height + aBorderSizes[NS_SIDE_TOP]);
+  if (aRadii[C_TL].width > 0.f && aRadii[C_TL].height > 0.f) {
+    oRadii[C_TL].width = std::max(0.f, aRadii[C_TL].width + aBorderSizes[NS_SIDE_LEFT]);
+    oRadii[C_TL].height = std::max(0.f, aRadii[C_TL].height + aBorderSizes[NS_SIDE_TOP]);
   }
 
-  if (aRadii[C_TR].width > 0.0 && aRadii[C_TR].height > 0.0) {
-    oRadii[C_TR].width = std::max(0.0, aRadii[C_TR].width + aBorderSizes[NS_SIDE_RIGHT]);
-    oRadii[C_TR].height = std::max(0.0, aRadii[C_TR].height + aBorderSizes[NS_SIDE_TOP]);
+  if (aRadii[C_TR].width > 0.f && aRadii[C_TR].height > 0.f) {
+    oRadii[C_TR].width = std::max(0.f, aRadii[C_TR].width + aBorderSizes[NS_SIDE_RIGHT]);
+    oRadii[C_TR].height = std::max(0.f, aRadii[C_TR].height + aBorderSizes[NS_SIDE_TOP]);
   }
 
-  if (aRadii[C_BR].width > 0.0 && aRadii[C_BR].height > 0.0) {
-    oRadii[C_BR].width = std::max(0.0, aRadii[C_BR].width + aBorderSizes[NS_SIDE_RIGHT]);
-    oRadii[C_BR].height = std::max(0.0, aRadii[C_BR].height + aBorderSizes[NS_SIDE_BOTTOM]);
+  if (aRadii[C_BR].width > 0.f && aRadii[C_BR].height > 0.f) {
+    oRadii[C_BR].width = std::max(0.f, aRadii[C_BR].width + aBorderSizes[NS_SIDE_RIGHT]);
+    oRadii[C_BR].height = std::max(0.f, aRadii[C_BR].height + aBorderSizes[NS_SIDE_BOTTOM]);
   }
 
-  if (aRadii[C_BL].width > 0.0 && aRadii[C_BL].height > 0.0) {
-    oRadii[C_BL].width = std::max(0.0, aRadii[C_BL].width + aBorderSizes[NS_SIDE_LEFT]);
-    oRadii[C_BL].height = std::max(0.0, aRadii[C_BL].height + aBorderSizes[NS_SIDE_BOTTOM]);
+  if (aRadii[C_BL].width > 0.f && aRadii[C_BL].height > 0.f) {
+    oRadii[C_BL].width = std::max(0.f, aRadii[C_BL].width + aBorderSizes[NS_SIDE_LEFT]);
+    oRadii[C_BL].height = std::max(0.f, aRadii[C_BL].height + aBorderSizes[NS_SIDE_BOTTOM]);
   }
 }
 
 /*static*/ void
 ComputeBorderCornerDimensions(const gfxRect& aOuterRect,
                               const gfxRect& aInnerRect,
                               const gfxCornerSizes& aRadii,
                               gfxCornerSizes *aDimsRet)
--- a/layout/base/nsCSSRenderingBorders.h
+++ b/layout/base/nsCSSRenderingBorders.h
@@ -16,16 +16,17 @@
 
 class gfxContext;
 struct gfxRGBA;
 struct nsBorderColors;
 
 namespace mozilla {
 namespace gfx {
 class GradientStops;
+struct RectCornerRadii;
 }
 }
 
 // define this to enable a bunch of debug dump info
 #undef DEBUG_NEW_BORDERS
 
 //thickness of dashed line relative to dotted line
 #define DOT_LENGTH  1           //square
@@ -71,16 +72,19 @@ class GradientStops;
 typedef enum {
   BorderColorStyleNone,
   BorderColorStyleSolid,
   BorderColorStyleLight,
   BorderColorStyleDark
 } BorderColorStyle;
 
 struct nsCSSBorderRenderer {
+  typedef mozilla::gfx::Float Float;
+  typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
+
   nsCSSBorderRenderer(int32_t aAppUnitsPerPixel,
                       gfxContext* aDestContext,
                       gfxRect& aOuterRect,
                       const uint8_t* aBorderStyles,
                       const gfxFloat* aBorderWidths,
                       gfxCornerSizes& aBorderRadii,
                       const nscolor* aBorderColors,
                       nsBorderColors* const* aCompositeColors,
@@ -214,18 +218,18 @@ struct nsCSSBorderRenderer {
   static void ComputeInnerRadii(const gfxCornerSizes& aRadii,
                                 const gfxFloat *aBorderSizes,
                                 gfxCornerSizes *aInnerRadiiRet);
 
   // Given aRadii as the border radii for a rectangle, compute the
   // appropriate radii for another rectangle *outside* that rectangle
   // by increasing the radii, except keeping sharp corners sharp.
   // Used for spread box-shadows
-  static void ComputeOuterRadii(const gfxCornerSizes& aRadii,
-                                const gfxFloat *aBorderSizes,
+  static void ComputeOuterRadii(const RectCornerRadii& aRadii,
+                                const Float *aBorderSizes,
                                 gfxCornerSizes *aOuterRadiiRet);
 };
 
 namespace mozilla {
 #ifdef DEBUG_NEW_BORDERS
 #include <stdarg.h>
 
 static inline void PrintAsString(const gfxPoint& p) {