Skip to content

Commit cf38e74

Browse files
noamrChromium LUCI CQ
authored andcommitted
Fix corner-shape shadow & outline rendering
As per resolution in w3c/csswg-drafts#13037 (comment) Shadows should be border-aligned, like border inset, with external "miters" that are clipped to the outer edge. The miter is computed by extending the start and end tangents of the curve until they reach the outer edge. The tangent is the line between the start point, and the "control point": a point that would be the quadratic control point if this was a normal quadratic curve with the same half-corner. As part of this work, refactoring the rendering tests to be both more precise and closer to something that can be reasoned about being close to the spec. See spec PR: w3c/csswg-drafts#13173 The PR and the WPT rendering code were developed side by side to ensure that the tests assert the spec. Since the spec defines that curves are approximate, and ref tests can fail on antialiasing and curve inaccuracies, the corners are also stroked with a blue 3px line on both the actual and ref versions, to avoid test failures in these occasions. (This change is guarded by the BorderRadiusCorrectionCoverageFactor, as without it shadow rects wouldn't have an origin rect, so the changed code paths is never reached). Bug: 463996869 Change-Id: I7f586440f47757416b8eb9a2002629cd29e0174a Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7215437 Reviewed-by: Philip Rogers <pdr@chromium.org> Commit-Queue: Noam Rosenthal <nrosenthal@google.com> Cr-Commit-Position: refs/heads/main@{#1554276}
1 parent 9de04ec commit cf38e74

15 files changed

+755
-364
lines changed

