Bug 1340317 - Support box shadow outer offset and spread radius in WebRender box shadows. r=lsalzman

This commit is contained in:
Mason Chang 2017-02-16 15:05:34 -08:00
Родитель 8ec93b1895
Коммит b4d5056da5
5 изменённых файлов: 111 добавлений и 56 удалений

Просмотреть файл

@ -227,6 +227,14 @@ static inline WrRect ToWrRect(const gfx::IntRectTyped<T>& rect)
return ToWrRect(IntRectToRect(rect));
}
static inline WrPoint ToWrPoint(const gfx::Point& point)
{
WrPoint p;
p.x = point.x;
p.y = point.y;
return p;
}
struct ByteBuffer
{
ByteBuffer(size_t aLength, uint8_t* aData)

Просмотреть файл

@ -283,6 +283,7 @@ struct WrPoint
return x == aRhs.x && y == aRhs.y;
}
operator mozilla::gfx::Point() const { return mozilla::gfx::Point(x, y); }
};
struct WrImageMask

Просмотреть файл

@ -1369,6 +1369,56 @@ nsCSSRendering::EndFrameTreesLocked()
}
}
bool
nsCSSRendering::HasBoxShadowNativeTheme(nsIFrame* aFrame,
bool& aMaybeHasBorderRadius)
{
const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
nsITheme::Transparency transparency;
if (aFrame->IsThemed(styleDisplay, &transparency)) {
aMaybeHasBorderRadius = false;
// For opaque (rectangular) theme widgets we can take the generic
// border-box path with border-radius disabled.
return transparency != nsITheme::eOpaque;
}
aMaybeHasBorderRadius = true;
return false;
}
gfx::Color
nsCSSRendering::GetShadowColor(nsCSSShadowItem* aShadow,
nsIFrame* aFrame,
float aOpacity)
{
// Get the shadow color; if not specified, use the foreground color
nscolor shadowColor;
if (aShadow->mHasColor)
shadowColor = aShadow->mColor;
else
shadowColor = aFrame->StyleColor()->mColor;
Color color = Color::FromABGR(shadowColor);
color.a *= aOpacity;
return color;
}
nsRect
nsCSSRendering::GetShadowRect(const nsRect aFrameArea,
bool aNativeTheme,
nsIFrame* aForFrame)
{
nsRect frameRect = aNativeTheme ?
aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
aFrameArea;
Sides skipSides = aForFrame->GetSkipSides();
frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
// Explicitly do not need to account for the spread radius here
// Webrender does it for us or PaintBoxShadow will for non-WR
return frameRect;
}
void
nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
@ -1383,25 +1433,11 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
return;
bool hasBorderRadius;
bool nativeTheme; // mutually exclusive with hasBorderRadius
// mutually exclusive with hasBorderRadius
bool nativeTheme = HasBoxShadowNativeTheme(aForFrame, hasBorderRadius);
const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();
nsITheme::Transparency transparency;
if (aForFrame->IsThemed(styleDisplay, &transparency)) {
// We don't respect border-radius for native-themed widgets
hasBorderRadius = false;
// For opaque (rectangular) theme widgets we can take the generic
// border-box path with border-radius disabled.
nativeTheme = transparency != nsITheme::eOpaque;
} else {
nativeTheme = false;
hasBorderRadius = true; // we'll update this below
}
nsRect frameRect = nativeTheme ?
aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
aFrameArea;
Sides skipSides = aForFrame->GetSkipSides();
frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
nsRect frameRect = GetShadowRect(aFrameArea, nativeTheme, aForFrame);
// Get any border radius, since box-shadow must also have rounded corners if
// the frame does.
@ -1465,15 +1501,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
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;
Color gfxShadowColor(Color::FromABGR(shadowColor));
gfxShadowColor.a *= aOpacity;
Color gfxShadowColor = GetShadowColor(shadowItem, aForFrame, aOpacity);
if (nativeTheme) {
nsContextBoxBlur blurringArea;
@ -1547,6 +1575,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
// Clip the shadow so that we only get the part that applies to aForFrame.
nsRect fragmentClip = shadowRectPlusBlur;
Sides skipSides = aForFrame->GetSkipSides();
if (!skipSides.IsEmpty()) {
if (skipSides.Left()) {
nscoord xmost = fragmentClip.XMost();
@ -1740,11 +1769,7 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
Rect shadowGfxRect = NSRectToRect(paddingRect, twipsPerPixel);
shadowGfxRect.Round();
// Set the shadow color; if not specified, use the foreground color
Color shadowColor = Color::FromABGR(shadowItem->mHasColor ?
shadowItem->mColor :
aForFrame->StyleColor()->mColor);
Color shadowColor = GetShadowColor(shadowItem, aForFrame, 1.0);
renderContext->Save();
// This clips the outside border radius.

Просмотреть файл

@ -354,6 +354,7 @@ struct nsBackgroundLayerState {
};
struct nsCSSRendering {
typedef mozilla::gfx::Color Color;
typedef mozilla::gfx::CompositionOp CompositionOp;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::Float Float;
@ -379,6 +380,17 @@ struct nsCSSRendering {
nsIFrame* aForFrame,
const nsRect& aFrameArea);
static nsRect GetShadowRect(const nsRect aFrameArea,
bool aNativeTheme,
nsIFrame* aForFrame);
static mozilla::gfx::Color GetShadowColor(nsCSSShadowItem* aShadow,
nsIFrame* aFrame,
float aOpacity);
// Returns if the frame has a themed frame.
// aMaybeHasBorderRadius will return false if we can early detect
// that we don't have a border radius.
static bool HasBoxShadowNativeTheme(nsIFrame* aFrame,
bool& aMaybeHasBorderRadius);
static void PaintBoxShadowOuter(nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
nsIFrame* aForFrame,

Просмотреть файл

@ -4771,45 +4771,54 @@ nsDisplayBoxShadowOuter::CreateWebRenderCommands(nsTArray<WebRenderCommand>& aCo
if (!shadows)
return;
bool hasBorderRadius;
bool nativeTheme = nsCSSRendering::HasBoxShadowNativeTheme(mFrame,
hasBorderRadius);
// Everything here is in app units, change to device units.
for (uint32_t i = 0; i < rects.Length(); ++i) {
Rect clipRect = NSRectToRect(rects[i], appUnitsPerDevPixel);
Rect gfxBorderRect = NSRectToRect(borderRect, appUnitsPerDevPixel);
nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
Rect deviceClipRect = aLayer->RelativeToParent(clipRect);
Rect deviceBoxRect = aLayer->RelativeToParent(gfxBorderRect);
for (uint32_t j = shadows->Length(); j > 0; j--) {
nsCSSShadowItem* shadow = shadows->ShadowAt(j - 1);
// Don't need the full size of the shadow rect like we do in
// nsCSSRendering since WR takes care of calculations for blur
// and spread radius.
nsRect shadowRect = nsCSSRendering::GetShadowRect(borderRect,
nativeTheme,
mFrame);
gfx::Color shadowColor = nsCSSRendering::GetShadowColor(shadow,
mFrame,
mOpacity);
shadowRect.MoveBy(shadow->mXOffset, shadow->mYOffset);
for (uint32_t j = shadows->Length(); j > 0; --j) {
nsCSSShadowItem* shadowItem = shadows->ShadowAt(j - 1);
nscoord blurRadius = shadowItem->mRadius;
float gfxBlurRadius = blurRadius / appUnitsPerDevPixel;
// Now translate everything to device pixels.
Point shadowOffset;
shadowOffset.x = (shadow->mXOffset / appUnitsPerDevPixel);
shadowOffset.y = (shadow->mYOffset / appUnitsPerDevPixel);
// TODO: Have to refactor our nsCSSRendering
// to get the acual rects correct.
nscolor shadowColor;
if (shadowItem->mHasColor)
shadowColor = shadowItem->mColor;
else
shadowColor = mFrame->StyleColor()->mColor;
Rect deviceBoxRect = NSRectToRect(shadowRect, appUnitsPerDevPixel);
deviceBoxRect = aLayer->RelativeToParent(deviceBoxRect);
Color gfxShadowColor(Color::FromABGR(shadowColor));
gfxShadowColor.a *= mOpacity;
Rect deviceClipRect = aLayer->RelativeToParent(clipRect + shadowOffset);
WrPoint offset;
offset.x = shadowItem->mXOffset;
offset.y = shadowItem->mYOffset;
float blurRadius = shadow->mRadius / appUnitsPerDevPixel;
// TODO: Calculate the border radius here.
float borderRadius = 0.0;
float spreadRadius = shadow->mSpread / appUnitsPerDevPixel;
aCommands.AppendElement(OpDPPushBoxShadow(
wr::ToWrRect(deviceBoxRect),
wr::ToWrRect(deviceClipRect),
wr::ToWrRect(deviceBoxRect),
offset,
wr::ToWrColor(gfxShadowColor),
gfxBlurRadius,
0,
0,
wr::ToWrPoint(shadowOffset),
wr::ToWrColor(shadowColor),
blurRadius,
spreadRadius,
borderRadius,
WrBoxShadowClipMode::Outset
));
));
}
}
}