Title: [251917] trunk/Source/WebCore
Revision
251917
Author
za...@apple.com
Date
2019-11-01 08:35:43 -0700 (Fri, 01 Nov 2019)

Log Message

[LFC][IFC] Add support for text-align: justify
https://bugs.webkit.org/show_bug.cgi?id=203715
<rdar://problem/56804607>

Reviewed by Antti Koivisto.

Add basic support for text-align: justify. Register expansion opportunities as the text content is being
appended to the line (Line::appendTextContent) and distribute the extra space when the line is being closed (Line::close).
Now LFC rendering of OpenSource/PerformanceTests/Layout/line-layout-simple.html visually matches trunk rendering.

* layout/displaytree/DisplayPainter.cpp:
(WebCore::Display::paintInlineContent):
* layout/displaytree/DisplayRun.h:
(WebCore::Display::Run::TextContext::resetExpansion):
* layout/inlineformatting/InlineLine.cpp:
(WebCore::Layout::Line::Run::expand):
(WebCore::Layout::Line::close):
(WebCore::Layout::Line::justifyRuns):
(WebCore::Layout::Line::alignContentHorizontally):
(WebCore::Layout::Line::appendTextContent):
* layout/inlineformatting/InlineLine.h:
(WebCore::Layout::Line::isTextAlignJustify const):
(WebCore::Layout::Line::Run::setCollapsesToZeroAdvanceWidth):
(WebCore::Layout::Line::Run::adjustExpansionBehavior):
(WebCore::Layout::Line::Run::expand): Deleted.
* layout/inlineformatting/InlineLineLayout.cpp:
(WebCore::Layout::LineLayout::close):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (251916 => 251917)


--- trunk/Source/WebCore/ChangeLog	2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/ChangeLog	2019-11-01 15:35:43 UTC (rev 251917)
@@ -1,3 +1,33 @@
+2019-11-01  Zalan Bujtas  <za...@apple.com>
+
+        [LFC][IFC] Add support for text-align: justify
+        https://bugs.webkit.org/show_bug.cgi?id=203715
+        <rdar://problem/56804607>
+
+        Reviewed by Antti Koivisto.
+
+        Add basic support for text-align: justify. Register expansion opportunities as the text content is being
+        appended to the line (Line::appendTextContent) and distribute the extra space when the line is being closed (Line::close).
+        Now LFC rendering of OpenSource/PerformanceTests/Layout/line-layout-simple.html visually matches trunk rendering.
+
+        * layout/displaytree/DisplayPainter.cpp:
+        (WebCore::Display::paintInlineContent):
+        * layout/displaytree/DisplayRun.h:
+        (WebCore::Display::Run::TextContext::resetExpansion):
+        * layout/inlineformatting/InlineLine.cpp:
+        (WebCore::Layout::Line::Run::expand):
+        (WebCore::Layout::Line::close):
+        (WebCore::Layout::Line::justifyRuns):
+        (WebCore::Layout::Line::alignContentHorizontally):
+        (WebCore::Layout::Line::appendTextContent):
+        * layout/inlineformatting/InlineLine.h:
+        (WebCore::Layout::Line::isTextAlignJustify const):
+        (WebCore::Layout::Line::Run::setCollapsesToZeroAdvanceWidth):
+        (WebCore::Layout::Line::Run::adjustExpansionBehavior):
+        (WebCore::Layout::Line::Run::expand): Deleted.
+        * layout/inlineformatting/InlineLineLayout.cpp:
+        (WebCore::Layout::LineLayout::close):
+
 2019-11-01  Antti Koivisto  <an...@apple.com>
 
         Style::Builder should not depend on StyleResolver

Modified: trunk/Source/WebCore/layout/displaytree/DisplayPainter.cpp (251916 => 251917)


--- trunk/Source/WebCore/layout/displaytree/DisplayPainter.cpp	2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/displaytree/DisplayPainter.cpp	2019-11-01 15:35:43 UTC (rev 251917)
@@ -127,7 +127,7 @@
             auto& lineBox = formattingState.lineBoxForRun(*run);
             auto baselineOffset = rootAbsoluteDisplayBox.top() + lineBox.logicalTop() + lineBox.baselineOffset();
             if (auto expansionContext = textContext->expansion())
