Title: [286955] trunk
Revision
286955
Author
n...@apple.com
Date
2021-12-13 10:23:00 -0800 (Mon, 13 Dec 2021)

Log Message

Fix paint order of CSS text decorations
https://bugs.webkit.org/show_bug.cgi?id=227445

Reviewed by Simon Fraser.

Source/WebCore:

* rendering/TextBoxPainter.cpp:
(WebCore::TextBoxPainter::paintForegroundAndDecorations):
(WebCore::TextBoxPainter::createDecorationPainter):
(WebCore::TextBoxPainter::paintBackgroundDecorations):
(WebCore::TextBoxPainter::paintForegroundDecorations):
(WebCore::TextBoxPainter::paintDecoration): Deleted.
* rendering/TextBoxPainter.h:
* rendering/TextDecorationPainter.cpp:
(WebCore::TextDecorationPainter::paintBackgroundDecorations):
(WebCore::TextDecorationPainter::paintForegroundDecorations):
(WebCore::TextDecorationPainter::paintLineThrough):
(WebCore::TextDecorationPainter::paintTextDecoration): Deleted.
* rendering/TextDecorationPainter.h:

LayoutTests:

* TestExpectations:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (286954 => 286955)


--- trunk/LayoutTests/ChangeLog	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/LayoutTests/ChangeLog	2021-12-13 18:23:00 UTC (rev 286955)
@@ -1,3 +1,12 @@
+2021-12-13  Tim Nguyen  <n...@apple.com>
+
+        Fix paint order of CSS text decorations
+        https://bugs.webkit.org/show_bug.cgi?id=227445
+
+        Reviewed by Simon Fraser.
+
+        * TestExpectations:
+
 2021-12-13  Sergio Villar Senin  <svil...@igalia.com>
 
         [css-writing-modes] Use the correct margins in computeInlinePreferredLogicalWidths in orthogonal flows

Modified: trunk/LayoutTests/TestExpectations (286954 => 286955)


--- trunk/LayoutTests/TestExpectations	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/LayoutTests/TestExpectations	2021-12-13 18:23:00 UTC (rev 286955)
@@ -3431,7 +3431,6 @@
 webkit.org/b/230041 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-color-selection-001.html [ ImageOnlyFailure ]
 webkit.org/b/230041 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-color-selection-pseudo-01.html [ ImageOnlyFailure ]
 webkit.org/b/203530 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-line-recalc.html [ ImageOnlyFailure ]
-webkit.org/b/203531 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-propagation-shadow.html [ ImageOnlyFailure ]
 webkit.org/b/203528 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-thickness-linethrough-001.html [ ImageOnlyFailure ]
 webkit.org/b/203528 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-thickness-overline-001.html [ ImageOnlyFailure ]
 webkit.org/b/203528 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-thickness-scroll-001.html [ ImageOnlyFailure ]
@@ -3471,7 +3470,6 @@
 imported/w3c/web-platform-tests/css/css-text-decor/text-emphasis-style-shape-001.xht [ ImageOnlyFailure ]
 imported/w3c/web-platform-tests/css/css-text-decor/text-emphasis-style-string-001.xht [ ImageOnlyFailure ]
 webkit.org/b/230083 imported/w3c/web-platform-tests/css/css-text-decor/text-underline-offset-001.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/css/css-text-decor/text-underline-offset-zero-position.html [ ImageOnlyFailure ]
 
 # wpt css-position failures
 webkit.org/b/203445 [ Debug ] imported/w3c/web-platform-tests/css/css-position/position-absolute-container-dynamic-002.html [ Skip ]

Modified: trunk/Source/WebCore/ChangeLog (286954 => 286955)


