Skip to content
Permalink
Browse files
[LFC][Integration] Support background-clip:text on inline boxes
https://bugs.webkit.org/show_bug.cgi?id=231877

Reviewed by Alan Bujtas.

Source/WebCore:

Implement text mask painting.

Test: fast/inline/inline-background-clip-text.html

* layout/integration/InlineIteratorBoxLegacyPath.h:
(WebCore::InlineIterator::BoxLegacyPath::firstLeafBoxForInlineBox const):
(WebCore::InlineIterator::BoxLegacyPath::lastLeafBoxForInlineBox const):
* layout/integration/InlineIteratorBoxModernPath.h:
(WebCore::InlineIterator::BoxModernPath::firstLeafBoxForInlineBox const):
(WebCore::InlineIterator::BoxModernPath::lastLeafBoxForInlineBox const):
(WebCore::InlineIterator::BoxModernPath::isWithinInlineBox):
* layout/integration/InlineIteratorInlineBox.cpp:
(WebCore::InlineIterator::InlineBox::firstLeafBox const):
(WebCore::InlineIterator::InlineBox::lastLeafBox const):
(WebCore::InlineIterator::InlineBox::endLeafBox const):

Add iterator support for getting leaf boxes of an inline box.

* layout/integration/InlineIteratorInlineBox.h:
* layout/integration/LayoutIntegrationCoverage.cpp:
(WebCore::LayoutIntegration::printReason):
(WebCore::LayoutIntegration::canUseForRenderInlineChild):
* layout/integration/LayoutIntegrationCoverage.h:
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelObject::paintMaskForTextFillBox):

Paint the mask directly using TextBoxPainter instead of recursing to the general painting code.

* rendering/TextBoxPainter.cpp:
(WebCore::TextBoxPainter::TextBoxPainter):
* rendering/TextBoxPainter.h:

LayoutTests:

* fast/inline/inline-background-clip-text-expected.html: Added.
* fast/inline/inline-background-clip-text.html: Added.


Canonical link: https://commits.webkit.org/243129@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@284334 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
anttijk committed Oct 17, 2021
1 parent 304f4e7 commit b411e0ce2be6314b837c92c4b779cfd9cd0173e7
Showing 15 changed files with 171 additions and 20 deletions.
@@ -1,3 +1,13 @@
2021-10-17 Antti Koivisto <antti@apple.com>

[LFC][Integration] Support background-clip:text on inline boxes
https://bugs.webkit.org/show_bug.cgi?id=231877

Reviewed by Alan Bujtas.

* fast/inline/inline-background-clip-text-expected.html: Added.
* fast/inline/inline-background-clip-text.html: Added.

2021-10-16 Robin Morisset <rmorisset@apple.com>

Allow WASM to use up to 4GB
@@ -0,0 +1,10 @@
<style>
div {
font-family: 'Ahem';
}
.clip {
color: green;
}
</style>
<div>a <span class="clip">b <span>c </span></span>d</div>

@@ -0,0 +1,11 @@
<style>
div {
font-family: 'Ahem';
}
.clip {
color: transparent;
background-clip: text;
background-color: green;
}
</style>
<div>a <span class="clip">b <span>c </span></span>d</div>
@@ -2911,6 +2911,7 @@ http/tests/misc/large-js-program.py [ Crash Timeout ]
fast/repaint/block-inputrange-repaint.html [ Pass Failure ]

fast/inline/padding-ellipsis-right.html [ ImageOnlyFailure ]
fast/inline/inline-background-clip-text.html [ ImageOnlyFailure ]

# This test hardcodes the result of a platform-dependent font lookup algorithm.
fast/text/fallback-language-han.html [ Skip ]
@@ -1,3 +1,42 @@
2021-10-17 Antti Koivisto <antti@apple.com>

[LFC][Integration] Support background-clip:text on inline boxes
https://bugs.webkit.org/show_bug.cgi?id=231877