-                context.drawText(style.fontCascade(), TextRun { textContext->content(), logicalLeft, expansionContext->horiztontalExpansion, expansionContext->behavior }, { logicalLeft, baselineOffset });
+                context.drawText(style.fontCascade(), TextRun { textContext->content(), logicalLeft, expansionContext->horizontalExpansion, expansionContext->behavior }, { logicalLeft, baselineOffset });
             else
                 context.drawText(style.fontCascade(), TextRun { textContext->content(), logicalLeft }, { logicalLeft, baselineOffset });
         } else if (auto* cachedImage = run->image()) {

Modified: trunk/Source/WebCore/layout/displaytree/DisplayRun.h (251916 => 251917)


--- trunk/Source/WebCore/layout/displaytree/DisplayRun.h	2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/displaytree/DisplayRun.h	2019-11-01 15:35:43 UTC (rev 251917)
@@ -53,9 +53,10 @@
 
         struct ExpansionContext {
             ExpansionBehavior behavior;
-            LayoutUnit horiztontalExpansion;
+            LayoutUnit horizontalExpansion;
         };
         void setExpansion(ExpansionContext expansionContext) { m_expansionContext = expansionContext; }
+        void resetExpansion() { m_expansionContext = WTF::nullopt; }
         Optional<ExpansionContext> expansion() const { return m_expansionContext; }
 
         void expand(const TextContext& other);

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp (251916 => 251917)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2019-11-01 15:35:43 UTC (rev 251917)
@@ -46,6 +46,35 @@
 {
 }
 
+void Line::Run::expand(const Run& other)
+{
+    ASSERT(isText());
+    ASSERT(other.isText());
+    ASSERT(!isCollapsedToZeroAdvanceWidth());
+    ASSERT(!hasTrailingCollapsedContent());
+
+    auto& otherDisplayRun = other.displayRun();
+    m_displayRun.expandHorizontally(otherDisplayRun.logicalWidth());
+    m_displayRun.textContext()->expand(*otherDisplayRun.textContext());
+    m_hasTrailingCollapsedContent = other.isCollapsed();
+    m_isWhitespace &= other.isWhitespace();
+    m_isCollapsible = false;
+
+    // FIXME: This is a very simple expansion merge. We should eventually switch over to FontCascade::expansionOpportunityCount. 
+    if (hasExpansionOpportunity() && other.hasExpansionOpportunity()) {
+        // Run is expanded with a whitespace content.
+        adjustExpansionBehavior(ForbidLeadingExpansion | AllowTrailingExpansion);
+        m_expansionOpportunityCount = *m_expansionOpportunityCount + *other.expansionOpportunityCount();
+    } else if (!hasExpansionOpportunity() && other.hasExpansionOpportunity()) {
+        // Nonwhitespace runs is expanded with whitespace.
+        setHasExpansionOpportunity(ForbidLeadingExpansion | AllowTrailingExpansion);
+        m_expansionOpportunityCount = other.expansionOpportunityCount();
+    } else if (hasExpansionOpportunity() && !other.hasExpansionOpportunity()) {
+        // Run is expanded with a nonwhitespace content.
+        adjustExpansionBehavior(AllowLeadingExpansion | AllowTrailingExpansion);
+    }
+}
+
 Line::Line(const InlineFormattingContext& inlineFormattingContext, const InitialConstraints& initialConstraints, Optional<TextAlignMode> horizontalAlignment, SkipAlignment skipAlignment)
     : m_inlineFormattingContext(inlineFormattingContext)
     , m_initialStrut(initialConstraints.heightAndBaseline ? initialConstraints.heightAndBaseline->strut : WTF::nullopt)
@@ -112,7 +141,7 @@
     return true;
 }
 
-Line::RunList Line::close()
+Line::RunList Line::close(IsLastLineWithInlineContent isLastLineWithInlineContent)
 {
     removeTrailingTrimmableContent();
     // Join text runs together when possible.
@@ -145,7 +174,7 @@
 
     if (!m_skipAlignment) {
         alignContentVertically();
-        alignContentHorizontally();
+        alignContentHorizontally(isLastLineWithInlineContent);
     }
 
     return WTFMove(m_runList);
@@ -223,10 +252,53 @@
     }
 }
 
