Skip to content
Permalink
Browse files
Repaint rect does not take into account text-underline-offset
https://bugs.webkit.org/show_bug.cgi?id=241442

Reviewed by Antti Koivisto.

This patch add support for text decoration ink overflow (IFC).

1. Collect decoration ink overflow (logical)
2. Update display box ink overflow (visual)

* Source/WebCore/layout/formattingContexts/inline/display/InlineDisplayContentBuilder.cpp:
(WebCore::Layout::InlineDisplayContentBuilder::build):
(WebCore::Layout::InlineDisplayContentBuilder::collectInkOverflowForInlineBoxes):
(WebCore::Layout::visualBottomForTextDecorationContent):
(WebCore::Layout::InlineDisplayContentBuilder::collectInkOverflowForTextDecorations):
* Source/WebCore/layout/formattingContexts/inline/display/InlineDisplayContentBuilder.h:
* Source/WebCore/style/InlineTextBoxStyle.cpp:
(WebCore::visualOverflowForDecorations):
(WebCore::defaultGap):
* Source/WebCore/style/InlineTextBoxStyle.h:

Canonical link: https://commits.webkit.org/251477@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295472 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
alanbujtas committed Jun 11, 2022
1 parent 6e0d565 commit 964536040a3ef2c7f6b53c0a48bcea0190e41319
Showing 9 changed files with 135 additions and 11 deletions.
@@ -1,5 +1,5 @@
PASS window.internals.repaintRectsAsText().indexOf('8 8 784 12') is not -1
PASS window.internals.repaintRectsAsText().indexOf('8 20 784 12') is not -1
PASS window.internals.repaintRectsAsText().indexOf('8 8 784 13') is not -1
PASS window.internals.repaintRectsAsText().indexOf('8 20 784 13') is not -1
PASS successfullyParsed is true

TEST COMPLETE
@@ -31,8 +31,8 @@
elements[i].style.textDecoration = "underline";
document.body.offsetHeight;
if (window.internals) {
shouldNotBe("window.internals.repaintRectsAsText().indexOf('8 8 784 12')", "-1");
shouldNotBe("window.internals.repaintRectsAsText().indexOf('8 20 784 12')", "-1");
shouldNotBe("window.internals.repaintRectsAsText().indexOf('8 8 784 13')", "-1");
shouldNotBe("window.internals.repaintRectsAsText().indexOf('8 20 784 13')", "-1");
internals.stopTrackingRepaints();
}
finishJSTest();
@@ -0,0 +1,10 @@
PASS if the underlines are green.
content
content
content
(repaint rects
(rect 48 46 112 25)
(rect 39 82 25 112)
(rect 39 214 25 112)
)