Reviewed by Alan Bujtas.

Implement text mask painting.

Test: fast/inline/inline-background-clip-text.html

* layout/integration/InlineIteratorBoxLegacyPath.h:
(WebCore::InlineIterator::BoxLegacyPath::firstLeafBoxForInlineBox const):
(WebCore::InlineIterator::BoxLegacyPath::lastLeafBoxForInlineBox const):
* layout/integration/InlineIteratorBoxModernPath.h:
(WebCore::InlineIterator::BoxModernPath::firstLeafBoxForInlineBox const):
(WebCore::InlineIterator::BoxModernPath::lastLeafBoxForInlineBox const):
(WebCore::InlineIterator::BoxModernPath::isWithinInlineBox):
* layout/integration/InlineIteratorInlineBox.cpp:
(WebCore::InlineIterator::InlineBox::firstLeafBox const):
(WebCore::InlineIterator::InlineBox::lastLeafBox const):
(WebCore::InlineIterator::InlineBox::endLeafBox const):

Add iterator support for getting leaf boxes of an inline box.

* layout/integration/InlineIteratorInlineBox.h:
* layout/integration/LayoutIntegrationCoverage.cpp:
(WebCore::LayoutIntegration::printReason):
(WebCore::LayoutIntegration::canUseForRenderInlineChild):
* layout/integration/LayoutIntegrationCoverage.h:
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelObject::paintMaskForTextFillBox):

Paint the mask directly using TextBoxPainter instead of recursing to the general painting code.

* rendering/TextBoxPainter.cpp:
(WebCore::TextBoxPainter::TextBoxPainter):
* rendering/TextBoxPainter.h:

2021-10-16 Alan Bujtas <zalan@apple.com>

[LFC][IFC] Adjust the logical right side of the line with line spanning inline boxes
@@ -811,6 +811,8 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS
layout/integration/InlineIteratorTextBox.h
layout/integration/LayoutIntegrationInlineContent.h
layout/integration/LayoutIntegrationLine.h

layout/layouttree/LayoutContainerBox.h
layout/layouttree/LayoutBox.h

loader/CanvasActivityRecord.h
@@ -96,6 +96,16 @@ class BoxLegacyPath {
m_inlineBox = inlineFlowBox()->prevLineBox();
}

BoxLegacyPath firstLeafBoxForInlineBox() const
{
return { inlineFlowBox()->firstLeafDescendant() };
}

BoxLegacyPath lastLeafBoxForInlineBox() const
{
return { inlineFlowBox()->lastLeafDescendant() };
}

bool operator==(const BoxLegacyPath& other) const { return m_inlineBox == other.m_inlineBox; }

bool atEnd() const { return !m_inlineBox; }
@@ -28,6 +28,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)

#include "FontCascade.h"
#include "LayoutContainerBox.h"
#include "LayoutIntegrationInlineContent.h"
#include "TextBoxSelectableRange.h"

@@ -194,13 +195,53 @@ class BoxModernPath {
ASSERT(atEnd() || box().isInlineBox());
}

BoxModernPath firstLeafBoxForInlineBox() const
{
ASSERT(box().isInlineBox());

auto& inlineBox = box().layoutBox();

// The next box is the first descendant of this box;
auto first = *this;
first.traverseNextOnLine();

if (!first.atEnd() && !first.isWithinInlineBox(inlineBox))
first.setAtEnd();

return first;
}

BoxModernPath lastLeafBoxForInlineBox() const
{
ASSERT(box().isInlineBox());

auto& inlineBox = box().layoutBox();

// FIXME: Get the last box index directly from the display box.
auto last = firstLeafBoxForInlineBox();
for (auto box = last; !box.atEnd() && box.isWithinInlineBox(inlineBox); box.traverseNextOnLine())
last = box;

return last;
}

bool operator==(const BoxModernPath& other) const { return m_inlineContent == other.m_inlineContent && m_boxIndex == other.m_boxIndex; }

