Bug 1090614 - Make nsCSSRendering::PaintBoxShadowOuter construct a Moz2D Path to do its clipping. r=mattwoodrow

This commit is contained in:
Jonathan Watt 2014-10-31 11:16:27 +00:00
parent 6a77b8e610
commit 8466e5627a
4 changed files with 75 additions and 38 deletions

View File

@ -13,6 +13,7 @@
#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"
@ -577,6 +578,25 @@ nsCSSRendering::ComputePixelRadii(const nscoord *aAppUnitsRadii,
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,
@ -1194,6 +1214,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
const nsRect& aDirtyRect,
float aOpacity)
{
DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
const nsStyleBorder* styleBorder = aForFrame->StyleBorder();
nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
if (!shadows)
@ -1223,7 +1244,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
// 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];
@ -1236,12 +1257,12 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
}
}
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
@ -1259,6 +1280,8 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
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)
@ -1277,9 +1300,10 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
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;
@ -1291,7 +1315,6 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
gfxRGBA gfxShadowColor(shadowColor);
gfxShadowColor.a *= aOpacity;
gfxContext* renderContext = aRenderingContext.ThebesContext();
if (nativeTheme) {
nsContextBoxBlur blurringArea;
@ -1307,7 +1330,6 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
if (!shadowContext)
continue;
// shadowContext is owned by either blurringArea or aRenderingContext.
MOZ_ASSERT(shadowContext == blurringArea.GetContext());
renderContext->Save();
@ -1342,18 +1364,21 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
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);
}
renderContext->SetFillRule(FillRule::FILL_EVEN_ODD);
renderContext->Clip();
{
// 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);
}
// Clip the shadow so that we only get the part that applies to aForFrame.
nsRect fragmentClip = shadowRectPlusBlur;
@ -1383,16 +1408,16 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
}
}
}
aRenderingContext.ThebesContext()->
renderContext->
Clip(NSRectToSnappedRect(fragmentClip,
aForFrame->PresContext()->AppUnitsPerDevPixel(),
*aRenderingContext.GetDrawTarget()));
aDrawTarget));
gfxCornerSizes clipRectRadii;
if (hasBorderRadius) {
gfxFloat spreadDistance = shadowItem->mSpread / twipsPerPixel;
Float spreadDistance = shadowItem->mSpread / twipsPerPixel;
gfxFloat borderSizes[4];
Float borderSizes[4];
borderSizes[NS_SIDE_LEFT] = spreadDistance;
borderSizes[NS_SIDE_TOP] = spreadDistance;

View File

@ -24,6 +24,7 @@ namespace mozilla {
namespace gfx {
class DrawTarget;
struct RectCornerRadii;
}
namespace layers {
@ -319,6 +320,7 @@ 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;
/**
@ -344,6 +346,10 @@ struct nsCSSRendering {
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);

View File

@ -7,6 +7,8 @@
#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"
@ -173,8 +175,8 @@ nsCSSBorderRenderer::ComputeInnerRadii(const gfxCornerSizes& aRadii,
}
/* static */ void
nsCSSBorderRenderer::ComputeOuterRadii(const gfxCornerSizes& aRadii,
const gfxFloat *aBorderSizes,
nsCSSBorderRenderer::ComputeOuterRadii(const RectCornerRadii& aRadii,
const Float *aBorderSizes,
gfxCornerSizes *aOuterRadiiRet)
{
gfxCornerSizes& oRadii = *aOuterRadiiRet;
@ -183,24 +185,24 @@ nsCSSBorderRenderer::ComputeOuterRadii(const gfxCornerSizes& aRadii,
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]);
}
}

View File

@ -21,6 +21,7 @@ struct nsBorderColors;
namespace mozilla {
namespace gfx {
class GradientStops;
struct RectCornerRadii;
}
}
@ -76,6 +77,9 @@ typedef enum {
} BorderColorStyle;
struct nsCSSBorderRenderer {
typedef mozilla::gfx::Float Float;
typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
nsCSSBorderRenderer(int32_t aAppUnitsPerPixel,
gfxContext* aDestContext,
gfxRect& aOuterRect,
@ -219,8 +223,8 @@ struct nsCSSBorderRenderer {
// 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);
};