Changeset 159194 in webkit


Ignore:
Timestamp:
Nov 13, 2013 7:34:58 AM (10 years ago)
Author:
Antti Koivisto
Message:

Factor simple line creation loop to function
https://bugs.webkit.org/show_bug.cgi?id=124279

Reviewed by Andreas Kling.

  • rendering/SimpleLineLayout.cpp:

(WebCore::SimpleLineLayout::Style::Style):

Capture style that affects line layout to a struct.

(WebCore::SimpleLineLayout::textWidth):
(WebCore::SimpleLineLayout::measureWord):
(WebCore::SimpleLineLayout::createLineRuns):

Factor the line loop here.

(WebCore::SimpleLineLayout::createTextRuns):

Location:
trunk/Source/WebCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r159192 r159194  
     12013-11-13  Antti Koivisto  <antti@apple.com>
     2
     3        Factor simple line creation loop to function
     4        https://bugs.webkit.org/show_bug.cgi?id=124279
     5
     6        Reviewed by Andreas Kling.
     7
     8        * rendering/SimpleLineLayout.cpp:
     9        (WebCore::SimpleLineLayout::Style::Style):
     10       
     11            Capture style that affects line layout to a struct.
     12
     13        (WebCore::SimpleLineLayout::textWidth):
     14        (WebCore::SimpleLineLayout::measureWord):
     15        (WebCore::SimpleLineLayout::createLineRuns):
     16       
     17            Factor the line loop here.
     18
     19        (WebCore::SimpleLineLayout::createTextRuns):
     20
    1212013-11-12  Antti Koivisto  <antti@apple.com>
    222
  • trunk/Source/WebCore/rendering/SimpleLineLayout.cpp

    r159192 r159194  
    204204}
    205205
     206struct Style {
     207    Style(const RenderStyle& style)
     208        : font(style.font())
     209        , textAlign(style.textAlign())
     210        , collapseWhitespace(style.collapseWhiteSpace())
     211        , preserveNewline(style.preserveNewline())
     212        , wrapLines(style.autoWrap())
     213        , breakWordOnOverflow(style.overflowWrap() == BreakOverflowWrap && (wrapLines || preserveNewline))
     214        , spaceWidth(font.width(TextRun(&space, 1)))
     215        , tabWidth(collapseWhitespace ? 0 : style.tabSize())
     216    {
     217    }
     218    const Font& font;
     219    ETextAlign textAlign;
     220    bool collapseWhitespace;
     221    bool preserveNewline;
     222    bool wrapLines;
     223    bool breakWordOnOverflow;
     224    float spaceWidth;
     225    unsigned tabWidth;
     226};
     227
    206228static inline bool isWhitespace(UChar character, bool preserveNewline)
    207229{
     
    220242
    221243template <typename CharacterType>
    222 static float textWidth(const RenderText& renderText, const CharacterType* text, unsigned textLength, unsigned from, unsigned to, float xPosition, const Font& font, float tabWidth)
    223 {
    224     if (font.isFixedPitch() || (!from && to == textLength))
    225         return renderText.width(from, to - from, font, xPosition, nullptr, nullptr);
     244static float textWidth(const RenderText& renderText, const CharacterType* text, unsigned textLength, unsigned from, unsigned to, float xPosition, const Style& style)
     245{
     246    if (style.font.isFixedPitch() || (!from && to == textLength))
     247        return renderText.width(from, to - from, style.font, xPosition, nullptr, nullptr);
    226248
    227249    TextRun run(text + from, to - from);
    228250    run.setXPos(xPosition);
    229251    run.setCharactersLength(textLength - from);
    230     run.setTabSize(!!tabWidth, tabWidth);
     252    run.setTabSize(!!style.tabWidth, style.tabWidth);
    231253
    232254    ASSERT(run.charactersLength() >= run.length());
    233255
    234     return font.width(run);
    235 }
    236 
    237 template <typename CharacterType>
    238 static float measureWord(const RenderText& textRenderer, const CharacterType* text, unsigned textLength, unsigned start, unsigned end, float lineWidth, bool collapseWhitespace, const Font& font, float tabWidth, float spaceWidth)
     256    return style.font.width(run);
     257}
     258
     259template <typename CharacterType>
     260static float measureWord(unsigned start, unsigned end, float lineWidth, const Style& style, const CharacterType* text, unsigned textLength, const RenderText& textRenderer)
    239261{
    240262    if (text[start] == ' ' && end == start + 1)
    241         return spaceWidth;
    242 
    243     bool measureWithEndSpace = collapseWhitespace && end < textLength && text[end] == ' ';
     263        return style.spaceWidth;
     264
     265    bool measureWithEndSpace = style.collapseWhitespace && end < textLength && text[end] == ' ';
    244266    if (measureWithEndSpace)
    245267        ++end;
    246     float width = textWidth(textRenderer, text, textLength, start, end, lineWidth, font, collapseWhitespace ? 0 : tabWidth);
    247 
    248     return measureWithEndSpace ? width - spaceWidth : width;
     268    float width = textWidth(textRenderer, text, textLength, start, end, lineWidth, style);
     269
     270    return measureWithEndSpace ? width - style.spaceWidth : width;
     271}
     272
     273template <typename CharacterType>
     274Vector<Run, 4> createLineRuns(unsigned lineStart, LineWidth& lineWidth, LazyLineBreakIterator& lineBreakIterator, const Style& style, const CharacterType* text, unsigned textLength, const RenderText& textRenderer)
     275{
     276    Vector<Run, 4> lineRuns;
     277    lineRuns.uncheckedAppend(Run(lineStart, 0));
     278
     279    unsigned wordEnd = lineStart;
     280    while (wordEnd < textLength) {
     281        ASSERT(!style.collapseWhitespace || !isWhitespace(text[wordEnd], style.preserveNewline));
     282
     283        unsigned wordStart = wordEnd;
     284
     285        if (style.preserveNewline && text[wordStart] == '\n') {
     286            ++wordEnd;
     287            // FIXME: This creates a dedicated run for newline. This is wasteful and unnecessary but it keeps test results unchanged.
     288            if (wordStart > lineStart)
     289                lineRuns.append(Run(wordStart, lineRuns.last().right));
     290            lineRuns.last().right = lineRuns.last().left;
     291            lineRuns.last().end = wordEnd;
     292            break;
     293        }
     294
     295        if (!style.collapseWhitespace && isWhitespace(text[wordStart], style.preserveNewline))
     296            wordEnd = wordStart + 1;
     297        else
     298            wordEnd = nextBreakablePosition<CharacterType, false>(lineBreakIterator, text, textLength, wordStart + 1);
     299
     300        bool wordIsPrecededByWhitespace = style.collapseWhitespace && wordStart > lineStart && isWhitespace(text[wordStart - 1], style.preserveNewline);
     301        if (wordIsPrecededByWhitespace)
     302            --wordStart;
     303
     304        float wordWidth = measureWord(wordStart, wordEnd, lineWidth.committedWidth(), style, text, textLength, textRenderer);
     305
     306        lineWidth.addUncommittedWidth(wordWidth);
     307
     308        if (style.wrapLines) {
     309            // Move to the next line if the current one is full and we have something on it.
     310            if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
     311                break;
     312
     313            // This is for white-space: pre-wrap which requires special handling for end line whitespace.
     314            if (!style.collapseWhitespace && lineWidth.fitsOnLine() && wordEnd < textLength && isWhitespace(text[wordEnd], style.preserveNewline)) {
     315                // Look ahead to see if the next whitespace would fit.
     316                float whitespaceWidth = textWidth(textRenderer, text, textLength, wordEnd, wordEnd + 1, lineWidth.committedWidth(), style);
     317                if (!lineWidth.fitsOnLineIncludingExtraWidth(whitespaceWidth)) {
     318                    // If not eat away the rest of the whitespace on the line.
     319                    unsigned whitespaceEnd = skipWhitespaces(text, wordEnd, textLength, style.preserveNewline);
     320                    // Include newline to this run too.
     321                    if (whitespaceEnd < textLength && text[whitespaceEnd] == '\n')
     322                        ++whitespaceEnd;
     323                    lineRuns.last().end = whitespaceEnd;
     324                    lineRuns.last().right = lineWidth.availableWidth();
     325                    break;
     326                }
     327            }
     328        }
     329
     330        if (wordStart > lineRuns.last().end) {
     331            // There were more than one consecutive whitespace.
     332            ASSERT(wordIsPrecededByWhitespace);
     333            // Include space to the end of the previous run.
     334            lineRuns.last().end++;
     335            lineRuns.last().right += style.spaceWidth;
     336            // Start a new run on the same line.
     337            lineRuns.append(Run(wordStart + 1, lineRuns.last().right));
     338        }
     339
     340        if (!lineWidth.fitsOnLine() && style.breakWordOnOverflow) {
     341            // Backtrack and start measuring character-by-character.
     342            lineWidth.addUncommittedWidth(-lineWidth.uncommittedWidth());
     343            unsigned splitEnd = wordStart;
     344            for (; splitEnd < wordEnd; ++splitEnd) {
     345                float charWidth = textWidth(textRenderer, text, textLength, splitEnd, splitEnd + 1, 0, style);
     346                lineWidth.addUncommittedWidth(charWidth);
     347                if (!lineWidth.fitsOnLine() && splitEnd > lineStart)
     348                    break;
     349                lineWidth.commit();
     350            }
     351            lineRuns.last().end = splitEnd;
     352            lineRuns.last().right = lineWidth.committedWidth();
     353            // To match line boxes, set single-space-only line width to zero.
     354            if (text[lineRuns.last().start] == ' ' && lineRuns.last().start + 1 == lineRuns.last().end)
     355                lineRuns.last().right = lineRuns.last().left;
     356            break;
     357        }
     358
     359        lineWidth.commit();
     360
     361        lineRuns.last().right = lineWidth.committedWidth();
     362        lineRuns.last().end = wordEnd;
     363
     364        if (style.collapseWhitespace)
     365            wordEnd = skipWhitespaces(text, wordEnd, textLength, style.preserveNewline);
     366
     367        if (!lineWidth.fitsOnLine() && style.wrapLines) {
     368            // The first run on the line overflows.
     369            ASSERT(lineRuns.size() == 1);
     370            break;
     371        }
     372    }
     373    return lineRuns;
    249374}
    250375
     
    283408void createTextRuns(Layout::RunVector& runs, unsigned& lineCount, RenderBlockFlow& flow, RenderText& textRenderer)
    284409{
    285     const RenderStyle& style = flow.style();
    286 
    287     // These properties are supported.
    288     const Font& font = style.font();
    289     unsigned tabWidth = style.tabSize();
    290     ETextAlign textAlign = style.textAlign(); // Not 'justify'.
    291     bool collapseWhitespace = style.collapseWhiteSpace();
    292     bool preserveNewline = style.preserveNewline();
    293     bool wrapLines = style.autoWrap();
    294     bool breakWordOnOverflow = style.overflowWrap() == BreakOverflowWrap && (wrapLines || preserveNewline);
     410    const Style style(flow.style());
    295411
    296412    const CharacterType* text = textRenderer.text()->getCharacters<CharacterType>();
    297413    const unsigned textLength = textRenderer.textLength();
    298414
    299     float spaceWidth = font.width(TextRun(&space, 1));
    300     LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
     415    LazyLineBreakIterator lineBreakIterator(textRenderer.text(), flow.style().locale());
    301416
    302417    unsigned lineEnd = 0;
    303418    while (lineEnd < textLength) {
    304         if (collapseWhitespace)
    305             lineEnd = skipWhitespaces(text, lineEnd, textLength, preserveNewline);
     419        if (style.collapseWhitespace)
     420            lineEnd = skipWhitespaces(text, lineEnd, textLength, style.preserveNewline);
     421
    306422        unsigned lineStart = lineEnd;
    307         unsigned wordEnd = lineEnd;
     423
    308424        LineWidth lineWidth(flow, false, DoNotIndentText);
    309 
    310         Vector<Run, 4> lineRuns;
    311         lineRuns.uncheckedAppend(Run(lineStart, 0));
    312 
    313         while (wordEnd < textLength) {
    314             ASSERT(!collapseWhitespace || !isWhitespace(text[wordEnd], preserveNewline));
    315 
    316             unsigned wordStart = wordEnd;
    317 
    318             if (preserveNewline && text[wordStart] == '\n') {
    319                 ++wordEnd;
    320                 // FIXME: This creates a dedicated run for newline. This is wasteful and unnecessary but it keeps test results unchanged.
    321                 if (wordStart > lineStart)
    322                     lineRuns.append(Run(lineEnd, lineRuns.last().right));
    323                 lineRuns.last().right = lineRuns.last().left;
    324                 lineRuns.last().end = lineEnd + 1;
    325                 lineEnd = wordEnd;
    326                 break;
    327             }
    328 
    329             if (!collapseWhitespace && isWhitespace(text[wordStart], preserveNewline))
    330                 wordEnd = wordStart + 1;
    331             else
    332                 wordEnd = nextBreakablePosition<CharacterType, false>(lineBreakIterator, text, textLength, wordStart + 1);
    333 
    334             bool wordIsPrecededByWhitespace = collapseWhitespace && wordStart > lineStart && isWhitespace(text[wordStart - 1], preserveNewline);
    335             if (wordIsPrecededByWhitespace)
    336                 --wordStart;
    337 
    338             float wordWidth = measureWord(textRenderer, text, textLength, wordStart, wordEnd, lineWidth.committedWidth(), collapseWhitespace, font, tabWidth, spaceWidth);
    339 
    340             lineWidth.addUncommittedWidth(wordWidth);
    341 
    342             if (wrapLines) {
    343                 // Move to the next line if the current one is full and we have something on it.
    344                 if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
    345                     break;
    346 
    347                 // This is for white-space: pre-wrap which requires special handling for end line whitespace.
    348                 if (!collapseWhitespace && lineWidth.fitsOnLine() && wordEnd < textLength && isWhitespace(text[wordEnd], preserveNewline)) {
    349                     // Look ahead to see if the next whitespace would fit.
    350                     float whitespaceWidth = textWidth(textRenderer, text, textLength, wordEnd, wordEnd + 1, lineWidth.committedWidth(), font, tabWidth);
    351                     if (!lineWidth.fitsOnLineIncludingExtraWidth(whitespaceWidth)) {
    352                         // If not eat away the rest of the whitespace on the line.
    353                         unsigned whitespaceEnd = skipWhitespaces(text, wordEnd, textLength, preserveNewline);
    354                         // Include newline to this run too.
    355                         if (whitespaceEnd < textLength && text[whitespaceEnd] == '\n')
    356                             ++whitespaceEnd;
    357                         lineRuns.last().end = whitespaceEnd;
    358                         lineRuns.last().right = lineWidth.availableWidth();
    359                         lineEnd = whitespaceEnd;
    360                         break;
    361                     }
    362                 }
    363             }
    364 
    365             if (wordStart > lineEnd) {
    366                 // There were more than one consecutive whitespace.
    367                 ASSERT(wordIsPrecededByWhitespace);
    368                 // Include space to the end of the previous run.
    369                 lineRuns.last().end++;
    370                 lineRuns.last().right += spaceWidth;
    371                 // Start a new run on the same line.
    372                 lineRuns.append(Run(wordStart + 1, lineRuns.last().right));
    373             }
    374 
    375             if (!lineWidth.fitsOnLine() && breakWordOnOverflow) {
    376                 // Backtrack and start measuring character-by-character.
    377                 lineWidth.addUncommittedWidth(-lineWidth.uncommittedWidth());
    378                 unsigned splitEnd = wordStart;
    379                 for (; splitEnd < wordEnd; ++splitEnd) {
    380                     float charWidth = textWidth(textRenderer, text, textLength, splitEnd, splitEnd + 1, 0, font, tabWidth);
    381                     lineWidth.addUncommittedWidth(charWidth);
    382                     if (!lineWidth.fitsOnLine() && splitEnd > lineStart)
    383                         break;
    384                     lineWidth.commit();
    385                 }
    386                 lineRuns.last().end = splitEnd;
    387                 lineRuns.last().right = lineWidth.committedWidth();
    388                 lineEnd = splitEnd;
    389                 // To match line boxes, set single-space-only line width to zero.
    390                 if (text[lineRuns.last().start] == ' ' && lineRuns.last().start + 1 == lineRuns.last().end)
    391                     lineRuns.last().right = lineRuns.last().left;
    392                 break;
    393             }
    394 
    395             lineWidth.commit();
    396 
    397             lineRuns.last().right = lineWidth.committedWidth();
    398             lineRuns.last().end = wordEnd;
    399 
    400             lineEnd = wordEnd;
    401             if (collapseWhitespace)
    402                 wordEnd = skipWhitespaces(text, wordEnd, textLength, preserveNewline);
    403 
    404             if (!lineWidth.fitsOnLine() && wrapLines) {
    405                 // The first run on the line overflows.
    406                 ASSERT(lineRuns.size() == 1);
    407                 break;
    408             }
    409         }
     425        auto lineRuns = createLineRuns(lineStart, lineWidth, lineBreakIterator, style, text, textLength, textRenderer);
     426
     427        lineEnd = lineRuns.last().end;
    410428        if (lineStart == lineEnd)
    411429            continue;
     
    413431        lineRuns.last().isEndOfLine = true;
    414432
    415         float lineLeft = computeLineLeft(textAlign, lineWidth.availableWidth() - lineWidth.committedWidth());
     433        float lineLeft = computeLineLeft(style.textAlign, lineWidth.availableWidth() - lineWidth.committedWidth());
    416434        adjustRunOffsets(lineRuns, lineLeft);
    417435
Note: See TracChangeset for help on using the changeset viewer.