Skip to content

Commit

Permalink
[Outline] Add support for outline with border-radius (non-inline edit…
Browse files Browse the repository at this point in the history
…ion)

https://bugs.webkit.org/show_bug.cgi?id=246064

Reviewed by Antti Koivisto.

1. Compute the radii for inner/outer outline border.
2. Call BorderPainter::paintSides as if we were drawing "border" property.

* Source/WebCore/rendering/BorderPainter.cpp:
(WebCore::BorderPainter::paintOutline):

(Test needs fuzzy adjustment due to the slightly different border radius values to mimic outline.)

Canonical link: https://commits.webkit.org/255300@main
  • Loading branch information
alanbaradlay committed Oct 7, 2022
1 parent 76aa139 commit c3770c7
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 46 deletions.
@@ -0,0 +1,65 @@
<style>
div {
display: inline-block;
width: 50px;
height: 50px;
border: 2px solid green;
margin-right: 6px;
margin-bottom: 6px;
position: relative;
top: -2px;
left: -2px;
}

.container {
display: block;
margin: 0px;
width: auto;
height: auto;
border: none;
}

#outline-width > div {
border-width: 5px;
top: -3px;
}

#outline-offset > div {
top: -8px;
width: 54px;
height: 54px
}

</style>
<div style="border-radius: 12px;"></div>
<div style="border-radius: 17px;"></div>
<div style="border-radius: 22px;"></div>
<div style="border-radius: 27px;"></div>
<div style="border-radius: 32px;"></div>
<br>
<div style="border-radius: 12px 32px;"></div>
<div style="border-radius: 17px 27px;"></div>
<div style="border-radius: 22px 22px;"></div>
<div style="border-radius: 27px 17px;"></div>
<div style="border-radius: 32px 12px;"></div>
<br>
<div style="border-radius: 0px 12px 32px 27px;"></div>
<div style="border-radius: 12px 17px 27px 22px;"></div>
<div style="border-radius: 17px 22px 22px 17px;"></div>
<div style="border-radius: 22px 27px 17px 12px;"></div>
<div style="border-radius: 27px 32px 12px 0px;"></div>
<br>
<div class=container id=outline-width>
<div style="left: -3px; border-radius: 0px 15px 35px 30px;"></div>
<div style="left: -9px; border-radius: 15px 20px 30px 25px;"></div>
<div style="left: -15px; border-radius: 20px 25px 25px 20px;"></div>
<div style="left: -21px; border-radius: 25px 30px 20px 15px;"></div>
<div style="left: -27px; border-radius: 30px 35px 15px 0px;"></div>
</div>
<div class=container id=outline-offset>
<div style="left: -2px; border-radius: 0px 14px 34px 29px;"></div>
<div style="left: -6px; border-radius: 14px 19px 29px 24px;"></div>
<div style="left: -10px; border-radius: 19px 24px 24px 19px;"></div>
<div style="left: -14px; border-radius: 24px 29px 19px 14px;"></div>
<div style="left: -18px; border-radius: 29px 34px 14px 0px;"></div>
</div>
59 changes: 59 additions & 0 deletions LayoutTests/fast/borders/outline-border-radius-simple.html
@@ -0,0 +1,59 @@
<meta name="fuzzy" content="maxDifference=24-68; totalPixels=319-573" />
<style>
div {
display: inline-block;
width: 50px;
height: 50px;
outline: 2px solid green;
margin-right: 10px;
margin-bottom: 10px;
}

.container {
display: block;
margin: 0px;
width: auto;
height: auto;
outline: none;
}

#outline-width > div {
outline-width: 5px;
}