--- trunk/Source/WebCore/ChangeLog	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/Source/WebCore/ChangeLog	2021-12-13 18:23:00 UTC (rev 286955)
@@ -1,3 +1,24 @@
+2021-12-13  Tim Nguyen  <n...@apple.com>
+
+        Fix paint order of CSS text decorations
+        https://bugs.webkit.org/show_bug.cgi?id=227445
+
+        Reviewed by Simon Fraser.
+
+        * rendering/TextBoxPainter.cpp:
+        (WebCore::TextBoxPainter::paintForegroundAndDecorations):
+        (WebCore::TextBoxPainter::createDecorationPainter):
+        (WebCore::TextBoxPainter::paintBackgroundDecorations):
+        (WebCore::TextBoxPainter::paintForegroundDecorations):
+        (WebCore::TextBoxPainter::paintDecoration): Deleted.
+        * rendering/TextBoxPainter.h:
+        * rendering/TextDecorationPainter.cpp:
+        (WebCore::TextDecorationPainter::paintBackgroundDecorations):
+        (WebCore::TextDecorationPainter::paintForegroundDecorations):
+        (WebCore::TextDecorationPainter::paintLineThrough):
+        (WebCore::TextDecorationPainter::paintTextDecoration): Deleted.
+        * rendering/TextDecorationPainter.h:
+
 2021-12-13  Jer Noble  <jer.no...@apple.com>
 
         Unreviewed build fix; add a convenience function to safely compare possibly null CFStringRefs.

Modified: trunk/Source/WebCore/rendering/TextBoxPainter.cpp (286954 => 286955)


--- trunk/Source/WebCore/rendering/TextBoxPainter.cpp	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/Source/WebCore/rendering/TextBoxPainter.cpp	2021-12-13 18:23:00 UTC (rev 286955)
@@ -188,9 +188,6 @@
     // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
     auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualForeground(styledMarkedTexts);
 
-    for (auto& markedText : coalescedStyledMarkedTexts)
-        paintForeground(markedText);
-
     auto textDecorations = m_style.textDecorationsInEffect();
     bool highlightDecorations = !MarkedText::collectForHighlights(m_renderer, m_selectableRange, MarkedText::PaintPhase::Decoration).isEmpty();
     bool lineDecorations = !textDecorations.isEmpty();
@@ -224,8 +221,27 @@
         // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
         auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualDecorations(styledMarkedTexts);
 
+        for (auto& markedText : coalescedStyledMarkedTexts) {
+            unsigned startOffset = markedText.startOffset;
+            unsigned endOffset = markedText.endOffset;
+            if (startOffset < endOffset) {
+                // Avoid measuring the text when the entire line box is selected as an optimization.
+                FloatRect snappedSelectionRect = m_paintRect;
+                if (startOffset || endOffset != m_paintTextRun.length()) {
+                    LayoutRect selectionRect = { m_paintRect.x(), m_paintRect.y(), m_paintRect.width(), m_paintRect.height() };
+                    fontCascade().adjustSelectionRectForText(m_paintTextRun, selectionRect, startOffset, endOffset);
+                    snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, m_document.deviceScaleFactor(), m_paintTextRun.ltr());
+                }
+
+                TextDecorationPainter decorationPainter = createDecorationPainter(markedText, textDecorationSelectionClipOutRect, snappedSelectionRect);
+                paintBackgroundDecorations(decorationPainter, markedText, snappedSelectionRect);
+                paintForeground(markedText);
+                paintForegroundDecorations(decorationPainter, snappedSelectionRect);
+            }
+        }
+    } else {
         for (auto& markedText : coalescedStyledMarkedTexts)
-            paintDecoration(markedText, textDecorationSelectionClipOutRect);
+            paintForeground(markedText);
     }
 }
 
@@ -339,16 +355,9 @@
     textPainter.paintRange(m_paintTextRun, m_paintRect, textOriginFromPaintRect(m_paintRect), markedText.startOffset, markedText.endOffset);
 }
 