bool atEnd() const { return m_boxIndex == boxes().size(); }
const InlineDisplay::Box& box() const { return boxes()[m_boxIndex]; }
auto& inlineContent() const { return *m_inlineContent; }

private:
bool isWithinInlineBox(const Layout::Box& inlineBox)
{
auto* layoutBox = &box().layoutBox().parent();
for (; layoutBox->isInlineBox(); layoutBox = &layoutBox->parent()) {
if (layoutBox == &inlineBox)
return true;
}
return false;
}

void traverseNextBox()
{
ASSERT(!atEnd());
@@ -59,6 +59,27 @@ InlineBoxIterator InlineBox::previousInlineBox() const
return InlineBoxIterator(*this).traversePreviousInlineBox();
}

LeafBoxIterator InlineBox::firstLeafBox() const
{
return WTF::switchOn(m_pathVariant, [](auto& path) -> LeafBoxIterator {
return { path.firstLeafBoxForInlineBox() };
});
}

LeafBoxIterator InlineBox::lastLeafBox() const
{
return WTF::switchOn(m_pathVariant, [](auto& path) -> LeafBoxIterator {
return { path.lastLeafBoxForInlineBox() };
});
}

LeafBoxIterator InlineBox::endLeafBox() const
{
if (auto last = lastLeafBox())
return last->nextOnLine();
return { };
}

