From cee682b980ea5c6f5b871458a02f3b146233b5b4 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 9 Oct 2024 12:08:46 -0700 Subject: [PATCH] PR #2643 - The border-radius Directive Should Apply to Outlines However, `-moz-outline-radius` should still override any `border-radius`. This is primarily done for backward compatibility with some themes. Additionally, it also allows for more advanced outline control than the CSS spec provides, without breaking spec. Finally, if the spec is ever updated to include `outline-radius`, we'll be ready to drop the `-moz-` prefix! Note: BZ 315209 has an inadvertent double-negative in nsDisplayList.cpp: HasRadius() essentially returns the opposite value it should. --- layout/base/nsCSSRendering.cpp | 57 ++++++++++++++++++---------------- layout/base/nsDisplayList.cpp | 19 +++++++----- layout/base/nsDisplayList.h | 3 ++ layout/style/nsStyleStruct.cpp | 2 ++ 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index c683a19605..54f965e9d2 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -855,8 +855,6 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, const nsRect& aBorderArea, nsStyleContext* aStyleContext) { - nscoord twipsRadii[8]; - // Get our style context's color struct. const nsStyleOutline* ourOutline = aStyleContext->StyleOutline(); MOZ_ASSERT(ourOutline != NS_STYLE_BORDER_STYLE_NONE, @@ -898,24 +896,6 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, if (innerRect.Contains(aDirtyRect)) return; - nsRect outerRect = innerRect; - outerRect.Inflate(width, width); - - // get the radius for our outline - nsIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius, aBorderArea.Size(), - outerRect.Size(), Sides(), twipsRadii); - - // Get our conversion values - nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1); - - // get the outer rectangles - Rect oRect(NSRectToRect(outerRect, twipsPerPixel)); - - // convert the radii - nsMargin outlineMargin(width, width, width, width); - RectCornerRadii outlineRadii; - ComputePixelRadii(twipsRadii, twipsPerPixel, &outlineRadii); - if (outlineStyle == NS_STYLE_BORDER_STYLE_AUTO) { if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) { nsITheme* theme = aPresContext->GetTheme(); @@ -935,6 +915,36 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, outlineStyle = NS_STYLE_BORDER_STYLE_SOLID; } + RectCornerRadii outlineRadii; + nsRect outerRect = innerRect; + outerRect.Inflate(width, width); + + const nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel(); + Rect oRect(NSRectToRect(outerRect, oneDevPixel)); + + const Float outlineWidths[4] = { + Float(width) / oneDevPixel, Float(width) / oneDevPixel, + Float(width) / oneDevPixel, Float(width) / oneDevPixel}; + + // convert the radii + nscoord twipsRadii[8]; + + // get the radius for our outline + if (nsLayoutUtils::HasNonZeroCorner(ourOutline->mOutlineRadius)) { + nsIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius, aBorderArea.Size(), + outerRect.Size(), Sides(), twipsRadii); + ComputePixelRadii(twipsRadii, oneDevPixel, &outlineRadii); + } else if (aForFrame->GetBorderRadii(twipsRadii)) { + RectCornerRadii innerRadii; + ComputePixelRadii(twipsRadii, oneDevPixel, &innerRadii); + + Float devPixelOffset = aPresContext->AppUnitsToFloatDevPixels(offset); + const Float widths[4] = { + outlineWidths[0] + devPixelOffset, outlineWidths[1] + devPixelOffset, + outlineWidths[2] + devPixelOffset, outlineWidths[3] + devPixelOffset}; + nsCSSBorderRenderer::ComputeOuterRadii(innerRadii, widths, &outlineRadii); + } + uint8_t outlineStyles[4] = { outlineStyle, outlineStyle, outlineStyle, outlineStyle }; @@ -947,12 +957,7 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, outlineColor, outlineColor }; - // convert the border widths - Float outlineWidths[4] = { Float(width / twipsPerPixel), - Float(width / twipsPerPixel), - Float(width / twipsPerPixel), - Float(width / twipsPerPixel) }; - Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel); + Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel); nsIDocument* document = nullptr; nsIContent* content = aForFrame->GetContent(); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index fa28ea5f87..825c564211 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -3757,18 +3757,23 @@ nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, mFrame->StyleContext()); } +bool nsDisplayOutline::HasRadius() const { + if (nsLayoutUtils::HasNonZeroCorner(mFrame->StyleOutline()->mOutlineRadius)) { + return true; + } + return nsLayoutUtils::HasNonZeroCorner(mFrame->StyleBorder()->mBorderRadius); +} + bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) { const nsStyleOutline* outline = mFrame->StyleOutline(); nsRect borderBox(ToReferenceFrame(), mFrame->GetSize()); - if (borderBox.Contains(aRect) && - !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) { - if (outline->mOutlineOffset >= 0) { - // aRect is entirely inside the border-rect, and the outline isn't - // rendered inside the border-rect, so the outline is not visible. - return true; - } + if (borderBox.Contains(aRect) && !HasRadius() && + outline->mOutlineOffset >= 0) { + // aRect is entirely inside the border-rect, and the outline isn't + // rendered inside the border-rect, so the outline is not visible. + return true; } return false; diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 2b4efd86ef..2f3f53b7fc 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -3220,6 +3220,9 @@ public: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override; virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override; NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE) + +private: + bool HasRadius() const; }; /** diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 4f95ddaeb0..9de0f13c00 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -510,6 +510,8 @@ nsStyleBorder::CalcDifference(const nsStyleBorder& aNewData) const } } + // Note that border radius is used as a fallback for outline radius, if set. + // Any optimizations here should apply to both. if (mBorderRadius != aNewData.mBorderRadius || !mBorderColors != !aNewData.mBorderColors) { return nsChangeHint_RepaintFrame;