-void TextBoxPainter::paintDecoration(const StyledMarkedText& markedText, const FloatRect& clipOutRect)
+TextDecorationPainter TextBoxPainter::createDecorationPainter(const StyledMarkedText& markedText, const FloatRect& clipOutRect, const FloatRect& snappedSelectionRect)
 {
-    // 1. Compute text selection
-    unsigned startOffset = markedText.startOffset;
-    unsigned endOffset = markedText.endOffset;
-    if (startOffset >= endOffset)
-        return;
-
     GraphicsContext& context = m_paintInfo.context();
-    const FontCascade& font = fontCascade();
 
     updateGraphicsContext(context, markedText.style.textStyles);
 
@@ -358,16 +367,18 @@
 
     // Note that if the text is truncated, we let the thing being painted in the truncation
     // draw its own decoration.
-
-    // Avoid measuring the text when the entire line box is selected as an optimization.
-    FloatRect snappedSelectionRect = m_paintRect;
-    if (startOffset || endOffset != m_paintTextRun.length()) {
-        LayoutRect selectionRect = { m_paintRect.x(), m_paintRect.y(), m_paintRect.width(), m_paintRect.height() };
-        font.adjustSelectionRectForText(m_paintTextRun, selectionRect, startOffset, endOffset);
-        snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, m_document.deviceScaleFactor(), m_paintTextRun.ltr());
+    GraphicsContextStateSaver stateSaver { context, false };
+    bool isDraggedContent = markedText.type == MarkedText::DraggedContent;
+    if (isDraggedContent || !clipOutRect.isEmpty()) {
+        stateSaver.save();
+        if (isDraggedContent)
+            context.setAlpha(markedText.style.alpha);
+        if (!clipOutRect.isEmpty())
+            context.clipOut(clipOutRect);
     }
 
-    // 2. Paint
+    // Create painter
+    const FontCascade& font = fontCascade();
     auto textDecorations = m_style.textDecorationsInEffect();
     textDecorations.add(TextDecorationPainter::textDecorationsInEffectForStyle(markedText.style.textDecorationStyles));
     TextDecorationPainter decorationPainter { context, textDecorations, m_renderer, m_isFirstLine, font, markedText.style.textDecorationStyles };
@@ -380,21 +391,27 @@
             decorationPainter.setShadowColorFilter(&m_style.appleColorFilter());
     }
 
-    {
-        GraphicsContextStateSaver stateSaver { context, false };
-        bool isDraggedContent = markedText.type == MarkedText::DraggedContent;
-        if (isDraggedContent || !clipOutRect.isEmpty()) {
-            stateSaver.save();
-            if (isDraggedContent)
-                context.setAlpha(markedText.style.alpha);
-            if (!clipOutRect.isEmpty())
-                context.clipOut(clipOutRect);
-        }
-        decorationPainter.paintTextDecoration(m_paintTextRun.subRun(startOffset, endOffset - startOffset), textOriginFromPaintRect(snappedSelectionRect), snappedSelectionRect.location());
+    return decorationPainter;
+}
+
+void TextBoxPainter::paintBackgroundDecorations(TextDecorationPainter& decorationPainter, const StyledMarkedText& markedText, const FloatRect& snappedSelectionRect)
+{
+    decorationPainter.paintBackgroundDecorations(m_paintTextRun.subRun(markedText.startOffset, markedText.endOffset - markedText.startOffset), textOriginFromPaintRect(snappedSelectionRect), snappedSelectionRect.location());
+
+    if (textBox().isCombinedText()) {
+        GraphicsContext& context = m_paintInfo.context();
+        context.concatCTM(rotation(m_paintRect, Counterclockwise));
     }
+}
 
-    if (isCombinedText)
+void TextBoxPainter::paintForegroundDecorations(TextDecorationPainter& decorationPainter, const FloatRect& snappedSelectionRect)
+{
+    decorationPainter.paintForegroundDecorations(snappedSelectionRect.location());
+
+    if (textBox().isCombinedText()) {
+        GraphicsContext& context = m_paintInfo.context();
         context.concatCTM(rotation(m_paintRect, Counterclockwise));
+    }
 }
 
 void TextBoxPainter::paintCompositionUnderlines()

Modified: trunk/Source/WebCore/rendering/TextBoxPainter.h (286954 => 286955)