InlineBoxIterator::InlineBoxIterator(Box::PathVariant&& pathVariant)
: BoxIterator(WTFMove(pathVariant))
{
@@ -47,6 +47,10 @@ class InlineBox : public Box {
InlineBoxIterator nextInlineBox() const;
InlineBoxIterator previousInlineBox() const;
InlineBoxIterator iterator() const;

LeafBoxIterator firstLeafBox() const;
LeafBoxIterator lastLeafBox() const;
LeafBoxIterator endLeafBox() const;
};

class InlineBoxIterator : public BoxIterator {
@@ -209,9 +209,6 @@ static void printReason(AvoidanceReason reason, TextStream& stream)
case AvoidanceReason::BoxDecorationBreakClone:
stream << "webkit-box-decoration-break: clone";
break;
case AvoidanceReason::InlineBoxHasBackgroundClipText:
stream << "inline box has background-clip: text";
break;
default:
break;
}
@@ -481,8 +478,6 @@ static OptionSet<AvoidanceReason> canUseForRenderInlineChild(const RenderInline&
if (style.boxDecorationBreak() == BoxDecorationBreak::Clone)
SET_REASON_AND_RETURN_IF_NEEDED(BoxDecorationBreakClone, reasons, includeReasons);
#endif
if (style.backgroundClip() == FillBox::Text)
SET_REASON_AND_RETURN_IF_NEEDED(InlineBoxHasBackgroundClipText, reasons, includeReasons);
if (style.hasOutline())
SET_REASON_AND_RETURN_IF_NEEDED(ContentHasOutline, reasons, includeReasons);
if (renderInline.isInFlowPositioned())
@@ -91,7 +91,7 @@ enum class AvoidanceReason : uint64_t {
MultiColumnFlowIsFloating = 1LLU << 50,
// Unused = 1LLU << 51,
// Unused = 1LLU << 52,
InlineBoxHasBackgroundClipText = 1LLU << 53,
// Unused = 1LLU << 53,
UnsupportedFieldset = 1LLU << 54,
ChildBoxIsFloatingOrPositioned = 1LLU << 55,
ContentIsSVG = 1LLU << 56,
@@ -60,6 +60,7 @@
#include "RenderView.h"
#include "ScrollingConstraints.h"
#include "Settings.h"
#include "TextBoxPainter.h"
#include "TransformState.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/NeverDestroyed.h>
@@ -711,22 +712,28 @@ InterpolationQuality RenderBoxModelObject::chooseInterpolationQuality(GraphicsCo
return view().imageQualityController().chooseInterpolationQuality(context, this, image, layer, size);
}

void RenderBoxModelObject::paintMaskForTextFillBox(ImageBuffer* maskImage, const FloatRect& maskRect, const InlineIterator::InlineBoxIterator& box, const LayoutRect& scrolledPaintRect)
void RenderBoxModelObject::paintMaskForTextFillBox(ImageBuffer* maskImage, const FloatRect& maskRect, const InlineIterator::InlineBoxIterator& inlineBox, const LayoutRect& scrolledPaintRect)
{
GraphicsContext& maskImageContext = maskImage->context();
maskImageContext.translate(-maskRect.location());

// Now add the text to the clip. We do this by painting using a special paint phase that signals to
// LegacyInlineTextBoxes that they should just add their contents to the clip.
PaintInfo info(maskImageContext, LayoutRect { maskRect }, PaintPhase::TextClip, PaintBehavior::ForceBlackText);
if (box) {
auto* legacyInlineBox = const_cast<LegacyInlineFlowBox*>(box->legacyInlineBox());
const auto& rootBox = legacyInlineBox->root();
legacyInlineBox->paint(info, LayoutPoint(scrolledPaintRect.x() - legacyInlineBox->x(), scrolledPaintRect.y() - legacyInlineBox->y()), rootBox.lineTop(), rootBox.lineBottom());
} else {
LayoutSize localOffset = is<RenderBox>(*this) ? downcast<RenderBox>(*this).locationOffset() : LayoutSize();
paint(info, scrolledPaintRect.location() - localOffset);
// the painter it should just modify the clip.
PaintInfo maskInfo(maskImageContext, LayoutRect { maskRect }, PaintPhase::TextClip, PaintBehavior::ForceBlackText);
if (inlineBox) {
auto paintOffset = scrolledPaintRect.location() - toLayoutSize(LayoutPoint(inlineBox->rect().location()));

for (auto box = inlineBox->firstLeafBox(), end = inlineBox->endLeafBox(); box != end; box.traverseNextOnLine()) {
if (!box->isText())
continue;
TextBoxPainter textBoxPainter(downcast<InlineIterator::TextBoxIterator>(box), maskInfo, paintOffset);
textBoxPainter.paint();
}
return;
}

LayoutSize localOffset = is<RenderBox>(*this) ? downcast<RenderBox>(*this).locationOffset() : LayoutSize();
paint(maskInfo, scrolledPaintRect.location() - localOffset);
}

void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer& bgLayer, const LayoutRect& rect,
@@ -56,8 +56,8 @@ TextBoxPainter::TextBoxPainter(const LayoutIntegration::InlineContent& inlineCon
}
#endif

TextBoxPainter::TextBoxPainter(InlineIterator::TextBoxIterator&& textBox, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
: m_textBox(WTFMove(textBox))
TextBoxPainter::TextBoxPainter(const InlineIterator::TextBoxIterator& textBox, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
: m_textBox(textBox)
, m_renderer(m_textBox->renderer())
, m_document(m_renderer.document())
, m_style(m_textBox->style())
@@ -50,15 +50,15 @@ class TextBoxPainter {
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
TextBoxPainter(const LayoutIntegration::InlineContent&, const InlineDisplay::Box&, PaintInfo&, const LayoutPoint& paintOffset);
#endif
TextBoxPainter(const InlineIterator::TextBoxIterator&, PaintInfo&, const LayoutPoint& paintOffset);

~TextBoxPainter();

void paint();

static FloatRect calculateUnionOfAllDocumentMarkerBounds(const LegacyInlineTextBox&);

private:
TextBoxPainter(InlineIterator::TextBoxIterator&&, PaintInfo&, const LayoutPoint& paintOffset);

auto& textBox() const { return *m_textBox; }

void paintBackground();

0 comments on commit b411e0c

Please sign in to comment.