Changeset 175601 in webkit


Ignore:
Timestamp:
Nov 4, 2014 7:47:45 PM (9 years ago)
Author:
Alan Bujtas
Message:

Simple line layout: Abstract out content iteration and text handling in general.
https://bugs.webkit.org/show_bug.cgi?id=138346

Reviewed by Antti Koivisto.

Add a class that can act as the primary iterator/fragment handling interface to the line parser.
This helps adding support multiple renderer elements without changing the parser logic.
Currently it operates strictly on the first child of the RenderBlockFlow.

Covered by existing tests.

  • rendering/SimpleLineLayout.cpp:

(WebCore::SimpleLineLayout::FlowContentIterator::FlowContentIterator):
(WebCore::SimpleLineLayout::FlowContentIterator::findNextBreakablePosition):
(WebCore::SimpleLineLayout::FlowContentIterator::findNextNonWhitespacePosition):
(WebCore::SimpleLineLayout::FlowContentIterator::textWidth):
(WebCore::SimpleLineLayout::FlowContentIterator::isNewlineCharacter):
(WebCore::SimpleLineLayout::FlowContentIterator::isEndOfContent):
(WebCore::SimpleLineLayout::FlowContentIterator::style):
(WebCore::SimpleLineLayout::computeLineLeft):
(WebCore::SimpleLineLayout::TextFragment::TextFragment):
(WebCore::SimpleLineLayout::removeTrailingWhitespace):
(WebCore::SimpleLineLayout::initializeNewLine):
(WebCore::SimpleLineLayout::splitFragmentToFitLine):
(WebCore::SimpleLineLayout::nextFragment):
(WebCore::SimpleLineLayout::createLineRuns):
(WebCore::SimpleLineLayout::closeLineEndingAndAdjustRuns):
(WebCore::SimpleLineLayout::createTextRuns):
(WebCore::SimpleLineLayout::create):
(WebCore::SimpleLineLayout::skipWhitespace): Deleted.
(WebCore::SimpleLineLayout::textWidth): Deleted.

  • rendering/SimpleLineLayout.h:
Location:
trunk/Source/WebCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r175600 r175601  
     12014-11-04  Zalan Bujtas  <zalan@apple.com>
     2
     3        Simple line layout: Abstract out content iteration and text handling in general.
     4        https://bugs.webkit.org/show_bug.cgi?id=138346
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Add a class that can act as the primary iterator/fragment handling interface to the line parser.
     9        This helps adding support multiple renderer elements without changing the parser logic.
     10        Currently it operates strictly on the first child of the RenderBlockFlow. 
     11
     12        Covered by existing tests.
     13
     14        * rendering/SimpleLineLayout.cpp:
     15        (WebCore::SimpleLineLayout::FlowContentIterator::FlowContentIterator):
     16        (WebCore::SimpleLineLayout::FlowContentIterator::findNextBreakablePosition):
     17        (WebCore::SimpleLineLayout::FlowContentIterator::findNextNonWhitespacePosition):
     18        (WebCore::SimpleLineLayout::FlowContentIterator::textWidth):
     19        (WebCore::SimpleLineLayout::FlowContentIterator::isNewlineCharacter):
     20        (WebCore::SimpleLineLayout::FlowContentIterator::isEndOfContent):
     21        (WebCore::SimpleLineLayout::FlowContentIterator::style):
     22        (WebCore::SimpleLineLayout::computeLineLeft):
     23        (WebCore::SimpleLineLayout::TextFragment::TextFragment):
     24        (WebCore::SimpleLineLayout::removeTrailingWhitespace):
     25        (WebCore::SimpleLineLayout::initializeNewLine):
     26        (WebCore::SimpleLineLayout::splitFragmentToFitLine):
     27        (WebCore::SimpleLineLayout::nextFragment):
     28        (WebCore::SimpleLineLayout::createLineRuns):
     29        (WebCore::SimpleLineLayout::closeLineEndingAndAdjustRuns):
     30        (WebCore::SimpleLineLayout::createTextRuns):
     31        (WebCore::SimpleLineLayout::create):
     32        (WebCore::SimpleLineLayout::skipWhitespace): Deleted.
     33        (WebCore::SimpleLineLayout::textWidth): Deleted.
     34        * rendering/SimpleLineLayout.h:
     35
    1362014-11-04  Jeremy Jones  <jeremyj@apple.com>
    237
  • trunk/Source/WebCore/rendering/SimpleLineLayout.cpp

    r175565 r175601  
    215215
    216216template <typename CharacterType>
    217 static inline unsigned skipWhitespace(const CharacterType* text, unsigned offset, unsigned length, bool preserveNewline, unsigned& spaceCount)
    218 {
    219     spaceCount = 0;
    220     for (; offset < length; ++offset) {
    221         bool isSpace = text[offset] == ' ';
    222         if (!(isSpace || text[offset] == '\t' || (!preserveNewline && text[offset] == '\n')))
    223             return offset;
    224         if (isSpace)
    225             ++spaceCount;
    226     }
    227     return length;
    228 }
    229 
    230 template <typename CharacterType>
    231 static float textWidth(const RenderText& renderText, const CharacterType* text, unsigned textLength, unsigned from, unsigned to, float xPosition, const Style& style)
    232 {
    233     if (style.font.isFixedPitch() || (!from && to == textLength))
    234         return renderText.width(from, to - from, style.font, xPosition, nullptr, nullptr);
    235 
    236     TextRun run(text + from, to - from);
    237     run.setXPos(xPosition);
    238     run.setCharactersLength(textLength - from);
    239     run.setTabSize(!!style.tabWidth, style.tabWidth);
    240 
    241     ASSERT(run.charactersLength() >= run.length());
    242 
    243     return style.font.width(run);
    244 }
     217class FlowContentIterator {
     218public:
     219    FlowContentIterator(const RenderBlockFlow& flow)
     220        : m_flow(flow)
     221        , m_style(flow.style())
     222        , m_lineBreakIterator(downcast<RenderText>(*flow.firstChild()).text(), flow.style().locale())
     223    {
     224    }
     225
     226    unsigned findNextBreakablePosition(unsigned position)
     227    {
     228        String string = m_lineBreakIterator.string();
     229        return nextBreakablePosition<CharacterType, false>(m_lineBreakIterator, string.characters<CharacterType>(), string.length(), position);
     230    }
     231
     232    unsigned findNextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
     233    {
     234        String string = m_lineBreakIterator.string();
     235        unsigned length = string.length();
     236        const CharacterType* text = string.characters<CharacterType>();
     237        spaceCount = 0;
     238        for (; position < length; ++position) {
     239            bool isSpace = text[position] == ' ';
     240            if (!(isSpace || text[position] == '\t' || (!m_style.preserveNewline && text[position] == '\n')))
     241                return position;
     242            if (isSpace)
     243                ++spaceCount;
     244        }
     245        return length;
     246    }
     247
     248    float textWidth(unsigned from, unsigned to, float xPosition) const
     249    {
     250        String string = m_lineBreakIterator.string();
     251        unsigned length = string.length();
     252        if (m_style.font.isFixedPitch() || (!from && to == length)) {
     253            const RenderText& renderer = downcast<RenderText>(*m_flow.firstChild());
     254            return renderer.width(from, to - from, m_style.font, xPosition, nullptr, nullptr);
     255        }
     256
     257        TextRun run(string.characters<CharacterType>() + from, to - from);
     258        run.setXPos(xPosition);
     259        run.setCharactersLength(length - from);
     260        run.setTabSize(!!m_style.tabWidth, m_style.tabWidth);
     261        ASSERT(run.charactersLength() >= run.length());
     262        return m_style.font.width(run);
     263    }
     264
     265    bool isNewlineCharacter(unsigned position) const
     266    {
     267        ASSERT(m_lineBreakIterator.string().length() > position);
     268        return m_lineBreakIterator.string().at(position) == '\n';
     269    }
     270
     271    bool isEndOfContent(unsigned position) const
     272    {
     273        return position >= m_lineBreakIterator.string().length();
     274    }
     275
     276    const Style& style() const { return m_style; }
     277
     278private:
     279    const RenderBlockFlow& m_flow;
     280    Style m_style;
     281    LazyLineBreakIterator m_lineBreakIterator;
     282};
     283
    245284
    246285static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset)
     
    261300        return left + std::max<float>(remainingWidth / 2, 0);
    262301    case JUSTIFY:
     302        ASSERT_NOT_REACHED();
    263303        break;
    264304    }
     
    276316        , mustBreak(false)
    277317        , width(0)
    278     { }
     318    {
     319    }
    279320
    280321    TextFragment(unsigned textStart, unsigned textEnd, float textWidth, bool isWhitespaceOnly)
     
    286327        , mustBreak(false)
    287328        , width(textWidth)
    288     { }
     329    {
     330    }
    289331
    290332    bool isEmpty() const
     
    398440
    399441template <typename CharacterType>
    400 static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& lineRuns, const Style& style, const CharacterType* text, unsigned textLength)
    401 {
     442void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& lineRuns, const FlowContentIterator<CharacterType>& contentIterator)
     443{
     444    const auto& style = contentIterator.style();
    402445    bool preWrap = style.wrapLines && !style.collapseWhitespace;
    403446    // Trailing whitespace gets removed when we either collapse whitespace or pre-wrap is present.
     
    435478
    436479    // If we skipped any whitespace and now the line end is a "preserved" newline, skip the newline too as we are wrapping the line here already.
    437     if (lastPosition != lineState.position && style.preserveNewline && lineState.position < textLength && text[lineState.position] == '\n')
     480    if (lastPosition != lineState.position && style.preserveNewline && contentIterator.isNewlineCharacter(lineState.position))
    438481        ++lineState.position;
    439482}
    440483
    441484template <typename CharacterType>
    442 static void initializeNewLine(LineState& lineState, const Style& style, const CharacterType* text, unsigned textLength, unsigned lineStartRunIndex)
     485void initializeNewLine(LineState& lineState, const FlowContentIterator<CharacterType>& contentIterator, unsigned lineStartRunIndex)
    443486{
    444487    lineState.lineStartRunIndex = lineStartRunIndex;
     
    452495    } else {
    453496        unsigned spaceCount = 0;
    454         lineState.jumpTo(style.collapseWhitespace ? skipWhitespace(text, lineState.position, textLength, style.preserveNewline, spaceCount) : lineState.position, 0);
     497        lineState.jumpTo(contentIterator.style().collapseWhitespace ? contentIterator.findNextNonWhitespacePosition(lineState.position, spaceCount) : lineState.position, 0);
    455498    }
    456499    lineState.oveflowedFragment = TextFragment();
     
    458501
    459502template <typename CharacterType>
    460 static TextFragment splitFragmentToFitLine(TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const RenderText& textRenderer,
    461     const CharacterType* text, unsigned textLength, const Style& style)
     503TextFragment splitFragmentToFitLine(TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const FlowContentIterator<CharacterType>& contentIterator)
    462504{
    463505    // Fast path for single char fragments.
     
    478520    while (left < right) {
    479521        unsigned middle = (left + right) / 2;
    480         width = textWidth(textRenderer, text, textLength, fragmentToSplit.start, middle + 1, 0, style);
     522        width = contentIterator.textWidth(fragmentToSplit.start, middle + 1, 0);
    481523        if (availableWidth > width)
    482524            left = middle + 1;
     
    493535    TextFragment fragmentForNextLine(fragmentToSplit);
    494536    fragmentToSplit.end = right;
    495     fragmentToSplit.width = fragmentToSplit.isEmpty() ? 0 : textWidth(textRenderer, text, textLength, fragmentToSplit.start, right, 0, style);
     537    fragmentToSplit.width = fragmentToSplit.isEmpty() ? 0 : contentIterator.textWidth(fragmentToSplit.start, right, 0);
    496538
    497539    fragmentForNextLine.start = fragmentToSplit.end;
     
    501543
    502544template <typename CharacterType>
    503 static TextFragment nextFragment(unsigned previousFragmentEnd, LazyLineBreakIterator& lineBreakIterator, const Style& style, const CharacterType* text, unsigned textLength,
    504     float xPosition, const RenderText& textRenderer)
     545TextFragment nextFragment(unsigned previousFragmentEnd, FlowContentIterator<CharacterType>& contentIterator, float xPosition)
    505546{
    506547    // A fragment can have
     
    508549    // 2. whitespace (collasped, non-collapsed multi or single) or
    509550    // 3. non-whitespace characters.
     551    const auto& style = contentIterator.style();
    510552    TextFragment fragment;
    511     fragment.mustBreak = style.preserveNewline && text[previousFragmentEnd] == '\n';
     553    fragment.mustBreak = style.preserveNewline && contentIterator.isNewlineCharacter(previousFragmentEnd);
    512554    unsigned spaceCount = 0;
    513555    unsigned whitespaceEnd = previousFragmentEnd;
    514556    if (!fragment.mustBreak)
    515         whitespaceEnd = skipWhitespace(text, previousFragmentEnd, textLength, style.preserveNewline, spaceCount);
     557        whitespaceEnd = contentIterator.findNextNonWhitespacePosition(previousFragmentEnd, spaceCount);
    516558    fragment.isWhitespaceOnly = previousFragmentEnd < whitespaceEnd;
    517559    fragment.start = previousFragmentEnd;
     
    521563        fragment.end = fragment.start + 1;
    522564    else
    523         fragment.end = nextBreakablePosition<CharacterType, false>(lineBreakIterator, text, textLength, previousFragmentEnd + 1);
     565        fragment.end = contentIterator.findNextBreakablePosition(previousFragmentEnd + 1);
    524566    bool multiple = fragment.start + 1 < fragment.end;
    525567    fragment.isCollapsedWhitespace = multiple && fragment.isWhitespaceOnly && style.collapseWhitespace;
     
    536578        fragment.width = style.spaceWidth * spaceCount;
    537579    else
    538         fragment.width = textWidth(textRenderer, text, textLength, fragment.start, fragment.end, xPosition, style);
     580        fragment.width = contentIterator.textWidth(fragment.start, fragment.end, xPosition);
    539581    return fragment;
    540582}
    541583
    542584template <typename CharacterType>
    543 bool createLineRuns(LineState& lineState, Layout::RunVector& lineRuns, LazyLineBreakIterator& lineBreakIterator, const Style& style, const CharacterType* text,
    544     unsigned textLength, const RenderText& textRenderer)
    545 {
     585bool createLineRuns(LineState& lineState, Layout::RunVector& lineRuns, FlowContentIterator<CharacterType>& contentIterator)
     586{
     587    const auto& style = contentIterator.style();
    546588    bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow;
    547     while (lineState.position < textLength) {
     589    while (!contentIterator.isEndOfContent(lineState.position)) {
    548590        // Find the next text fragment. Start from the end of the previous fragment -current line end.
    549         TextFragment fragment = nextFragment(lineState.position, lineBreakIterator, style, text, textLength, lineState.width(), textRenderer);
     591        TextFragment fragment = nextFragment(lineState.position, contentIterator, lineState.width());
    550592        if ((lineCanBeWrapped && !lineState.fits(fragment.width)) || fragment.mustBreak) {
    551593            // Overflow wrapping behaviour:
     
    569611                // Whitespace collapse is off or non-whitespace content. split the fragment; (modified)fragment -> this lineState, oveflowedFragment -> next line.
    570612                // When this is the only (first) fragment, the first character stays on the line, even if it does not fit.
    571                 lineState.oveflowedFragment = splitFragmentToFitLine(fragment, lineState.availableWidth - lineState.width(), isFirstFragment, textRenderer, text, textLength, style);
     613                lineState.oveflowedFragment = splitFragmentToFitLine(fragment, lineState.availableWidth - lineState.width(), isFirstFragment, contentIterator);
    572614                if (!fragment.isEmpty()) {
    573615                    // Whitespace fragments can get pushed entirely to the next line.
     
    594636    }
    595637    lineState.commitAndCreateRun(lineRuns);
    596     return lineState.position >= textLength && lineState.oveflowedFragment.isEmpty();
    597 }
    598 
    599 template <typename CharacterType>
    600 static void closeLineEndingAndAdjustRuns(LineState& lineState, Layout::RunVector& lineRuns, unsigned& lineCount,
    601     const Style& style, const CharacterType* text, unsigned textLength)
     638    return contentIterator.isEndOfContent(lineState.position) && lineState.oveflowedFragment.isEmpty();
     639}
     640
     641template <typename CharacterType>
     642void closeLineEndingAndAdjustRuns(LineState& lineState, Layout::RunVector& lineRuns, unsigned& lineCount, const FlowContentIterator<CharacterType>& contentIterator)
    602643{
    603644    if (lineState.lineStartRunIndex == lineRuns.size())
     
    605646
    606647    ASSERT(lineRuns.size());
    607     removeTrailingWhitespace(lineState, lineRuns, style, text, textLength);
     648    removeTrailingWhitespace(lineState, lineRuns, contentIterator);
    608649    // Adjust runs' position by taking line's alignment into account.
    609     if (float lineLogicalLeft = computeLineLeft(style.textAlign, lineState.availableWidth, lineState.committedWidth, lineState.logicalLeftOffset)) {
     650    if (float lineLogicalLeft = computeLineLeft(contentIterator.style().textAlign, lineState.availableWidth, lineState.committedWidth, lineState.logicalLeftOffset)) {
    610651        for (unsigned i = lineState.lineStartRunIndex; i < lineRuns.size(); ++i) {
    611652            lineRuns[i].logicalLeft += lineLogicalLeft;
     
    629670
    630671template <typename CharacterType>
    631 void createTextRuns(Layout::RunVector& runs, unsigned& lineCount, RenderBlockFlow& flow, RenderText& textRenderer)
    632 {
    633     const Style style(flow.style());
    634     const CharacterType* text = textRenderer.text()->characters<CharacterType>();
    635     const unsigned textLength = textRenderer.textLength();
     672void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
     673{
    636674    LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
    637675    LayoutUnit lineHeight = lineHeightFromFlow(flow);
    638     LazyLineBreakIterator lineBreakIterator(textRenderer.text(), flow.style().locale());
    639676    LineState lineState;
    640677    bool isEndOfContent = false;
     678    FlowContentIterator<CharacterType> contentIterator = FlowContentIterator<CharacterType>(flow);
    641679
    642680    do {
    643681        flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
    644682        updateLineConstrains(flow, lineState.availableWidth, lineState.logicalLeftOffset);
    645         initializeNewLine(lineState, style, text, textLength, runs.size());
    646         isEndOfContent = createLineRuns(lineState, runs, lineBreakIterator, style, text, textLength, textRenderer);
    647         closeLineEndingAndAdjustRuns(lineState, runs, lineCount, style, text, textLength);
     683        initializeNewLine(lineState, contentIterator, runs.size());
     684        isEndOfContent = createLineRuns(lineState, runs, contentIterator);
     685        closeLineEndingAndAdjustRuns(lineState, runs, lineCount, contentIterator);
    648686    } while (!isEndOfContent);
    649687    ASSERT(!lineState.uncommittedWidth);
     
    652690std::unique_ptr<Layout> create(RenderBlockFlow& flow)
    653691{
     692    unsigned lineCount = 0;
    654693    Layout::RunVector runs;
    655     unsigned lineCount = 0;
    656 
    657694    RenderText& textRenderer = downcast<RenderText>(*flow.firstChild());
    658695    ASSERT(!textRenderer.firstTextBox());
    659696
    660697    if (textRenderer.is8Bit())
    661         createTextRuns<LChar>(runs, lineCount, flow, textRenderer);
     698        createTextRuns<LChar>(runs, flow, lineCount);
    662699    else
    663         createTextRuns<UChar>(runs, lineCount, flow, textRenderer);
     700        createTextRuns<UChar>(runs, flow, lineCount);
    664701
    665702    textRenderer.clearNeedsLayout();
  • trunk/Source/WebCore/rendering/SimpleLineLayout.h

    r175574 r175601  
    4444
    4545struct Run {
     46#if COMPILER(MSVC)
    4647    Run() { }
     48#endif
    4749    Run(unsigned start, unsigned end, float logicalLeft, float logicalRight, bool isEndOfLine)
    4850        : start(start)
Note: See TracChangeset for help on using the changeset viewer.