--- trunk/Source/WebCore/rendering/TextBoxPainter.h	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/Source/WebCore/rendering/TextBoxPainter.h	2021-12-13 18:23:00 UTC (rev 286955)
@@ -39,6 +39,7 @@
 class RenderStyle;
 class RenderText;
 class ShadowData;
+class TextDecorationPainter;
 struct CompositionUnderline;
 struct MarkedText;
 struct PaintInfo;
@@ -71,7 +72,9 @@
     void paintBackground(unsigned startOffset, unsigned endOffset, const Color&, BackgroundStyle = BackgroundStyle::Normal);
     void paintBackground(const StyledMarkedText&);
     void paintForeground(const StyledMarkedText&);
-    void paintDecoration(const StyledMarkedText&, const FloatRect& clipOutRect);
+    TextDecorationPainter createDecorationPainter(const StyledMarkedText&, const FloatRect&, const FloatRect&);
+    void paintBackgroundDecorations(TextDecorationPainter&, const StyledMarkedText&, const FloatRect&);
+    void paintForegroundDecorations(TextDecorationPainter&, const FloatRect&);
     void paintCompositionUnderline(const CompositionUnderline&);
     void paintPlatformDocumentMarker(const MarkedText&);
 

Modified: trunk/Source/WebCore/rendering/TextDecorationPainter.cpp (286954 => 286955)


--- trunk/Source/WebCore/rendering/TextDecorationPainter.cpp	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/Source/WebCore/rendering/TextDecorationPainter.cpp	2021-12-13 18:23:00 UTC (rev 286955)
@@ -201,7 +201,8 @@
 {
 }
 
-void TextDecorationPainter::paintTextDecoration(const TextRun& textRun, const FloatPoint& textOrigin, const FloatPoint& boxOrigin)
+// Paint text-shadow, underline, overline
+void TextDecorationPainter::paintBackgroundDecorations(const TextRun& textRun, const FloatPoint& textOrigin, const FloatPoint& boxOrigin)
 {
     const auto& fontMetrics = m_lineStyle.fontMetrics();
     float textDecorationThickness = m_lineStyle.textDecorationThickness().resolve(m_lineStyle.computedFontSize(), fontMetrics);
@@ -228,10 +229,8 @@
                 // FIXME: Need to support text-decoration-skip: none.
                 m_context.drawLineForText(rect, m_isPrinting, style == TextDecorationStyle::Double, strokeStyle);
             }
-        } else {
-            ASSERT(decoration == TextDecorationLine::LineThrough);
-            m_context.drawLineForText(rect, m_isPrinting, style == TextDecorationStyle::Double, strokeStyle);
-        }
+        } else
+            ASSERT_NOT_REACHED();
     };
 
     bool areLinesOpaque = !m_isPrinting && (!m_decorations.contains(TextDecorationLine::Underline) || m_styles.underlineColor.isOpaque())
@@ -293,13 +292,11 @@
             rect.move(0, autoTextDecorationThickness - textDecorationThickness - wavyOffset);
             paintDecoration(TextDecorationLine::Overline, m_styles.overlineStyle, m_styles.overlineColor, rect);
         }
-        if (m_decorations.contains(TextDecorationLine::LineThrough)) {
-            FloatRect rect(localOrigin, FloatSize(m_width, textDecorationThickness));
-            float autoTextDecorationThickness = TextDecorationThickness::createWithAuto().resolve(m_lineStyle.computedFontSize(), fontMetrics);
-            auto center = 2 * fontMetrics.floatAscent() / 3 + autoTextDecorationThickness / 2;
-            rect.move(0, center - textDecorationThickness / 2);
-            paintDecoration(TextDecorationLine::LineThrough, m_styles.linethroughStyle, m_styles.linethroughColor, rect);
-        }
+
+        // We only want to paint the shadow, hence the transparent color, not the actual line-through,
+        // which will be painted in paintForegroundDecorations().
+        if (shadow && m_decorations.contains(TextDecorationLine::LineThrough))
+            paintLineThrough(Color::transparentBlack, textDecorationThickness, localOrigin);
     } while (shadow);
 
     if (clipping)