#outline-offset > div {
outline-offset: 2px;
}
</style>
<div style="border-radius: 10px;"></div>
<div style="border-radius: 15px;"></div>
<div style="border-radius: 20px;"></div>
<div style="border-radius: 25px;"></div>
<div style="border-radius: 30px;"></div>
<br>
<div style="border-radius: 10px 30px;"></div>
<div style="border-radius: 15px 25px;"></div>
<div style="border-radius: 20px 20px;"></div>
<div style="border-radius: 25px 15px;"></div>
<div style="border-radius: 30px 10px;"></div>
<br>
<div style="border-radius: 0px 10px 30px 25px;"></div>
<div style="border-radius: 10px 15px 25px 20px;"></div>
<div style="border-radius: 15px 20px 20px 15px;"></div>
<div style="border-radius: 20px 25px 15px 10px;"></div>
<div style="border-radius: 25px 30px 10px 0px;"></div>
<br>
<div class=container id=outline-width>
<div style="border-radius: 0px 10px 30px 25px;"></div>
<div style="border-radius: 10px 15px 25px 20px;"></div>
<div style="border-radius: 15px 20px 20px 15px;"></div>
<div style="border-radius: 20px 25px 15px 10px;"></div>
<div style="border-radius: 25px 30px 10px 0px;"></div>
</div>
<div class=container id=outline-offset>
<div style="border-radius: 0px 10px 30px 25px;"></div>
<div style="border-radius: 10px 15px 25px 20px;"></div>
<div style="border-radius: 15px 20px 20px 15px;"></div>
<div style="border-radius: 20px 25px 15px 10px;"></div>
<div style="border-radius: 25px 30px 10px 0px;"></div>
</div>
97 changes: 51 additions & 46 deletions Source/WebCore/rendering/BorderPainter.cpp
Expand Up @@ -163,7 +163,7 @@ void BorderPainter::paintBorder(const LayoutRect& rect, const RenderStyle& style
if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(outerBorder, m_paintInfo.rect))
outerBorder.setRadii(RoundedRect::Radii());

auto sides = Sides {
paintSides({
outerBorder,
innerBorder,
unadjustedInnerBorder,
Expand All @@ -175,15 +175,14 @@ void BorderPainter::paintBorder(const LayoutRect& rect, const RenderStyle& style
includeLogicalRightEdge,
appliedClipAlready,
style.isHorizontalWritingMode()
};
paintSides(sides);
});
}

