Changeset 175601 in webkit
- Timestamp:
- Nov 4, 2014 7:47:45 PM (9 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r175600 r175601 1 2014-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 1 36 2014-11-04 Jeremy Jones <jeremyj@apple.com> 2 37 -
trunk/Source/WebCore/rendering/SimpleLineLayout.cpp
r175565 r175601 215 215 216 216 template <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 } 217 class FlowContentIterator { 218 public: 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 278 private: 279 const RenderBlockFlow& m_flow; 280 Style m_style; 281 LazyLineBreakIterator m_lineBreakIterator; 282 }; 283 245 284 246 285 static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset) … … 261 300 return left + std::max<float>(remainingWidth / 2, 0); 262 301 case JUSTIFY: 302 ASSERT_NOT_REACHED(); 263 303 break; 264 304 } … … 276 316 , mustBreak(false) 277 317 , width(0) 278 { } 318 { 319 } 279 320 280 321 TextFragment(unsigned textStart, unsigned textEnd, float textWidth, bool isWhitespaceOnly) … … 286 327 , mustBreak(false) 287 328 , width(textWidth) 288 { } 329 { 330 } 289 331 290 332 bool isEmpty() const … … 398 440 399 441 template <typename CharacterType> 400 static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& lineRuns, const Style& style, const CharacterType* text, unsigned textLength) 401 { 442 void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& lineRuns, const FlowContentIterator<CharacterType>& contentIterator) 443 { 444 const auto& style = contentIterator.style(); 402 445 bool preWrap = style.wrapLines && !style.collapseWhitespace; 403 446 // Trailing whitespace gets removed when we either collapse whitespace or pre-wrap is present. … … 435 478 436 479 // 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)) 438 481 ++lineState.position; 439 482 } 440 483 441 484 template <typename CharacterType> 442 static void initializeNewLine(LineState& lineState, const Style& style, const CharacterType* text, unsigned textLength, unsigned lineStartRunIndex)485 void initializeNewLine(LineState& lineState, const FlowContentIterator<CharacterType>& contentIterator, unsigned lineStartRunIndex) 443 486 { 444 487 lineState.lineStartRunIndex = lineStartRunIndex; … … 452 495 } else { 453 496 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); 455 498 } 456 499 lineState.oveflowedFragment = TextFragment(); … … 458 501 459 502 template <typename CharacterType> 460 static TextFragment splitFragmentToFitLine(TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const RenderText& textRenderer, 461 const CharacterType* text, unsigned textLength, const Style& style) 503 TextFragment splitFragmentToFitLine(TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const FlowContentIterator<CharacterType>& contentIterator) 462 504 { 463 505 // Fast path for single char fragments. … … 478 520 while (left < right) { 479 521 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); 481 523 if (availableWidth > width) 482 524 left = middle + 1; … … 493 535 TextFragment fragmentForNextLine(fragmentToSplit); 494 536 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); 496 538 497 539 fragmentForNextLine.start = fragmentToSplit.end; … … 501 543 502 544 template <typename CharacterType> 503 static TextFragment nextFragment(unsigned previousFragmentEnd, LazyLineBreakIterator& lineBreakIterator, const Style& style, const CharacterType* text, unsigned textLength, 504 float xPosition, const RenderText& textRenderer) 545 TextFragment nextFragment(unsigned previousFragmentEnd, FlowContentIterator<CharacterType>& contentIterator, float xPosition) 505 546 { 506 547 // A fragment can have … … 508 549 // 2. whitespace (collasped, non-collapsed multi or single) or 509 550 // 3. non-whitespace characters. 551 const auto& style = contentIterator.style(); 510 552 TextFragment fragment; 511 fragment.mustBreak = style.preserveNewline && text[previousFragmentEnd] == '\n';553 fragment.mustBreak = style.preserveNewline && contentIterator.isNewlineCharacter(previousFragmentEnd); 512 554 unsigned spaceCount = 0; 513 555 unsigned whitespaceEnd = previousFragmentEnd; 514 556 if (!fragment.mustBreak) 515 whitespaceEnd = skipWhitespace(text, previousFragmentEnd, textLength, style.preserveNewline, spaceCount);557 whitespaceEnd = contentIterator.findNextNonWhitespacePosition(previousFragmentEnd, spaceCount); 516 558 fragment.isWhitespaceOnly = previousFragmentEnd < whitespaceEnd; 517 559 fragment.start = previousFragmentEnd; … … 521 563 fragment.end = fragment.start + 1; 522 564 else 523 fragment.end = nextBreakablePosition<CharacterType, false>(lineBreakIterator, text, textLength,previousFragmentEnd + 1);565 fragment.end = contentIterator.findNextBreakablePosition(previousFragmentEnd + 1); 524 566 bool multiple = fragment.start + 1 < fragment.end; 525 567 fragment.isCollapsedWhitespace = multiple && fragment.isWhitespaceOnly && style.collapseWhitespace; … … 536 578 fragment.width = style.spaceWidth * spaceCount; 537 579 else 538 fragment.width = textWidth(textRenderer, text, textLength, fragment.start, fragment.end, xPosition, style);580 fragment.width = contentIterator.textWidth(fragment.start, fragment.end, xPosition); 539 581 return fragment; 540 582 } 541 583 542 584 template <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 { 585 bool createLineRuns(LineState& lineState, Layout::RunVector& lineRuns, FlowContentIterator<CharacterType>& contentIterator) 586 { 587 const auto& style = contentIterator.style(); 546 588 bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow; 547 while ( lineState.position < textLength) {589 while (!contentIterator.isEndOfContent(lineState.position)) { 548 590 // 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()); 550 592 if ((lineCanBeWrapped && !lineState.fits(fragment.width)) || fragment.mustBreak) { 551 593 // Overflow wrapping behaviour: … … 569 611 // Whitespace collapse is off or non-whitespace content. split the fragment; (modified)fragment -> this lineState, oveflowedFragment -> next line. 570 612 // 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); 572 614 if (!fragment.isEmpty()) { 573 615 // Whitespace fragments can get pushed entirely to the next line. … … 594 636 } 595 637 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 641 template <typename CharacterType> 642 void closeLineEndingAndAdjustRuns(LineState& lineState, Layout::RunVector& lineRuns, unsigned& lineCount, const FlowContentIterator<CharacterType>& contentIterator) 602 643 { 603 644 if (lineState.lineStartRunIndex == lineRuns.size()) … … 605 646 606 647 ASSERT(lineRuns.size()); 607 removeTrailingWhitespace(lineState, lineRuns, style, text, textLength);648 removeTrailingWhitespace(lineState, lineRuns, contentIterator); 608 649 // 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)) { 610 651 for (unsigned i = lineState.lineStartRunIndex; i < lineRuns.size(); ++i) { 611 652 lineRuns[i].logicalLeft += lineLogicalLeft; … … 629 670 630 671 template <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(); 672 void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount) 673 { 636 674 LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore(); 637 675 LayoutUnit lineHeight = lineHeightFromFlow(flow); 638 LazyLineBreakIterator lineBreakIterator(textRenderer.text(), flow.style().locale());639 676 LineState lineState; 640 677 bool isEndOfContent = false; 678 FlowContentIterator<CharacterType> contentIterator = FlowContentIterator<CharacterType>(flow); 641 679 642 680 do { 643 681 flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore); 644 682 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); 648 686 } while (!isEndOfContent); 649 687 ASSERT(!lineState.uncommittedWidth); … … 652 690 std::unique_ptr<Layout> create(RenderBlockFlow& flow) 653 691 { 692 unsigned lineCount = 0; 654 693 Layout::RunVector runs; 655 unsigned lineCount = 0;656 657 694 RenderText& textRenderer = downcast<RenderText>(*flow.firstChild()); 658 695 ASSERT(!textRenderer.firstTextBox()); 659 696 660 697 if (textRenderer.is8Bit()) 661 createTextRuns<LChar>(runs, lineCount, flow, textRenderer);698 createTextRuns<LChar>(runs, flow, lineCount); 662 699 else 663 createTextRuns<UChar>(runs, lineCount, flow, textRenderer);700 createTextRuns<UChar>(runs, flow, lineCount); 664 701 665 702 textRenderer.clearNeedsLayout(); -
trunk/Source/WebCore/rendering/SimpleLineLayout.h
r175574 r175601 44 44 45 45 struct Run { 46 #if COMPILER(MSVC) 46 47 Run() { } 48 #endif 47 49 Run(unsigned start, unsigned end, float logicalLeft, float logicalRight, bool isEndOfLine) 48 50 : start(start)
Note: See TracChangeset
for help on using the changeset viewer.