@@ -308,6 +305,34 @@
         m_context.clearShadow();
 }
 
+void TextDecorationPainter::paintForegroundDecorations(const FloatPoint& boxOrigin)
+{
+    if (!m_decorations.contains(TextDecorationLine::LineThrough))
+        return;
+
+    float textDecorationThickness = m_lineStyle.textDecorationThickness().resolve(m_lineStyle.computedFontSize(), m_lineStyle.fontMetrics());
+    paintLineThrough(m_styles.linethroughColor, textDecorationThickness, boxOrigin);
+}
+
+void TextDecorationPainter::paintLineThrough(const Color& color, float thickness, const FloatPoint& localOrigin)
+{
+    const auto& fontMetrics = m_lineStyle.fontMetrics();
+    FloatRect rect(localOrigin, FloatSize(m_width, thickness));
+    float autoTextDecorationThickness = TextDecorationThickness::createWithAuto().resolve(m_lineStyle.computedFontSize(), fontMetrics);
+    auto center = 2 * fontMetrics.floatAscent() / 3 + autoTextDecorationThickness / 2;
+    rect.move(0, center - thickness / 2);
+
+    m_context.setStrokeColor(color);
+
+    TextDecorationStyle style = m_styles.linethroughStyle;
+    auto strokeStyle = textDecorationStyleToStrokeStyle(style);
+
+    if (style == TextDecorationStyle::Wavy)
+        strokeWavyTextDecoration(m_context, rect, m_lineStyle.computedFontPixelSize());
+    else
+        m_context.drawLineForText(rect, m_isPrinting, style == TextDecorationStyle::Double, strokeStyle);
+}
+
 static void collectStylesForRenderer(TextDecorationPainter::Styles& result, const RenderObject& renderer, OptionSet<TextDecorationLine> remainingDecorations, bool firstLineStyle, PseudoId pseudoId)
 {
     auto extractDecorations = [&] (const RenderStyle& style, OptionSet<TextDecorationLine> decorations) {

Modified: trunk/Source/WebCore/rendering/TextDecorationPainter.h (286954 => 286955)


--- trunk/Source/WebCore/rendering/TextDecorationPainter.h	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/Source/WebCore/rendering/TextDecorationPainter.h	2021-12-13 18:23:00 UTC (rev 286955)
@@ -52,7 +52,9 @@
     void setTextShadow(const ShadowData* textShadow) { m_shadow = textShadow; }
     void setShadowColorFilter(const FilterOperations* colorFilter) { m_shadowColorFilter = colorFilter; }
 
-    void paintTextDecoration(const TextRun&, const FloatPoint& textOrigin, const FloatPoint& boxOrigin);
+    void paintBackgroundDecorations(const TextRun&, const FloatPoint& textOrigin, const FloatPoint& boxOrigin);
+    void paintForegroundDecorations(const FloatPoint& boxOrigin);
+    void paintLineThrough(const Color&, float thickness, const FloatPoint& localOrigin);
 
     struct Styles {
         bool operator==(const Styles&) const;

Modified: trunk/Source/WebCore/style/InlineTextBoxStyle.cpp (286954 => 286955)


--- trunk/Source/WebCore/style/InlineTextBoxStyle.cpp	2021-12-13 18:13:44 UTC (rev 286954)
+++ trunk/Source/WebCore/style/InlineTextBoxStyle.cpp	2021-12-13 18:23:00 UTC (rev 286955)
@@ -189,7 +189,7 @@
     }
 
     // These metrics must match where underlines get drawn.
-    // FIXME: Share the code in TextDecorationPainter::paintTextDecoration() so we can just query it for the painted geometry.
+    // FIXME: Share the code in TextDecorationPainter::paintBackgroundDecorations() so we can just query it for the painted geometry.
     if (decoration & TextDecorationLine::Underline) {
         // Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText()
         int underlineOffset = 1;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to