@@ -0,0 +1,36 @@
<style>
div {
margin: 20px;
font-family: Ahem;
}
span {
text-decoration: underline;
text-underline-offset: 10px;
color: blue;
}
</style>
PASS if the underlines are green.
<div id=container>
<div><span>content</span></div>
<div style="writing-mode: vertical-lr"><span>content</span></div>
<div style="writing-mode: vertical-rl"><span>content</span></div>
</div>
<pre id=result></pre>
<script>
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
setTimeout(function() {
if (window.internals)
window.internals.startTrackingRepaints();
for (let changeThis of container.getElementsByTagName("span"))
changeThis.style.textDecorationColor = "green";
if (window.internals) {
result.innerText = window.internals.repaintRectsAsText();
window.internals.stopTrackingRepaints();
}
if (window.testRunner)
testRunner.notifyDone();
}, 10);
</script>
@@ -137,7 +137,7 @@ struct Box {
{
auto offset = physicalTop - top();
m_unflippedVisualRect.setY(physicalTop);
m_inkOverflow.shiftMaxYEdgeTo(m_inkOverflow.y() + offset);
m_inkOverflow.setY(m_inkOverflow.y() + offset);
}
void setBottom(float physicalBottom)
{
@@ -29,6 +29,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)

#include "FontCascade.h"
#include "InlineTextBoxStyle.h"
#include "LayoutBoxGeometry.h"
#include "LayoutInitialContainingBlock.h"
#include "TextUtil.h"
@@ -102,6 +103,7 @@ DisplayBoxes InlineDisplayContentBuilder::build(const LineBuilder::LineContent&
else
processNonBidiContent(lineContent, lineBox, displayLine, boxes);
processOverflownRunsForEllipsis(boxes, displayLine.right());
collectInkOverflowForTextDecorations(boxes, displayLine);
collectInkOverflowForInlineBoxes(boxes);
return boxes;
}
@@ -788,7 +790,7 @@ void InlineDisplayContentBuilder::collectInkOverflowForInlineBoxes(DisplayBoxes&
for (size_t index = boxes.size(); index--;) {
auto& displayBox = boxes[index];

auto mayHaveInkOverflow = displayBox.isAtomicInlineLevelBox() || displayBox.isGenericInlineLevelBox() || displayBox.isNonRootInlineBox();
auto mayHaveInkOverflow = displayBox.isText() || displayBox.isAtomicInlineLevelBox() || displayBox.isGenericInlineLevelBox() || displayBox.isNonRootInlineBox();
if (!mayHaveInkOverflow)
continue;
if (displayBox.isNonRootInlineBox() && !accumulatedInkOverflowRect.isEmpty())
@@ -805,6 +807,75 @@ void InlineDisplayContentBuilder::collectInkOverflowForInlineBoxes(DisplayBoxes&
}
}

static float logicalBottomForTextDecorationContent(const DisplayBoxes& boxes, bool isHorizontalWritingMode)
{
auto logicalBottom = std::optional<float> { };
for (auto& displayBox : boxes) {
if (displayBox.isRootInlineBox())
continue;
if (!displayBox.style().textDecorationsInEffect().contains(TextDecorationLine::Underline))
continue;
if (displayBox.isText() || displayBox.style().textDecorationSkipInk() == TextDecorationSkipInk::None) {
auto contentLogicalBottom = isHorizontalWritingMode ? displayBox.bottom() : displayBox.right();
logicalBottom = logicalBottom ? std::max(*logicalBottom, contentLogicalBottom) : contentLogicalBottom;
}
}
// This function is not called unless there's at least one run on the line with TextDecorationLine::Underline.
ASSERT(logicalBottom);
return logicalBottom.value_or(0.f);
}

void InlineDisplayContentBuilder::collectInkOverflowForTextDecorations(DisplayBoxes& boxes, const InlineDisplay::Line& displayLine)
{
auto logicalBottomForTextDecoration = std::optional<float> { };
auto writingMode = root().style().writingMode();
auto isHorizontalWritingMode = WebCore::isHorizontalWritingMode(writingMode);

for (auto& displayBox : boxes) {
if (!displayBox.isText())
continue;

auto& style = displayBox.style();
auto textDecorations = style.textDecorationsInEffect();
if (!textDecorations)
continue;

auto underlineOffset = [&]() -> std::optional<float> {
if (!textDecorations.contains(TextDecorationLine::Underline))
return { };
if (!logicalBottomForTextDecoration)
logicalBottomForTextDecoration = logicalBottomForTextDecorationContent(boxes, isHorizontalWritingMode);
auto textRunLogicalOffsetFromLineBottom = *logicalBottomForTextDecoration - (isHorizontalWritingMode ? displayBox.bottom() : displayBox.right());
// Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText()
return computeUnderlineOffset({ style, defaultGap(style), UnderlineOffsetArguments::TextUnderlinePositionUnder { displayLine.baselineType(), displayBox.height(), textRunLogicalOffsetFromLineBottom } }) + 1.0f;
};

auto decorationOverflow = visualOverflowForDecorations(style, underlineOffset());
if (!decorationOverflow.isEmpty()) {
m_contentHasInkOverflow = true;
auto inflatedVisualOverflowRect = [&] {
auto inkOverflowRect = displayBox.inkOverflow();
switch (writingMode) {
case WritingMode::TopToBottom:
inkOverflowRect.inflate(decorationOverflow.left, decorationOverflow.top, decorationOverflow.right, decorationOverflow.bottom);
break;
case WritingMode::LeftToRight:
inkOverflowRect.inflate(decorationOverflow.bottom, decorationOverflow.right, decorationOverflow.top, decorationOverflow.left);
break;
case WritingMode::RightToLeft:
inkOverflowRect.inflate(decorationOverflow.top, decorationOverflow.right, decorationOverflow.bottom, decorationOverflow.left);
break;
default:
ASSERT_NOT_REACHED();
break;
}
return inkOverflowRect;
};
displayBox.adjustInkOverflow(inflatedVisualOverflowRect());
}
}
}

void InlineDisplayContentBuilder::computeIsFirstIsLastBoxForInlineContent(DisplayBoxes& boxes)
{
HashMap<const Box*, size_t> lastDisplayBoxForLayoutBoxIndexes;
@@ -53,6 +53,7 @@ class InlineDisplayContentBuilder {
void processBidiContent(const LineBuilder::LineContent&, const LineBox&, const InlineDisplay::Line&, DisplayBoxes&);
void processOverflownRunsForEllipsis(DisplayBoxes&, InlineLayoutUnit lineBoxRight);
void collectInkOverflowForInlineBoxes(DisplayBoxes&);
void collectInkOverflowForTextDecorations(DisplayBoxes&, const InlineDisplay::Line&);

void appendTextDisplayBox(const Line::Run&, const InlineRect&, DisplayBoxes&);
void appendSoftLineBreakDisplayBox(const Line::Run&, const InlineRect&, DisplayBoxes&);
@@ -244,15 +244,13 @@ GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const I

// Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText()
int underlineOffset = 1;
float textDecorationBaseFontSize = 16;
auto defaultGap = lineStyle.computedFontSize() / textDecorationBaseFontSize;
// FIXME: RenderStyle calls us with empty textRun but only when TextUnderlinePosition is not Under.
ASSERT(textRun || lineStyle.textUnderlinePosition() != TextUnderlinePosition::Under);
if (!textRun)
underlineOffset += computeUnderlineOffset({ lineStyle, defaultGap });
underlineOffset += computeUnderlineOffset({ lineStyle, defaultGap(lineStyle) });
else {
underlineOffset += computeUnderlineOffset({ lineStyle
, defaultGap
, defaultGap(lineStyle)
, UnderlineOffsetArguments::TextUnderlinePositionUnder { textRun->lineBox()->baselineType(), textRun->logicalBottom() - textRun->logicalTop(), textRunLogicalOffsetFromLineBottom(textRun) }
});
}
@@ -263,5 +261,11 @@ GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle)
{
return visualOverflowForDecorations(lineStyle, InlineIterator::TextBoxIterator { });
}


float defaultGap(const RenderStyle& lineStyle)
{
const float textDecorationBaseFontSize = 16.f;
return lineStyle.computedFontSize() / textDecorationBaseFontSize;
}

}
@@ -53,6 +53,7 @@ struct WavyStrokeParameters {
float step { 0 };
};
WavyStrokeParameters getWavyStrokeParameters(float fontSize);

GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle);
GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineIterator::TextBoxIterator&);
GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, std::optional<float> underlineOffset);
@@ -69,5 +70,6 @@ struct UnderlineOffsetArguments {
};
float computeUnderlineOffset(const UnderlineOffsetArguments&);
float textRunLogicalOffsetFromLineBottom(const InlineIterator::TextBoxIterator&);
float defaultGap(const RenderStyle& lineStyle);

} // namespace WebCore

0 comments on commit 9645360

Please sign in to comment.