-void Line::alignContentHorizontally()
+void Line::justifyRuns()
 {
+    ASSERT(!m_runList.isEmpty());
+    ASSERT(availableWidth() > 0);
+    // Need to fix up the last run first.
+    auto& lastRun = m_runList.last();
+    if (lastRun->hasExpansionOpportunity())
+        lastRun->adjustExpansionBehavior(*lastRun->expansionBehavior() | ForbidTrailingExpansion);
+    // Collect the expansion opportunity numbers.
+    auto expansionOpportunityCount = 0;
+    for (auto& run : m_runList) {
+        if (!run->hasExpansionOpportunity())
+            continue;
+        expansionOpportunityCount += *run->expansionOpportunityCount();
+    }
+    // Nothing to distribute?
+    if (!expansionOpportunityCount)
+        return;
+    // Distribute the extra space.
+    auto expansionToDistribute = availableWidth() / expansionOpportunityCount;
+    LayoutUnit accumulatedExpansion;
+    for (auto& run : m_runList) {
+        // Expand and moves runs by the accumulated expansion.
+        if (!run->hasExpansionOpportunity()) {
+            run->moveHorizontally(accumulatedExpansion);
+            continue;
+        }
+        auto computedExpansion = expansionToDistribute * *run->expansionOpportunityCount();
+        run->setComputedHorizontalExpansion(computedExpansion);
+        run->moveHorizontally(accumulatedExpansion);
+        accumulatedExpansion += computedExpansion;
+    }
+}
+
+void Line::alignContentHorizontally(IsLastLineWithInlineContent lastLine)
+{
     ASSERT(!m_skipAlignment);
+    if (m_runList.isEmpty() || availableWidth() <= 0)
+        return;
 
+    if (isTextAlignJustify()) {
+        // Do not justify align the last line.
+        if (lastLine == IsLastLineWithInlineContent::No)
+            justifyRuns();
+        return;
+    }
+
     auto adjustmentForAlignment = [&]() -> Optional<LayoutUnit> {
         switch (*m_horizontalAlignment) {
         case TextAlignMode::Left:
@@ -382,6 +454,12 @@
     auto textContent = inlineItem.layoutBox().textContent().substring(contentStart, contentLength);
     auto lineRun = makeUnique<Run>(inlineItem, Display::Run { inlineItem.style(), logicalRect, Display::Run::TextContext { contentStart, contentLength, textContent } });
 
+    if (isTextAlignJustify()) {
+        // Register expansion opportunity for whitespace runs.
+        if (inlineItem.isWhitespace())
+            lineRun->setHasExpansionOpportunity(DefaultExpansion);
+    }
+
     auto collapsesToZeroAdvanceWidth = willCollapseCompletely();
     if (collapsesToZeroAdvanceWidth)
         lineRun->setCollapsesToZeroAdvanceWidth();

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.h (251916 => 251917)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.h	2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.h	2019-11-01 15:35:43 UTC (rev 251917)
@@ -97,6 +97,7 @@
         bool hasExpansionOpportunity() const { return m_expansionOpportunityCount.hasValue(); }
         Optional<ExpansionBehavior> expansionBehavior() const;
         Optional<unsigned> expansionOpportunityCount() const { return m_expansionOpportunityCount; }
+        void adjustExpansionBehavior(ExpansionBehavior);
         void setComputedHorizontalExpansion(LayoutUnit logicalExpansion);
 
         bool isCollapsible() const { return m_isCollapsible; }
@@ -114,7 +115,8 @@
         Optional<unsigned> m_expansionOpportunityCount;
     };
     using RunList = Vector<std::unique_ptr<Run>>;
-    RunList close();
+    enum class IsLastLineWithInlineContent { No, Yes };
+    RunList close(IsLastLineWithInlineContent = IsLastLineWithInlineContent::No);
 
     static LineBox::Baseline halfLeadingMetrics(const FontMetrics&, LayoutUnit lineLogicalHeight);
 
@@ -141,7 +143,7 @@
     void appendLineBreak(const InlineItem&);
 
     void removeTrailingTrimmableContent();
-    void alignContentHorizontally();
+    void alignContentHorizontally(IsLastLineWithInlineContent);
     void alignContentVertically();
 
     void adjustBaselineAndLineHeight(const InlineItem&);
@@ -148,6 +150,9 @@
     LayoutUnit inlineItemContentHeight(const InlineItem&) const;
     bool isVisuallyEmpty() const;
 
+    bool isTextAlignJustify() const { return m_horizontalAlignment == TextAlignMode::Justify; };
+    void justifyRuns();
+
     LayoutState& layoutState() const;
     const InlineFormattingContext& formattingContext() const; 
 
@@ -162,21 +167,6 @@
     LineBox m_lineBox;
 };
 
-inline void Line::Run::expand(const Run& other)
-{
-    ASSERT(isText());
-    ASSERT(other.isText());
-    ASSERT(!isCollapsedToZeroAdvanceWidth());
-    ASSERT(!hasTrailingCollapsedContent());
-
-    auto& otherDisplayRun = other.displayRun();
-    m_displayRun.expandHorizontally(otherDisplayRun.logicalWidth());
-    m_displayRun.textContext()->expand(*otherDisplayRun.textContext());
-    m_hasTrailingCollapsedContent = other.isCollapsed();
-    m_isWhitespace &= other.isWhitespace();
-    m_isCollapsible = false;
-}
-
 inline void Line::Run::setIsCollapsed()
 {
     ASSERT(isWhitespace());
@@ -195,6 +185,8 @@
     setIsCollapsed();
     m_collapsedToZeroAdvanceWidth = true;
     m_displayRun.setLogicalWidth({ });
+    m_expansionOpportunityCount = { };
+    m_displayRun.textContext()->resetExpansion();
 }
 
 inline Optional<ExpansionBehavior> Line::Run::expansionBehavior() const
@@ -213,6 +205,13 @@
     m_displayRun.textContext()->setExpansion({ expansionBehavior, { } });
 }
 