third_party/blink/renderer/core/paint/box_painter_base.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ void BoxPainterBase::PaintNormalBoxShadow(
348348
ContouredRect rounded_fill_rect(
349349
FloatRoundedRect(fill_rect, border.GetRadii()),
350350
border.GetCornerCurvature());
351+
if (RuntimeEnabledFeatures::
352+
BorderRadiusCorrectionCoverageFactorEnabled()) {
353+
rounded_fill_rect.SetOriginRect(border.GetOriginRect());
354+
}
351355
ApplySpreadToShadowShape(rounded_fill_rect, shadow.Spread());
352356
context.FillContouredRect(rounded_fill_rect, Color::kBlack,
353357
auto_dark_mode);

third_party/blink/renderer/platform/geometry/contoured_rect.cc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,6 @@ bool ContouredRect::IntersectsQuad(const gfx::QuadF& quad) const {
9393
void ContouredRect::OutsetWithCornerCorrection(const gfx::OutsetsF& outsets) {
9494
if (RuntimeEnabledFeatures::BorderRadiusCorrectionCoverageFactorEnabled()) {
9595
rect_.OutsetWithCornerCorrection(outsets);
96-
if (origin_rect_) {
97-
origin_rect_->OutsetWithCornerCorrection(outsets);
98-
}
9996
return;
10097
}
10198

third_party/blink/renderer/platform/geometry/path_builder.cc

Lines changed: 38 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -273,77 +273,56 @@ PathBuilder& PathBuilder::AddContouredRect(
273273
// This would include the outer border of the rect, as well as shadow and
274274
// margin.
275275
if (target_rect.Rect().Contains(origin_rect.Rect())) {
276-
const gfx::RectF& outer_rect = target_rect.Rect();
276+
auto miter = [&](const Corner& corner, const gfx::LineF& edge,
277+
const gfx::PointF& ref_point) {
278+
return edge
279+
.IntersectionWith(gfx::LineF(corner.IsEmpty()
280+
? (ref_point + edge.Normal())
281+
: corner.QuadraticControlPoint(),
282+
ref_point))
283+
.value_or(ref_point);
284+
};
285+
286+
auto miter_start = [&](const Corner& corner, const gfx::LineF& edge) {
287+
return miter(corner, edge, corner.Start());
288+
};
289+
auto miter_end = [&](const Corner& corner, const gfx::LineF& edge) {
290+
return miter(corner, edge, corner.End());
291+
};
292+
277293
const Corner top_right_corner = contoured_rect.TopRightCorner();
278294
const Corner bottom_right_corner = contoured_rect.BottomRightCorner();
279295
const Corner bottom_left_corner = contoured_rect.BottomLeftCorner();
280296
const Corner top_left_corner = contoured_rect.TopLeftCorner();
281-
MoveTo(top_right_corner.Start());
297+
298+
const gfx::LineF top_line(target_rect.Rect().origin(),
299+
target_rect.Rect().top_right());
300+
const gfx::LineF bottom_line(target_rect.Rect().bottom_left(),
301+
target_rect.Rect().bottom_right());
302+
const gfx::LineF left_line(target_rect.Rect().origin(),
303+
target_rect.Rect().bottom_left());
304+
const gfx::LineF right_line(target_rect.Rect().top_right(),
305+
target_rect.Rect().bottom_right());
306+
307+
MoveTo(miter_start(top_right_corner, top_line));
282308
AddCorner(top_right_corner);
283-
if (!top_right_corner.IsHyperellipse()) {
284-
LineTo(gfx::LineF(outer_rect.top_right(), outer_rect.bottom_right())
285-
.IntersectionWith({top_right_corner.QuadraticControlPoint(),
286-
top_right_corner.End()})
287-
.value_or(
288-
gfx::PointF(outer_rect.right(), origin_rect.Rect().y())));
289-
}
290-
291-
if (!bottom_right_corner.IsHyperellipse()) {
292-
LineTo(gfx::LineF(outer_rect.top_right(), outer_rect.bottom_right())
293-
.IntersectionWith({bottom_right_corner.QuadraticControlPoint(),
294-
bottom_right_corner.Start()})
295-
.value_or(gfx::PointF(outer_rect.right(),
296-
origin_rect.Rect().bottom())));
297-
}
309+
LineTo(miter_end(top_right_corner, right_line));
298310

311+
LineTo(miter_start(bottom_right_corner, right_line));
299312
AddCorner(bottom_right_corner);
313+
LineTo(miter_end(bottom_right_corner, bottom_line));
300314

301-
if (!bottom_right_corner.IsHyperellipse()) {
302-
LineTo(gfx::LineF(outer_rect.bottom_left(), outer_rect.bottom_right())
303-
.IntersectionWith({bottom_right_corner.QuadraticControlPoint(),
304-
bottom_right_corner.End()})
305-
.value_or(gfx::PointF(origin_rect.Rect().right(),
306-
outer_rect.bottom())));
307-
}
308-
if (!bottom_left_corner.IsHyperellipse()) {
309-
LineTo(gfx::LineF(outer_rect.bottom_left(), outer_rect.bottom_right())
310-
.IntersectionWith({bottom_left_corner.QuadraticControlPoint(),
311-
bottom_left_corner.Start()})
312-
.value_or(
313-
gfx::PointF(origin_rect.Rect().x(), outer_rect.bottom())));
314-
}
315+
LineTo(miter_start(bottom_left_corner, bottom_line));
315316
AddCorner(bottom_left_corner);
316-
if (!bottom_left_corner.IsHyperellipse()) {
317-
LineTo(gfx::LineF(outer_rect.bottom_left(), outer_rect.origin())
318-
.IntersectionWith({bottom_left_corner.QuadraticControlPoint(),
319-
bottom_left_corner.End()})
320-
.value_or(
321-
gfx::PointF(outer_rect.x(), origin_rect.Rect().bottom())));
322-
}
323-
if (!top_left_corner.IsHyperellipse()) {
324-
LineTo(
325-
gfx::LineF(outer_rect.bottom_left(), outer_rect.origin())
326-
.IntersectionWith({top_left_corner.QuadraticControlPoint(),
327-
top_left_corner.Start()})
328-
.value_or(gfx::PointF(outer_rect.x(), origin_rect.Rect().y())));
329-
}
317+
LineTo(miter_end(bottom_left_corner, left_line));
318+
319+
LineTo(miter_start(top_left_corner, left_line));
330320
AddCorner(top_left_corner);
331-
if (!top_left_corner.IsHyperellipse()) {
332-
LineTo(
333-
gfx::LineF(outer_rect.top_right(), outer_rect.origin())
334-
.IntersectionWith({top_left_corner.QuadraticControlPoint(),
335-
top_left_corner.End()})
336-
.value_or(gfx::PointF(origin_rect.Rect().x(), outer_rect.y())));
337-
}
338-
if (!top_right_corner.IsHyperellipse()) {
339-
LineTo(gfx::LineF(outer_rect.top_right(), outer_rect.origin())
340-
.IntersectionWith({top_right_corner.QuadraticControlPoint(),
341-
top_right_corner.Start()})
342-
.value_or(
343-
gfx::PointF(origin_rect.Rect().right(), outer_rect.y())));
344-
}
321+
LineTo(miter_end(top_left_corner, top_line));
322+
345323
Close();
346324
ClearCachedData();
325+
347326
return *this;
348327
}
349328

third_party/blink/web_tests/external/wpt/css/css-borders/corner-shape/corner-shape-any-ref.html

Lines changed: 0 additions & 28 deletions
This file was deleted.

third_party/blink/web_tests/external/wpt/css/css-borders/corner-shape/corner-shape-gallery.manual.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
iframe {
2222
overflow: clip;
23-
width: 250px;
24-
height: 150px;
23+
width: 500px;
24+
height: 300px;
2525
border: none;
2626
}
2727

@@ -41,7 +41,7 @@
4141
</template>
4242
<script>
4343
addEventListener("DOMContentLoaded", async () => {
44-
const test_files = ["corner-shape-render-precise.html", "corner-shape-render-fuzzy.html"]
44+
const test_files = ["render-corner-shape.html"]
4545
const dom_parser = new DOMParser();
4646
for (const test_file of test_files) {
4747
const test_html = await (await fetch(test_file)).text();
@@ -51,7 +51,7 @@
5151
const scenario = document.getElementById("scenario").content.cloneNode(true);
5252
scenario.querySelector(".variant").innerText = variant;
5353
scenario.querySelector(".test").src = `${test_file}${variant}`;
54-
scenario.querySelector(".ref").src = `corner-shape-any-ref.html${variant}`;
54+
scenario.querySelector(".ref").src = `render-corner-shape-ref.html${variant}`;
5555
document.querySelector("main").append(scenario);
5656
}
5757
}

third_party/blink/web_tests/external/wpt/css/css-borders/corner-shape/corner-shape-render-fuzzy.html

Lines changed: 0 additions & 48 deletions
This file was deleted.

third_party/blink/web_tests/external/wpt/css/css-borders/corner-shape/corner-shape-render-precise.html

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Render corner shape</title>
5+
<script src="./resources/render-corner-shape.js"></script>
6+
</head>
7+
<body>
8+
<script>
9+
const element = create_element_with_corner_shape(
10+
new URLSearchParams(location.search),
11+
"ref"
12+
);
13+
document.body.appendChild(element);
14+
</script>
15+
</body>
16+
</html>

0 commit comments

Comments
 (0)