Changeset 237324 in webkit


Ignore:
Timestamp:
Oct 22, 2018 9:27:32 AM (6 years ago)
Author:
Alan Bujtas
Message:

[LFC][IFC] Add justify text-align support.
https://bugs.webkit.org/show_bug.cgi?id=190779

Reviewed by Antti Koivisto.

Collect expansion opportunities and adjust runs accordingly.

  • layout/inlineformatting/InlineFormattingContext.cpp:

(WebCore::Layout::InlineFormattingContext::layoutInlineContent const):

  • layout/inlineformatting/InlineFormattingContext.h:
  • layout/inlineformatting/InlineRun.h:

(WebCore::Layout::InlineRun::expansionOpportunity):
(WebCore::Layout::InlineRun::TextContext::setStart):
(WebCore::Layout::InlineRun::TextContext::setLength):
(WebCore::Layout::InlineRun::setTextContext):
(WebCore::Layout::InlineRun::createRun): Deleted.
(WebCore::Layout::InlineRun::createTextRun): Deleted.

  • layout/inlineformatting/Line.cpp:

(WebCore::Layout::InlineFormattingContext::Line::Line):
(WebCore::Layout::InlineFormattingContext::Line::init):
(WebCore::Layout::InlineFormattingContext::Line::computeExpansionOpportunities):
(WebCore::Layout::InlineFormattingContext::Line::appendContent):
(WebCore::Layout::InlineFormattingContext::Line::justifyRuns):
(WebCore::Layout::InlineFormattingContext::Line::close):
(WebCore::Layout::isNonCollapsedText): Deleted.

Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r237323 r237324  
     12018-10-22  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Add justify text-align support.
     4        https://bugs.webkit.org/show_bug.cgi?id=190779
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Collect expansion opportunities and adjust runs accordingly.
     9
     10        * layout/inlineformatting/InlineFormattingContext.cpp:
     11        (WebCore::Layout::InlineFormattingContext::layoutInlineContent const):
     12        * layout/inlineformatting/InlineFormattingContext.h:
     13        * layout/inlineformatting/InlineRun.h:
     14        (WebCore::Layout::InlineRun::expansionOpportunity):
     15        (WebCore::Layout::InlineRun::TextContext::setStart):
     16        (WebCore::Layout::InlineRun::TextContext::setLength):
     17        (WebCore::Layout::InlineRun::setTextContext):
     18        (WebCore::Layout::InlineRun::createRun): Deleted.
     19        (WebCore::Layout::InlineRun::createTextRun): Deleted.
     20        * layout/inlineformatting/Line.cpp:
     21        (WebCore::Layout::InlineFormattingContext::Line::Line):
     22        (WebCore::Layout::InlineFormattingContext::Line::init):
     23        (WebCore::Layout::InlineFormattingContext::Line::computeExpansionOpportunities):
     24        (WebCore::Layout::InlineFormattingContext::Line::appendContent):
     25        (WebCore::Layout::InlineFormattingContext::Line::justifyRuns):
     26        (WebCore::Layout::InlineFormattingContext::Line::close):
     27        (WebCore::Layout::isNonCollapsedText): Deleted.
     28
    1292018-10-22  Zalan Bujtas  <zalan@apple.com>
    230
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp

    r237323 r237324  
    144144        line.appendContent(*run);
    145145    }
    146     line.close();
     146    line.close(Line::LastLine::Yes);
    147147}
    148148
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h

    r237323 r237324  
    5454        void init(LayoutUnit lineLogicalLeft, LayoutUnit availableWidth);
    5555        void appendContent(const InlineLineBreaker::Run&);
    56         void close();
     56        enum class LastLine { No, Yes };
     57        void close(LastLine = LastLine::No);
    5758
    5859        bool hasContent() const { return m_firstRunIndex.has_value(); }
     
    6162
    6263    private:
     64        void justifyRuns();
     65        void computeExpansionOpportunities(const InlineLineBreaker::Run&);
     66
    6367        struct TrailingTrimmableContent {
    6468            LayoutUnit width;
     
    6670        };
    6771        std::optional<TrailingTrimmableContent> m_trailingTrimmableContent;
    68         bool m_lastRunIsNotCollapsedText { true };
     72        bool m_lastRunIsWhitespace { false };
     73        bool m_lastRunCanExpand { false };
    6974
    7075        InlineFormattingState& m_formattingState;
     
    7681
    7782        std::optional<unsigned> m_firstRunIndex;
     83        bool m_alignmentIsJustify { false };
    7884    };
    7985
  • trunk/Source/WebCore/layout/inlineformatting/InlineRun.h

    r237323 r237324  
    3535
    3636struct InlineRun {
    37     static InlineRun createRun(LayoutUnit logcialLeft, LayoutUnit width, const InlineItem&);
    38     static InlineRun createTextRun(LayoutUnit logcialLeft, LayoutUnit width, ItemPosition, unsigned length, const InlineItem&);
     37    InlineRun(LayoutUnit logcialLeft, LayoutUnit width, const InlineItem&);
    3938
    4039    LayoutUnit logicalLeft() const { return m_logicalLeft; }
     
    4645    void setLogicalRight(LayoutUnit logicalRight) { m_width -= (this->logicalRight() - logicalRight); }
    4746
     47    struct ExpansionOpportunity {
     48        unsigned count { 0 };
     49        ExpansionBehavior behavior { ForbidLeadingExpansion | ForbidTrailingExpansion };
     50        LayoutUnit expansion;
     51    };
     52    ExpansionOpportunity& expansionOpportunity() { return m_expansionOpportunity; }
     53
    4854    struct TextContext {
    4955    public:
    5056        TextContext(ItemPosition, unsigned length);
    5157
     58        void setStart(ItemPosition start) { m_start = start; }
     59        void setLength(unsigned length) { m_length = length; }
     60
    5261        ItemPosition start() const { return m_start; }
    5362        unsigned length() const { return m_length; }
    54 
    55         void setLength(unsigned length) { m_length = length; }
    5663
    5764    private:
     
    5966        unsigned m_length;
    6067    };
     68    void setTextContext(TextContext textContext) { m_textContext.emplace(textContext); }
    6169    std::optional<TextContext>& textContext() { return m_textContext; }
     70
    6271    const InlineItem& inlineItem() const { return m_inlineItem; }
    6372
    6473private:
    65     InlineRun(LayoutUnit logcialLeft, LayoutUnit width, const InlineItem&);
    66     InlineRun(LayoutUnit logcialLeft, LayoutUnit width, ItemPosition, unsigned length, const InlineItem&);
    67 
    6874    LayoutUnit m_logicalLeft;
    6975    LayoutUnit m_width;
     76    ExpansionOpportunity m_expansionOpportunity;
    7077
    7178    const InlineItem& m_inlineItem;
     
    7582using InlineRuns = Vector<InlineRun>;
    7683
    77 inline InlineRun InlineRun::createRun(LayoutUnit logicalLeft, LayoutUnit width, const InlineItem& inlineItem)
    78 {
    79     return { logicalLeft, width, inlineItem };
    80 }
    81 
    82 inline InlineRun InlineRun::createTextRun(LayoutUnit logicalLeft, LayoutUnit width, ItemPosition start, unsigned length, const InlineItem& inlineItem)
    83 {
    84     return { logicalLeft, width, start, length, inlineItem };
    85 }
    86 
    8784inline InlineRun::InlineRun(LayoutUnit logicalLeft, LayoutUnit width, const InlineItem& inlineItem)
    8885    : m_logicalLeft(logicalLeft)
    8986    , m_width(width)
    9087    , m_inlineItem(inlineItem)
    91 {
    92 }
    93 
    94 inline InlineRun::InlineRun(LayoutUnit logicalLeft, LayoutUnit width, ItemPosition start, unsigned length, const InlineItem& inlineItem)
    95     : m_logicalLeft(logicalLeft)
    96     , m_width(width)
    97     , m_inlineItem(inlineItem)
    98     , m_textContext(TextContext { start, length })
    9988{
    10089}
  • trunk/Source/WebCore/layout/inlineformatting/Line.cpp

    r237323 r237324  
    3737    : m_formattingState(formattingState)
    3838    , m_formattingRoot(formattingRoot)
     39    , m_alignmentIsJustify(m_formattingRoot.style().textAlign() == TextAlignMode::Justify)
    3940{
    4041}
     
    4546    m_lineWidth = availableWidth;
    4647    m_availableWidth = availableWidth;
     48
    4749    m_firstRunIndex = { };
     50    m_lastRunIsWhitespace = false;
     51    m_lastRunCanExpand = false;
     52    m_trailingTrimmableContent = { };
    4853}
    4954
     
    7075}
    7176
    72 static bool isNonCollapsedText(const InlineRunProvider::Run& inlineRun)
    73 {
    74     return inlineRun.isText() && !inlineRun.textContext()->isCollapsed();
    75 }
    76 
    7777static bool isTrimmableContent(const InlineRunProvider::Run& inlineRun)
    7878{
     
    8888}
    8989
     90void InlineFormattingContext::Line::computeExpansionOpportunities(const InlineLineBreaker::Run& run)
     91{
     92    if (!m_alignmentIsJustify)
     93        return;
     94
     95    auto isExpansionOpportunity = [](auto currentRunIsWhitespace, auto lastRunIsWhitespace) {
     96        return currentRunIsWhitespace || (!currentRunIsWhitespace && !lastRunIsWhitespace);
     97    };
     98
     99    auto expansionBehavior = [](auto isAtExpansionOpportunity) {
     100        ExpansionBehavior expansionBehavior = AllowTrailingExpansion;
     101        expansionBehavior |= isAtExpansionOpportunity ? ForbidLeadingExpansion : AllowLeadingExpansion;
     102        return expansionBehavior;
     103    };
     104
     105    auto isAtExpansionOpportunity = isExpansionOpportunity(run.content.isWhitespace(), m_lastRunIsWhitespace);
     106
     107    auto& currentInlineRun = m_formattingState.inlineRuns().last();
     108    auto& expansionOpportunity = currentInlineRun.expansionOpportunity();
     109    if (isAtExpansionOpportunity)
     110        ++expansionOpportunity.count;
     111
     112    expansionOpportunity.behavior = expansionBehavior(isAtExpansionOpportunity);
     113}
     114
    90115void InlineFormattingContext::Line::appendContent(const InlineLineBreaker::Run& run)
    91116{
    92     auto lastRunWasNotCollapsedText = m_lastRunIsNotCollapsedText;
    93     m_trailingTrimmableContent = { };
    94     m_availableWidth -= run.width;
    95 
    96117    auto& content = run.content;
    97     m_lastRunIsNotCollapsedText = isNonCollapsedText(content);
    98118
    99119    // Append this text run to the end of the last text run, if the last run is continuous.
     
    102122        auto textContext = content.textContext();
    103123        auto runLength = textContext->isCollapsed() ? 1 : textContext->length();
    104 
    105         if (isTrimmableContent(content))
    106             m_trailingTrimmableContent = TrailingTrimmableContent { run.width, runLength };
    107 
    108         if (hasContent() && lastRunWasNotCollapsedText) {
    109             auto& inlineRun = m_formattingState.inlineRuns().last();
    110             inlineRun.setWidth(inlineRun.width() + run.width);
    111             inlineRun.textContext()->setLength(inlineRun.textContext()->length() + runLength);
    112             return;
    113         }
    114124        textRun = InlineRun::TextContext { textContext->start(), runLength };
    115125    }
    116126
    117     if (textRun)
    118         m_formattingState.appendInlineRun(InlineRun::createTextRun(contentLogicalRight(), run.width, textRun->start(), textRun->length(), content.inlineItem()));
    119     else
    120         m_formattingState.appendInlineRun(InlineRun::createRun(contentLogicalRight(), run.width, content.inlineItem()));
     127    auto requiresNewInlineRun = !hasContent() || !content.isText() || !m_lastRunCanExpand;
     128    if (requiresNewInlineRun) {
     129        auto inlineRun = InlineRun { contentLogicalRight(), run.width, content.inlineItem() };
     130        if (textRun)
     131            inlineRun.setTextContext({ textRun->start(), textRun->length() });
     132        m_formattingState.appendInlineRun(inlineRun);
     133    } else {
     134        // Non-text runs always require new inline run.
     135        ASSERT(textRun);
     136        auto& inlineRun = m_formattingState.inlineRuns().last();
     137        inlineRun.setWidth(inlineRun.width() + run.width);
     138        inlineRun.textContext()->setLength(inlineRun.textContext()->length() + textRun->length());
     139    }
     140
     141    computeExpansionOpportunities(run);
     142
     143    m_availableWidth -= run.width;
     144    m_lastRunIsWhitespace = content.isWhitespace();
     145    m_lastRunCanExpand = content.isText() && !content.textContext()->isCollapsed();
    121146    m_firstRunIndex = m_firstRunIndex.value_or(m_formattingState.inlineRuns().size() - 1);
    122 }
    123 
    124 void InlineFormattingContext::Line::close()
     147    m_trailingTrimmableContent = { };
     148    if (isTrimmableContent(content))
     149        m_trailingTrimmableContent = TrailingTrimmableContent { run.width, textRun->length() };
     150}
     151
     152void InlineFormattingContext::Line::justifyRuns()
     153{
     154    if (!hasContent())
     155        return;
     156
     157    auto& inlineRuns = m_formattingState.inlineRuns();
     158    auto& lastInlineRun = inlineRuns.last();
     159
     160    // Adjust (forbid) trailing expansion for the last text run on line.
     161    auto expansionBehavior = lastInlineRun.expansionOpportunity().behavior;
     162    // Remove allow and add forbid.
     163    expansionBehavior ^= AllowTrailingExpansion;
     164    expansionBehavior |= ForbidTrailingExpansion;
     165    lastInlineRun.expansionOpportunity().behavior = expansionBehavior;
     166
     167    // Collect expansion opportunities and justify the runs.
     168    auto widthToDistribute = availableWidth();
     169    if (widthToDistribute <= 0)
     170        return;
     171
     172    auto expansionOpportunities = 0;
     173    for (auto runIndex = *m_firstRunIndex; runIndex < inlineRuns.size(); ++runIndex)
     174        expansionOpportunities += inlineRuns[runIndex].expansionOpportunity().count;
     175
     176    if (!expansionOpportunities)
     177        return;
     178
     179    float expansion = widthToDistribute.toFloat() / expansionOpportunities;
     180    LayoutUnit accumulatedExpansion = 0;
     181    for (auto runIndex = *m_firstRunIndex; runIndex < inlineRuns.size(); ++runIndex) {
     182        auto& inlineRun = inlineRuns[runIndex];
     183        auto expansionForRun = inlineRun.expansionOpportunity().count * expansion;
     184
     185        inlineRun.expansionOpportunity().expansion = expansionForRun;
     186        inlineRun.setLogicalLeft(inlineRun.logicalLeft() + accumulatedExpansion);
     187        inlineRun.setWidth(inlineRun.width() + expansionForRun);
     188        accumulatedExpansion += expansionForRun;
     189    }
     190}
     191
     192void InlineFormattingContext::Line::close(LastLine isLastLine)
    125193{
    126194    auto trimTrailingContent = [&]() {
     
    134202
    135203        if (!lastInlineRun.textContext()->length()) {
     204            if (*m_firstRunIndex == m_formattingState.inlineRuns().size() - 1)
     205                m_firstRunIndex = { };
    136206            m_formattingState.inlineRuns().removeLast();
    137             if (m_firstRunIndex.value())
    138                 --*m_firstRunIndex;
    139             else
    140                 m_firstRunIndex = { };
    141207        }
    142208        m_availableWidth += m_trailingTrimmableContent->width;
     
    144210    };
    145211
    146     auto alignRuns = [&]() {
     212    auto alignRuns = [&](auto alignment) {
    147213
    148214        if (!hasContent())
    149215            return;
    150216
    151         auto adjustedLogicalLeft = adjustedLineLogicalLeft(m_formattingRoot.style().textAlign(), m_lineLogicalLeft, m_availableWidth);
     217        auto adjustedLogicalLeft = adjustedLineLogicalLeft(alignment, m_lineLogicalLeft, m_availableWidth);
    152218        if (m_lineLogicalLeft == adjustedLogicalLeft)
    153219            return;
     
    163229
    164230    trimTrailingContent();
    165     alignRuns();
     231
     232    auto textAlignment = m_formattingRoot.style().textAlign();
     233    if (m_alignmentIsJustify) {
     234        if (isLastLine == LastLine::No) {
     235            justifyRuns();
     236            return;
     237        }
     238        textAlignment = TextAlignMode::Left;
     239    }
     240    alignRuns(textAlignment);
    166241}
    167242
Note: See TracChangeset for help on using the changeset viewer.