+inline void Line::Run::adjustExpansionBehavior(ExpansionBehavior expansionBehavior)
+{
+    ASSERT(isText());
+    ASSERT(hasExpansionOpportunity());
+    m_displayRun.textContext()->setExpansion({ expansionBehavior, m_displayRun.textContext()->expansion()->horizontalExpansion });
+}
+
 inline void Line::Run::setComputedHorizontalExpansion(LayoutUnit logicalExpansion)
 {
     ASSERT(isText());

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp (251916 => 251917)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp	2019-11-01 15:20:19 UTC (rev 251916)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp	2019-11-01 15:35:43 UTC (rev 251917)
@@ -143,7 +143,21 @@
     if (m_overflowTextLength)
         overflowContent = PartialContent { *m_overflowTextLength };
     auto trailingInlineItemIndex = m_lineInput.leadingInlineItemIndex + m_committedInlineItemCount - 1;
-    return LineContent { trailingInlineItemIndex, overflowContent, WTFMove(m_floats), m_line.close(), m_line.lineBox() };
+
+    auto isLastLineWithInlineContent = [&] {
+        if (overflowContent)
+            return Line::IsLastLineWithInlineContent::No;
+        // Skip floats backwards to see if this is going to be the last line with inline content.
+        for (auto i = m_lineInput.inlineItems.size(); i--;) {
+            if (!m_lineInput.inlineItems[i]->isFloat())
+                return i == trailingInlineItemIndex ? Line::IsLastLineWithInlineContent::Yes : Line::IsLastLineWithInlineContent::No;
+        }
+        // There has to be at least one non-float item.
+        ASSERT_NOT_REACHED();
+        return Line::IsLastLineWithInlineContent::No;
+    };
+
+    return LineContent { trailingInlineItemIndex, overflowContent, WTFMove(m_floats), m_line.close(isLastLineWithInlineContent()), m_line.lineBox() };
 }
 
 LineLayout::IsEndOfLine LineLayout::placeInlineItem(const InlineItem& inlineItem)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to