diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h index 360ebd55672d..87443bcc95e6 100644 --- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -227,6 +227,14 @@ static inline WrRect ToWrRect(const gfx::IntRectTyped& 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) diff --git a/gfx/webrender_bindings/webrender_ffi.h b/gfx/webrender_bindings/webrender_ffi.h index e57dd636ea15..c14073f20216 100644 --- a/gfx/webrender_bindings/webrender_ffi.h +++ b/gfx/webrender_bindings/webrender_ffi.h @@ -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 diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp index 9a3a86bd2418..fa4c1371e564 100644 --- a/layout/painting/nsCSSRendering.cpp +++ b/layout/painting/nsCSSRendering.cpp @@ -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. diff --git a/layout/painting/nsCSSRendering.h b/layout/painting/nsCSSRendering.h index 55dc35d8d770..3c8007dae08f 100644 --- a/layout/painting/nsCSSRendering.h +++ b/layout/painting/nsCSSRendering.h @@ -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, diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 0e131e4471fd..2fb94fd3c660 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -4771,45 +4771,54 @@ nsDisplayBoxShadowOuter::CreateWebRenderCommands(nsTArray& 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 - )); + )); } } }