void BorderPainter::paintOutline(const LayoutRect& paintRect)
{
auto& styleToUse = m_renderer.style();
float outlineWidth = floorToDevicePixel(styleToUse.outlineWidth(), document().deviceScaleFactor());
float outlineOffset = floorToDevicePixel(styleToUse.outlineOffset(), document().deviceScaleFactor());
auto outlineWidth = floorToDevicePixel(styleToUse.outlineWidth(), document().deviceScaleFactor());
auto outlineOffset = floorToDevicePixel(styleToUse.outlineOffset(), document().deviceScaleFactor());

// Only paint the focus ring by hand if the theme isn't able to draw it.
if (styleToUse.outlineStyleIsAuto() == OutlineIsAuto::On && !m_renderer.theme().supportsFocusRing(styleToUse)) {
Expand All @@ -201,55 +200,61 @@ void BorderPainter::paintOutline(const LayoutRect& paintRect)
if (styleToUse.outlineStyleIsAuto() == OutlineIsAuto::On || styleToUse.outlineStyle() == BorderStyle::None)
return;

auto& graphicsContext = m_paintInfo.context();

FloatRect outer = paintRect;
outer.inflate(outlineOffset + outlineWidth);
FloatRect inner = outer;
inner.inflate(-outlineWidth);

// FIXME: This prevents outlines from painting inside the object. See bug 12042
auto outer = paintRect;
outer.inflate(outlineOffset + outlineWidth);
if (outer.isEmpty())
return;

auto& document = this->document();
BorderStyle outlineStyle = styleToUse.outlineStyle();
Color outlineColor = styleToUse.visitedDependentColorWithColorFilter(CSSPropertyOutlineColor);

bool useTransparencyLayer = !outlineColor.isOpaque();
if (useTransparencyLayer) {
if (outlineStyle == BorderStyle::Solid) {
Path path;
path.addRect(outer);
path.addRect(inner);
graphicsContext.setFillRule(WindRule::EvenOdd);
graphicsContext.setFillColor(outlineColor);
graphicsContext.fillPath(path);
return;
auto isHorizontal = styleToUse.isHorizontalWritingMode();
auto hasBorderRadius = styleToUse.hasBorderRadius();
auto includeLogicalLeftEdge = true;
auto includeLogicalRightEdge = true;
auto roundedBorderRectFor = [&] (auto& borderRect, auto borderOffset) {
auto adjustedRadius = [&] (auto& radius, auto offset) {
auto widthValue = radius.width.isAuto() ? 0 : intValueForLength(radius.width, paintRect.width());
auto heightValue = radius.height.isAuto() ? 0 : intValueForLength(radius.height, paintRect.height());
if (!widthValue && !heightValue)
return LengthSize { { 0, LengthType::Fixed }, { 0, LengthType::Fixed } };
if (!widthValue)
return LengthSize { { 0, LengthType::Fixed }, { heightValue + offset, LengthType::Fixed } };
return LengthSize { { widthValue + offset, LengthType::Fixed }, { heightValue + offset, LengthType::Fixed } };
};

auto borderRadii = std::optional<BorderData::Radii> { };
if (hasBorderRadius) {
borderRadii = BorderData::Radii {
adjustedRadius(styleToUse.borderTopLeftRadius(), borderOffset),
adjustedRadius(styleToUse.borderTopRightRadius(), borderOffset),
adjustedRadius(styleToUse.borderBottomLeftRadius(), borderOffset),
adjustedRadius(styleToUse.borderBottomRightRadius(), borderOffset)
};
}
graphicsContext.beginTransparencyLayer(outlineColor.alphaAsFloat());
outlineColor = outlineColor.opaqueColor();
}

float leftOuter = outer.x();
float leftInner = inner.x();
float rightOuter = outer.maxX();
float rightInner = std::min(inner.maxX(), rightOuter);
float topOuter = outer.y();
float topInner = inner.y();
float bottomOuter = outer.maxY();
float bottomInner = std::min(inner.maxY(), bottomOuter);

drawLineForBoxSide(graphicsContext, document, FloatRect(FloatPoint(leftOuter, topOuter), FloatPoint(leftInner, bottomOuter)), BoxSide::Left, outlineColor, outlineStyle, outlineWidth, outlineWidth);
drawLineForBoxSide(graphicsContext, document, FloatRect(FloatPoint(leftOuter, topOuter), FloatPoint(rightOuter, topInner)), BoxSide::Top, outlineColor, outlineStyle, outlineWidth, outlineWidth);
drawLineForBoxSide(graphicsContext, document, FloatRect(FloatPoint(rightInner, topOuter), FloatPoint(rightOuter, bottomOuter)), BoxSide::Right, outlineColor, outlineStyle, outlineWidth, outlineWidth);
drawLineForBoxSide(graphicsContext, document, FloatRect(FloatPoint(leftOuter, bottomInner), FloatPoint(rightOuter, bottomOuter)), BoxSide::Bottom, outlineColor, outlineStyle, outlineWidth, outlineWidth);
return RenderStyle::getRoundedInnerBorderFor(borderRect, { }, { }, { }, { }, borderRadii, isHorizontal, includeLogicalLeftEdge, includeLogicalRightEdge);
};
auto innerRectForOutline = paintRect;
innerRectForOutline.inflate(outlineOffset);
auto innerBorder = roundedBorderRectFor(innerRectForOutline, LayoutUnit { outlineOffset });
auto outerBorder = roundedBorderRectFor(outer, LayoutUnit { outlineWidth + outlineOffset });
auto bleedAvoidance = BackgroundBleedShrinkBackground;
auto appliedClipAlready = false;
auto haveAllSolidEdges = true;

if (useTransparencyLayer)
graphicsContext.endTransparencyLayer();
paintSides({
outerBorder,
innerBorder,
innerBorder,
hasBorderRadius ? std::make_optional(styleToUse.borderRadii()) : std::nullopt,
borderEdgesForOutline(styleToUse, document().deviceScaleFactor()),
haveAllSolidEdges,
bleedAvoidance,
includeLogicalLeftEdge,
includeLogicalRightEdge,
appliedClipAlready,
isHorizontal
});
}


void BorderPainter::paintOutline(const LayoutPoint& paintOffset, const Vector<LayoutRect>& lineRects)
{
auto& graphicsContext = m_paintInfo.context();
Expand Down

0 comments on commit c3770c7

Please sign in to comment.