Changeset 181667 in webkit
- Timestamp:
- Mar 17, 2015, 3:20:30 PM (11 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 7 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/fast/text/simple-line-with-multiple-renderers-expected.html (added)
-
LayoutTests/fast/text/simple-line-with-multiple-renderers.html (added)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/rendering/SimpleLineLayout.cpp (modified) (11 diffs)
-
Source/WebCore/rendering/SimpleLineLayoutFlowContents.h (modified) (4 diffs)
-
Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp (modified) (1 diff)
-
Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.cpp (modified) (3 diffs)
-
Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.h (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r181659 r181667 1 2015-03-17 Zalan Bujtas <zalan@apple.com> 2 3 Simple line layout: Split fragments on renderer boundary on the fly. 4 https://bugs.webkit.org/show_bug.cgi?id=142579 5 6 Reviewed by Antti Koivisto. 7 8 Fragment splitting at renderers' boundary at the end of the line is no longer needed. 9 This patch ensures that TextFragmentIterator::nextTextFragment() does not concatenate 10 fragments across renderer boundary. 11 12 * fast/text/simple-line-with-multiple-renderers-expected.html: Added. 13 * fast/text/simple-line-with-multiple-renderers.html: Added. 14 1 15 2015-03-17 Dean Jackson <dino@apple.com> 2 16 -
trunk/Source/WebCore/ChangeLog
r181666 r181667 1 2015-03-17 Zalan Bujtas <zalan@apple.com> 2 3 Simple line layout: Split fragments on renderer boundary on the fly. 4 https://bugs.webkit.org/show_bug.cgi?id=142579 5 6 Reviewed by Antti Koivisto. 7 8 Fragment splitting at renderers' boundary at the end of the line is no longer needed. 9 This patch ensures that TextFragmentIterator::nextTextFragment() does not concatenate 10 fragments across renderer boundary. 11 12 Test: fast/text/simple-line-with-multiple-renderers.html 13 14 * rendering/SimpleLineLayout.cpp: 15 (WebCore::SimpleLineLayout::revertRuns): 16 (WebCore::SimpleLineLayout::LineState::isEmpty): 17 (WebCore::SimpleLineLayout::LineState::appendFragmentAndCreateRunIfNeeded): Check if fragments need a new run and 18 whether neighboring fragments need collapsing. 19 (WebCore::SimpleLineLayout::LineState::revertUncommitted): Revert fragments over multiple renderers if they form 20 a fragment continuation. 21 (WebCore::SimpleLineLayout::LineState::removeTrailingWhitespace): 22 (WebCore::SimpleLineLayout::forceFragmentToLine): Ensure that if a fragment has continuation across multiple renderers, 23 they all get added to the current line. 24 (WebCore::SimpleLineLayout::createLineRuns): 25 (WebCore::SimpleLineLayout::LineState::appendFragment): Deleted. 26 (WebCore::SimpleLineLayout::FragmentForwardIterator::FragmentForwardIterator): Deleted. 27 (WebCore::SimpleLineLayout::FragmentForwardIterator::operator++): Deleted. 28 (WebCore::SimpleLineLayout::FragmentForwardIterator::operator!=): Deleted. 29 (WebCore::SimpleLineLayout::FragmentForwardIterator::operator*): Deleted. 30 (WebCore::SimpleLineLayout::begin): Deleted. 31 (WebCore::SimpleLineLayout::end): Deleted. 32 (WebCore::SimpleLineLayout::preWrap): Deleted. 33 (WebCore::SimpleLineLayout::removeTrailingWhitespace): Deleted. 34 (WebCore::SimpleLineLayout::updateLineConstrains): Deleted. 35 (WebCore::SimpleLineLayout::splitRunsAtRendererBoundary): Deleted. 36 (WebCore::SimpleLineLayout::createTextRuns): Deleted. 37 (WebCore::SimpleLineLayout::create): Deleted. 38 (WebCore::SimpleLineLayout::Layout::create): Deleted. 39 * rendering/SimpleLineLayoutFlowContents.h: 40 (WebCore::SimpleLineLayout::FlowContents::Iterator::operator+): 41 (WebCore::SimpleLineLayout::FlowContents::Iterator::operator*): 42 (WebCore::SimpleLineLayout::FlowContents::Iterator::operator->): 43 * rendering/SimpleLineLayoutFunctions.cpp: 44 (WebCore::SimpleLineLayout::paintFlow): Unrelated fix on non-collapsed tab position. 45 * rendering/SimpleLineLayoutTextFragmentIterator.cpp: 46 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragmentIterator): 47 (WebCore::SimpleLineLayout::TextFragmentIterator::nextTextFragment): 48 (WebCore::SimpleLineLayout::TextFragmentIterator::revertToFragment): 49 (WebCore::SimpleLineLayout::TextFragmentIterator::textWidth): 50 (WebCore::SimpleLineLayout::TextFragmentIterator::skipToNextPosition): 51 * rendering/SimpleLineLayoutTextFragmentIterator.h: 52 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::TextFragment): 53 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::overlapsToNextRenderer): 54 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::isCollapsible): 55 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::operator==): 56 (WebCore::SimpleLineLayout::TextFragmentIterator::isLineBreak): 57 (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::split): Deleted. 58 1 59 2015-03-17 Jeremy Jones <jeremyj@apple.com> 2 60 -
trunk/Source/WebCore/rendering/SimpleLineLayout.cpp
r180689 r181667 232 232 } 233 233 234 static void revertRuns(Layout::RunVector& runs, unsigned length, float width) 235 { 236 ASSERT(runs.size()); 237 while (length) { 238 Run& lastRun = runs.last(); 239 unsigned lastRunLength = lastRun.end - lastRun.start; 240 if (lastRunLength > length) { 241 lastRun.logicalRight -= width; 242 lastRun.end -= length; 243 break; 244 } 245 runs.removeLast(); 246 length -= lastRunLength; 247 width -= (lastRun.logicalRight - lastRun.logicalLeft); 248 } 249 } 250 234 251 class LineState { 235 252 public: 236 253 void setAvailableWidth(float width) { m_availableWidth = width; } 254 void setCollapedWhitespaceWidth(float width) { m_collapsedWhitespaceWidth = width; } 237 255 void setLogicalLeftOffset(float offset) { m_logicalLeftOffset = offset; } 238 256 void setOverflowedFragment(const TextFragmentIterator::TextFragment& fragment) { m_overflowedFragment = fragment; } … … 242 260 const TextFragmentIterator::TextFragment& overflowedFragment() const { return m_overflowedFragment; } 243 261 bool hasTrailingWhitespace() const { return m_trailingWhitespaceLength; } 262 TextFragmentIterator::TextFragment lastFragment() const { return m_fragments.last(); } 244 263 bool isWhitespaceOnly() const { return m_trailingWhitespaceWidth && m_runsWidth == m_trailingWhitespaceWidth; } 245 264 bool fits(float extra) const { return m_availableWidth >= m_runsWidth + extra; } 246 265 bool firstCharacterFits() const { return m_firstCharacterFits; } 247 266 float width() const { return m_runsWidth; } 248 249 void appendFragment(const TextFragmentIterator::TextFragment& fragment, Layout::RunVector& runs) 267 bool isEmpty() const 268 { 269 if (!m_fragments.size()) 270 return true; 271 if (!m_lastCompleteFragment.isEmpty()) 272 return false; 273 return m_fragments.last().overlapsToNextRenderer(); 274 } 275 276 void appendFragmentAndCreateRunIfNeeded(const TextFragmentIterator::TextFragment& fragment, Layout::RunVector& runs) 250 277 { 251 278 // Adjust end position while collapsing. 252 279 unsigned endPosition = fragment.isCollapsed() ? fragment.start() + 1 : fragment.end(); 253 254 if ( m_createNewRun)280 // New line needs new run. 281 if (!m_runsWidth) 255 282 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false)); 256 283 else { 257 ASSERT(runs.size()); 258 Run& lastRun = runs.last(); 259 lastRun.end = endPosition; 260 lastRun.logicalRight = m_runsWidth + fragment.width(); 261 } 262 m_createNewRun = fragment.isCollapsed(); 284 const auto& lastFragment = m_fragments.last(); 285 // Advance last completed fragment when the previous fragment is all set (including multiple parts across renderers) 286 if ((lastFragment.type() != fragment.type()) || !lastFragment.overlapsToNextRenderer()) 287 m_lastCompleteFragment = lastFragment; 288 // Collapse neighbouring whitespace, if they are across multiple renderers and are not collapsed yet. 289 if (lastFragment.isCollapsible() && fragment.isCollapsible()) { 290 ASSERT(lastFragment.isLastInRenderer()); 291 if (!lastFragment.isCollapsed()) { 292 // Line width needs to be reset so that now it takes collapsing into consideration. 293 m_runsWidth -= (lastFragment.width() - m_collapsedWhitespaceWidth); 294 } 295 // This fragment is collapsed completely. No run is needed. 296 return; 297 } 298 if (lastFragment.isLastInRenderer() || lastFragment.isCollapsed()) 299 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false)); 300 else { 301 Run& lastRun = runs.last(); 302 lastRun.end = endPosition; 303 lastRun.logicalRight += fragment.width(); 304 } 305 } 306 m_fragments.append(fragment); 263 307 m_runsWidth += fragment.width(); 264 308 … … 275 319 } 276 320 321 TextFragmentIterator::TextFragment revertToLastCompleteFragment(Layout::RunVector& runs) 322 { 323 ASSERT(m_fragments.size()); 324 unsigned revertLength = 0; 325 float revertWidth = 0; 326 while (m_fragments.size()) { 327 const auto& current = m_fragments.last(); 328 if (current == m_lastCompleteFragment) 329 break; 330 revertLength += current.end() - current.start(); 331 revertWidth += current.width(); 332 m_fragments.removeLast(); 333 } 334 m_runsWidth -= revertWidth; 335 if (revertLength) 336 revertRuns(runs, revertLength, revertWidth); 337 return m_lastCompleteFragment; 338 } 339 277 340 void removeTrailingWhitespace(Layout::RunVector& runs) 278 341 { 279 ASSERT(runs.size()); 280 Run& lastRun = runs.last(); 281 lastRun.logicalRight -= m_trailingWhitespaceWidth; 282 lastRun.end -= m_trailingWhitespaceLength; 283 if (lastRun.start == lastRun.end) 284 runs.removeLast(); 285 342 // FIXME: sync m_fragment, but it's ok for now as we don't use m_fragment anymore after removeTrailingWhitespace. 343 if (!m_trailingWhitespaceLength) 344 return; 345 revertRuns(runs, m_trailingWhitespaceLength, m_trailingWhitespaceWidth); 286 346 m_runsWidth -= m_trailingWhitespaceWidth; 347 m_trailingWhitespaceLength = 0; 287 348 m_trailingWhitespaceWidth = 0; 288 m_trailingWhitespaceLength = 0;289 349 } 290 350 … … 294 354 TextFragmentIterator::TextFragment m_overflowedFragment; 295 355 float m_runsWidth { 0 }; 296 bool m_createNewRun { true };356 TextFragmentIterator::TextFragment m_lastCompleteFragment; 297 357 float m_trailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text. 298 358 unsigned m_trailingWhitespaceLength { 0 }; 359 float m_collapsedWhitespaceWidth { 0 }; 299 360 // Having one character on the line does not necessarily mean it actually fits. 300 361 // First character of the first fragment might be forced on to the current line even if it does not fit. 301 362 bool m_firstCharacterFits { false }; 363 Vector<TextFragmentIterator::TextFragment> m_fragments; 302 364 }; 303 365 … … 390 452 391 453 // Check if we need to skip the leading whitespace. 392 if (style.collapseWhitespace && firstFragment.type() == TextFragmentIterator::TextFragment::Whitespace) 393 firstFragment = textFragmentIterator.nextTextFragment(); 454 if (style.collapseWhitespace) { 455 while (firstFragment.type() == TextFragmentIterator::TextFragment::Whitespace) 456 firstFragment = textFragmentIterator.nextTextFragment(); 457 } 394 458 return firstFragment; 395 459 } 396 460 461 static void forceFragmentToLine(LineState& line, TextFragmentIterator& textFragmentIterator, Layout::RunVector& runs, const TextFragmentIterator::TextFragment& fragment) 462 { 463 line.appendFragmentAndCreateRunIfNeeded(fragment, runs); 464 // Check if there are more fragments to add to the current line. 465 if (!fragment.overlapsToNextRenderer()) 466 return; 467 auto nextFragment = textFragmentIterator.nextTextFragment(); 468 while (true) { 469 if (nextFragment.type() != fragment.type()) 470 break; 471 line.appendFragmentAndCreateRunIfNeeded(nextFragment, runs); 472 // Does it overlap to the next segment? 473 if (!nextFragment.overlapsToNextRenderer()) 474 return; 475 nextFragment = textFragmentIterator.nextTextFragment(); 476 } 477 line.setOverflowedFragment(nextFragment); 478 } 479 397 480 static bool createLineRuns(LineState& line, const LineState& previousLine, Layout::RunVector& runs, TextFragmentIterator& textFragmentIterator) 398 481 { 399 482 const auto& style = textFragmentIterator.style(); 483 line.setCollapedWhitespaceWidth(style.spaceWidth); 400 484 bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow; 401 485 auto fragment = firstFragment(textFragmentIterator, previousLine); … … 405 489 // Add the new line fragment only if there's nothing on the line. (otherwise the extra new line character would show up at the end of the content.) 406 490 if (!line.width()) 407 line.appendFragment (fragment, runs);491 line.appendFragmentAndCreateRunIfNeeded(fragment, runs); 408 492 break; 409 493 } … … 414 498 // 3. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap) 415 499 // 4. Non-whitespace fragment when there's already another fragment on the line gets pushed to the next line. 416 bool emptyLine = !line.width();500 bool emptyLine = line.isEmpty(); 417 501 // Whitespace fragment. 418 502 if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) { … … 420 504 // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line. 421 505 line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator)); 422 line.appendFragment (fragment, runs);506 line.appendFragmentAndCreateRunIfNeeded(fragment, runs); 423 507 } 424 508 // When whitespace collapse is on, whitespace that doesn't fit is simply skipped. … … 429 513 // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line. 430 514 line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator)); 431 line.appendFragment (fragment, runs);515 line.appendFragmentAndCreateRunIfNeeded(fragment, runs); 432 516 break; 433 517 } 434 518 // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though. 519 ASSERT(fragment.type() == TextFragmentIterator::TextFragment::NonWhitespace); 435 520 if (emptyLine) { 436 line.appendFragment(fragment, runs);521 forceFragmentToLine(line, textFragmentIterator, runs, fragment); 437 522 break; 438 523 } 439 524 // Non-breakable non-whitespace fragment when there's already content on the line. Push it to the next line. 525 if (line.lastFragment().overlapsToNextRenderer()) { 526 // Check if this fragment is a continuation of a previous segment. In such cases, we need to remove them all. 527 const auto& currentFragment = line.revertToLastCompleteFragment(runs); 528 textFragmentIterator.revertToFragment(currentFragment); 529 break; 530 } 440 531 line.setOverflowedFragment(fragment); 441 532 break; 442 533 } 443 line.appendFragment (fragment, runs);534 line.appendFragmentAndCreateRunIfNeeded(fragment, runs); 444 535 // Find the next text fragment. 445 536 fragment = textFragmentIterator.nextTextFragment(line.width()); 446 537 } 447 return fragment.type() == TextFragmentIterator::TextFragment::ContentEnd && line.overflowedFragment().isEmpty();538 return (fragment.type() == TextFragmentIterator::TextFragment::ContentEnd && line.overflowedFragment().isEmpty()) || line.overflowedFragment().type() == TextFragmentIterator::TextFragment::ContentEnd; 448 539 } 449 540 … … 467 558 } 468 559 469 static void splitRunsAtRendererBoundary(Layout::RunVector& lineRuns, const TextFragmentIterator& textFragmentIterator)470 {471 // FIXME: We should probably split during run construction instead of as a separate pass.472 if (lineRuns.isEmpty())473 return;474 unsigned runIndex = 0;475 do {476 const Run& run = lineRuns.at(runIndex);477 ASSERT(run.start != run.end);478 auto& startSegment = textFragmentIterator.segmentForPosition(run.start);479 if (run.end <= startSegment.end)480 continue;481 // This run overlaps multiple renderers. Split it up.482 // Split run at the renderer's boundary and create a new run for the left side, while use the current run as the right side.483 float logicalRightOfLeftRun = run.logicalLeft + textFragmentIterator.textWidth(run.start, startSegment.end, run.logicalLeft);484 lineRuns.insert(runIndex, Run(run.start, startSegment.end, run.logicalLeft, logicalRightOfLeftRun, false));485 Run& rightSideRun = lineRuns.at(runIndex + 1);486 rightSideRun.start = startSegment.end;487 rightSideRun.logicalLeft = logicalRightOfLeftRun;488 } while (++runIndex < lineRuns.size());489 }490 491 560 static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount) 492 561 { … … 506 575 closeLineEndingAndAdjustRuns(line, runs, previousRunCount, lineCount, textFragmentIterator); 507 576 } while (!isEndOfContent); 508 509 if (flow.firstChild() != flow.lastChild())510 splitRunsAtRendererBoundary(runs, textFragmentIterator);511 577 } 512 578 -
trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.h
r181290 r181667 56 56 57 57 Iterator& operator++(); 58 Iterator& operator--(); 58 59 bool operator==(const Iterator& other) const; 59 60 bool operator!=(const Iterator& other) const; 60 const Segment& operator*() const { return m_flowContents.m_segments[m_segmentIndex]; } 61 const Segment& operator*() const; 62 const Segment* operator->() const; 61 63 62 64 private: … … 68 70 Iterator end() const { return Iterator(*this, m_segments.size()); } 69 71 70 unsigned length() const { return m_segments.last().end; };71 72 72 unsigned segmentIndexForPosition(unsigned position) const; 73 73 74 74 private: 75 75 unsigned segmentIndexForPositionSlow(unsigned position) const; 76 77 76 const Vector<Segment, 8> m_segments; 78 79 77 mutable unsigned m_lastSegmentIndex; 80 78 }; … … 83 81 { 84 82 ++m_segmentIndex; 83 return *this; 84 } 85 86 inline FlowContents::Iterator& FlowContents::Iterator::operator--() 87 { 88 --m_segmentIndex; 85 89 return *this; 86 90 } … … 94 98 { 95 99 return !(*this == other); 100 } 101 102 inline const FlowContents::Segment& FlowContents::Iterator::operator*() const 103 { 104 ASSERT(m_segmentIndex < m_flowContents.m_segments.size()); 105 return m_flowContents.m_segments[m_segmentIndex]; 106 } 107 108 inline const FlowContents::Segment* FlowContents::Iterator::operator->() const 109 { 110 ASSERT(m_segmentIndex < m_flowContents.m_segments.size()); 111 return &(m_flowContents.m_segments[m_segmentIndex]); 96 112 } 97 113 -
trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp
r181505 r181667 85 85 TextRun textRun(run.text()); 86 86 textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); 87 textRun.setXPos(run.rect().x()); 87 88 FloatPoint textOrigin = run.baseline() + paintOffset; 88 89 textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), flow.document().deviceScaleFactor())); -
trunk/Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.cpp
r181325 r181667 49 49 TextFragmentIterator::TextFragmentIterator(const RenderBlockFlow& flow) 50 50 : m_flowContents(flow) 51 , m_lineBreakIterator((*m_flowContents.begin()).text, flow.style().locale()) 51 , m_currentSegment(m_flowContents.begin()) 52 , m_lineBreakIterator(m_currentSegment->text, flow.style().locale()) 52 53 , m_style(flow.style()) 53 54 { … … 61 62 // 3. non-whitespace characters. 62 63 // 4. empty, indicating content end. 63 if (isEnd(m_position)) 64 ASSERT(m_currentSegment != m_flowContents.end()); 65 if (m_position == m_currentSegment->end) 66 ++m_currentSegment; 67 // Is it content end? 68 if (m_currentSegment == m_flowContents.end()) 64 69 return TextFragment(m_position, m_position, 0, TextFragment::ContentEnd); 70 unsigned segmentEndPosition = m_currentSegment->end; 71 unsigned startPosition = m_position; 65 72 if (isLineBreak(m_position)) { 66 TextFragment fragment(m_position, m_position + 1, 0, TextFragment::LineBreak);67 ++m_position;68 return fragment;69 }70 unsigned startPosition = m_position;71 unsigned endPosition = skipToNextPosition(PositionType::NonWhitespace, startPosition );73 unsigned endPosition = ++m_position; 74 return TextFragment(startPosition, endPosition, 0, TextFragment::LineBreak); 75 } 76 float width = 0; 77 bool overlappingFragment = false; 78 unsigned endPosition = skipToNextPosition(PositionType::NonWhitespace, startPosition, width, xPosition, overlappingFragment); 72 79 ASSERT(startPosition <= endPosition); 73 if ( endPosition > startPosition) {80 if (startPosition < endPosition) { 74 81 bool multipleWhitespace = startPosition + 1 < endPosition; 75 82 bool isCollapsed = multipleWhitespace && m_style.collapseWhitespace; 76 83 bool isBreakable = !isCollapsed && multipleWhitespace; 77 float width = isCollapsed ? m_style.spaceWidth : textWidth(startPosition, endPosition, xPosition);78 84 m_position = endPosition; 79 return TextFragment(startPosition, endPosition, width, TextFragment::Whitespace, isCollapsed, isBreakable);80 } 81 endPosition = skipToNextPosition(PositionType::Breakable, startPosition + 1);85 return TextFragment(startPosition, endPosition, width, TextFragment::Whitespace, endPosition == segmentEndPosition, false, isCollapsed, m_style.collapseWhitespace, isBreakable); 86 } 87 endPosition = skipToNextPosition(PositionType::Breakable, startPosition, width, xPosition, overlappingFragment); 82 88 m_position = endPosition; 83 return TextFragment(startPosition, endPosition, textWidth(startPosition, endPosition, xPosition), TextFragment::NonWhitespace, false, m_style.breakWordOnOverflow); 84 } 85 86 float TextFragmentIterator::textWidth(unsigned from, unsigned to, float xPosition) const 87 { 88 const auto& fromSegment = m_flowContents.segmentForPosition(from); 89 ASSERT(is<RenderText>(fromSegment.renderer)); 90 if ((m_style.font.isFixedPitch() && fromSegment.end >= to) || (from == fromSegment.start && to == fromSegment.end)) 91 return downcast<RenderText>(fromSegment.renderer).width(from - fromSegment.start, to - from, m_style.font, xPosition, nullptr, nullptr); 92 93 const auto* segment = &fromSegment; 94 float textWidth = 0; 95 unsigned fragmentEnd = 0; 96 while (true) { 97 fragmentEnd = std::min(to, segment->end); 98 textWidth += segment->text.is8Bit() ? runWidth<LChar>(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth) : 99 runWidth<UChar>(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth); 100 if (fragmentEnd == to) 101 break; 102 from = fragmentEnd; 103 segment = &m_flowContents.segmentForPosition(fragmentEnd); 104 }; 105 106 return textWidth; 89 return TextFragment(startPosition, endPosition, width, TextFragment::NonWhitespace, endPosition == segmentEndPosition, overlappingFragment, false, false, m_style.breakWordOnOverflow); 90 } 91 92 void TextFragmentIterator::revertToFragment(const TextFragment& fragment) 93 { 94 ASSERT(m_position >= fragment.end()); 95 ASSERT(fragment.start() >= 0); 96 // Revert segment first. 97 while (m_currentSegment->start > fragment.start()) 98 --m_currentSegment; 99 // TODO: It reverts to the last fragment on the same position, but that's ok for now as we don't need to 100 // differentiate multiple renderers on the same position. 101 m_position = fragment.start(); 107 102 } 108 103 109 104 template <typename CharacterType> 110 static unsigned nextBreakablePosition(LazyLineBreakIterator& lineBreakIterator, const FlowContents::Segment& segment, unsigned startPosition) 111 { 112 return nextBreakablePositionNonLoosely<CharacterType, NBSPBehavior::IgnoreNBSP>(lineBreakIterator, segment.text.characters<CharacterType>(), segment.end - segment.start, startPosition); 105 unsigned TextFragmentIterator::nextBreakablePosition(const FlowContents::Segment& segment, unsigned startPosition) 106 { 107 ASSERT(startPosition < segment.end); 108 if (segment.text.impl() != m_lineBreakIterator.string().impl()) { 109 const String& currentText = m_lineBreakIterator.string(); 110 unsigned textLength = currentText.length(); 111 UChar lastCharacter = textLength > 0 ? currentText[textLength - 1] : 0; 112 UChar secondToLastCharacter = textLength > 1 ? currentText[textLength - 2] : 0; 113 m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter); 114 m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorModeUAX14); 115 } 116 const auto* characters = segment.text.characters<CharacterType>(); 117 unsigned segmentLength = segment.end - segment.start; 118 unsigned segmentPosition = startPosition - segment.start; 119 return segment.start + nextBreakablePositionNonLoosely<CharacterType, NBSPBehavior::IgnoreNBSP>(m_lineBreakIterator, characters, segmentLength, segmentPosition); 113 120 } 114 121 115 122 template <typename CharacterType> 116 static unsigned nextNonWhitespacePosition(const FlowContents::Segment& segment, unsigned startPosition, const TextFragmentIterator::Style& style) 117 { 123 unsigned TextFragmentIterator::nextNonWhitespacePosition(const FlowContents::Segment& segment, unsigned startPosition) 124 { 125 ASSERT(startPosition < segment.end); 118 126 const auto* text = segment.text.characters<CharacterType>(); 119 127 unsigned position = startPosition; 120 unsigned length = segment.end - segment.start; 121 for (; position < length; ++position) { 122 auto character = text[position]; 123 bool isWhitespace = character == ' ' || character == '\t' || (!style.preserveNewline && character == '\n'); 128 for (; position < segment.end; ++position) { 129 auto character = text[position - segment.start]; 130 bool isWhitespace = character == ' ' || character == '\t' || (!m_style.preserveNewline && character == '\n'); 124 131 if (!isWhitespace) 125 132 return position; … … 128 135 } 129 136 130 unsigned TextFragmentIterator::skipToNextPosition(PositionType positionType, unsigned startPosition) const 131 { 132 if (isEnd(startPosition)) 133 return startPosition; 134 137 float TextFragmentIterator::textWidth(unsigned from, unsigned to, float xPosition) const 138 { 139 auto& segment = *m_currentSegment; 140 ASSERT(segment.start <= from && from <= segment.end && segment.start <= to && to <= segment.end); 141 ASSERT(is<RenderText>(segment.renderer)); 142 if (m_style.font.isFixedPitch() || (from == segment.start && to == segment.end)) 143 return downcast<RenderText>(segment.renderer).width(from - segment.start, to - from, m_style.font, xPosition, nullptr, nullptr); 144 return segment.text.is8Bit() ? runWidth<LChar>(segment, from, to, xPosition) : runWidth<UChar>(segment, from, to, xPosition); 145 } 146 147 unsigned TextFragmentIterator::skipToNextPosition(PositionType positionType, unsigned startPosition, float& width, float xPosition, bool& overlappingFragment) 148 { 149 overlappingFragment = false; 135 150 unsigned currentPosition = startPosition; 136 FlowContents::Iterator it(m_flowContents, m_flowContents.segmentIndexForPosition(currentPosition)); 137 for (auto end = m_flowContents.end(); it != end; ++it) { 138 auto& segment = *it; 139 unsigned currentPositonRelativeToSegment = currentPosition - segment.start; 140 unsigned nextPositionRelativeToSegment = 0; 141 if (positionType == NonWhitespace) { 142 nextPositionRelativeToSegment = segment.text.is8Bit() ? nextNonWhitespacePosition<LChar>(segment, currentPositonRelativeToSegment, m_style) : 143 nextNonWhitespacePosition<UChar>(segment, currentPositonRelativeToSegment, m_style); 144 } else if (positionType == Breakable) { 145 if (segment.text.impl() != m_lineBreakIterator.string().impl()) { 146 UChar lastCharacter = segment.start > 0 ? characterAt(segment.start - 1) : 0; 147 UChar secondToLastCharacter = segment.start > 1 ? characterAt(segment.start - 2) : 0; 148 m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter); 149 m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorModeUAX14); 150 } 151 nextPositionRelativeToSegment = segment.text.is8Bit() ? nextBreakablePosition<LChar>(m_lineBreakIterator, segment, currentPositonRelativeToSegment) : 152 nextBreakablePosition<UChar>(m_lineBreakIterator, segment, currentPositonRelativeToSegment); 153 } else 154 ASSERT_NOT_REACHED(); 155 currentPosition = segment.start + nextPositionRelativeToSegment; 156 if (currentPosition < segment.end) 157 break; 158 } 159 return currentPosition; 151 unsigned nextPosition = currentPosition; 152 // Collapsed whitespace has constant width. Do not measure it. 153 if (positionType == NonWhitespace) 154 nextPosition = m_currentSegment->text.is8Bit() ? nextNonWhitespacePosition<LChar>(*m_currentSegment, currentPosition) : nextNonWhitespacePosition<UChar>(*m_currentSegment, currentPosition); 155 else if (positionType == Breakable) { 156 // nextBreakablePosition returns the same position for certain characters such as hyphens. Call next again with modified position unless it's the end of the segment. 157 nextPosition = m_currentSegment->text.is8Bit() ? nextBreakablePosition<LChar>(*m_currentSegment, currentPosition) : nextBreakablePosition<UChar>(*m_currentSegment, currentPosition); 158 // We need to know whether the word actually finishes at the end of this renderer or not. 159 if (nextPosition == m_currentSegment->end) { 160 auto nextSegment = m_currentSegment; 161 ++nextSegment; 162 if (nextSegment != m_flowContents.end()) 163 overlappingFragment = nextPosition < (nextSegment->text.is8Bit() ? nextBreakablePosition<LChar>(*nextSegment, nextPosition) : nextBreakablePosition<UChar>(*nextSegment, nextPosition)); 164 } else if (nextPosition == currentPosition) { 165 if (++nextPosition < m_currentSegment->end) 166 nextPosition = m_currentSegment->text.is8Bit() ? nextBreakablePosition<LChar>(*m_currentSegment, currentPosition + 1) : nextBreakablePosition<UChar>(*m_currentSegment, currentPosition + 1); 167 } 168 } 169 width = 0; 170 if (nextPosition == currentPosition) 171 return currentPosition; 172 bool measureText = positionType != NonWhitespace || !m_style.collapseWhitespace; 173 if (measureText) { 174 float textWidth = this->textWidth(currentPosition, nextPosition, xPosition); 175 xPosition += textWidth; 176 width += textWidth; 177 } else if (startPosition < nextPosition) 178 width = m_style.spaceWidth; 179 return nextPosition; 160 180 } 161 181 162 182 template <typename CharacterType> 163 float TextFragmentIterator::runWidth(const String& text, unsigned from, unsigned to, float xPosition) const164 { 165 ASSERT( from <= to);166 if ( from == to)183 float TextFragmentIterator::runWidth(const FlowContents::Segment& segment, unsigned startPosition, unsigned endPosition, float xPosition) const 184 { 185 ASSERT(startPosition <= endPosition); 186 if (startPosition == endPosition) 167 187 return 0; 168 bool measureWithEndSpace = m_style.collapseWhitespace && to < text.length() && text[to] == ' '; 188 unsigned segmentFrom = startPosition - segment.start; 189 unsigned segmentTo = endPosition - segment.start; 190 bool measureWithEndSpace = m_style.collapseWhitespace && segmentTo < segment.text.length() && segment.text[segmentTo] == ' '; 169 191 if (measureWithEndSpace) 170 ++ to;171 TextRun run( text.characters<CharacterType>() + from, to - from);192 ++segmentTo; 193 TextRun run(segment.text.characters<CharacterType>() + segmentFrom, segmentTo - segmentFrom); 172 194 run.setXPos(xPosition); 173 195 run.setTabSize(!!m_style.tabWidth, m_style.tabWidth); -
trunk/Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.h
r181325 r181667 44 44 enum Type { ContentEnd, LineBreak, Whitespace, NonWhitespace }; 45 45 TextFragment() = default; 46 TextFragment(unsigned start, unsigned end, float width, Type type, bool is Collapsed= false, bool isBreakable = false)46 TextFragment(unsigned start, unsigned end, float width, Type type, bool isLastInRenderer = false, bool overlapsToNextRenderer = false, bool isCollapsed = false, bool isCollapsible = false, bool isBreakable = false) 47 47 : m_start(start) 48 48 , m_end(end) 49 , m_width(width) 49 50 , m_type(type) 50 , m_width(width) 51 , m_isLastInRenderer(isLastInRenderer) 52 , m_overlapsToNextRenderer(overlapsToNextRenderer) 51 53 , m_isCollapsed(isCollapsed) 54 , m_isCollapsible(isCollapsible) 52 55 , m_isBreakable(isBreakable) 53 56 { … … 58 61 float width() const { return m_width; } 59 62 Type type() const { return m_type; } 63 bool isLastInRenderer() const { return m_isLastInRenderer; } 64 bool overlapsToNextRenderer() const { return m_overlapsToNextRenderer; } 60 65 bool isCollapsed() const { return m_isCollapsed; } 66 bool isCollapsible() const { return m_isCollapsible; } 61 67 bool isBreakable() const { return m_isBreakable; } 62 68 63 69 bool isEmpty() const { return start() == end(); } 64 70 TextFragment split(unsigned splitPosition, const TextFragmentIterator&); 71 bool operator==(const TextFragment& other) const 72 { 73 return m_start == other.m_start 74 && m_end == other.m_end 75 && m_width == other.m_width 76 && m_type == other.m_type 77 && m_isLastInRenderer == other.m_isLastInRenderer 78 && m_overlapsToNextRenderer == other.m_overlapsToNextRenderer 79 && m_isCollapsed == other.m_isCollapsed 80 && m_isCollapsible == other.m_isCollapsible 81 && m_isBreakable == other.m_isBreakable; 82 } 65 83 66 84 private: 67 85 unsigned m_start { 0 }; 68 86 unsigned m_end { 0 }; 87 float m_width { 0 }; 69 88 Type m_type { NonWhitespace }; 70 float m_width { 0 }; 89 bool m_isLastInRenderer { false }; 90 bool m_overlapsToNextRenderer { false }; 71 91 bool m_isCollapsed { false }; 92 bool m_isCollapsible { false }; 72 93 bool m_isBreakable { false }; 73 94 }; 74 95 TextFragment nextTextFragment(float xPosition = 0); 75 float textWidth(unsigned from, unsigned to, float xPosition) const; 96 void revertToFragment(const TextFragment&); 97 float textWidth(unsigned startPosition, unsigned endPosition, float xPosition) const; 76 98 77 99 struct Style { … … 89 111 }; 90 112 const Style& style() const { return m_style; } 91 // FIXME: remove splitRunsAtRendererBoundary()92 const FlowContents::Segment& segmentForPosition(unsigned position) const { return m_flowContents.segmentForPosition(position); };93 113 94 114 private: 95 115 enum PositionType { Breakable, NonWhitespace }; 96 unsigned skipToNextPosition(PositionType, unsigned startPosition) const; 97 UChar characterAt(unsigned position) const; 116 unsigned skipToNextPosition(PositionType, unsigned startPosition, float& width, float xPosition, bool& overlappingFragment); 98 117 bool isLineBreak(unsigned position) const; 99 bool isEnd(unsigned position) const; 100 template <typename CharacterType> float runWidth(const String&, unsigned from, unsigned to, float xPosition) const; 118 template <typename CharacterType> unsigned nextBreakablePosition(const FlowContents::Segment&, unsigned startPosition); 119 template <typename CharacterType> unsigned nextNonWhitespacePosition(const FlowContents::Segment&, unsigned startPosition); 120 template <typename CharacterType> float runWidth(const FlowContents::Segment&, unsigned startPosition, unsigned endPosition, float xPosition) const; 101 121 102 122 FlowContents m_flowContents; 103 mutable LazyLineBreakIterator m_lineBreakIterator; 123 FlowContents::Iterator m_currentSegment; 124 LazyLineBreakIterator m_lineBreakIterator; 104 125 const Style m_style; 105 126 unsigned m_position { 0 }; … … 128 149 } 129 150 130 inline UChar TextFragmentIterator::characterAt(unsigned position) const131 {132 auto& segment = m_flowContents.segmentForPosition(position);133 return segment.text[position - segment.start];134 }135 136 151 inline bool TextFragmentIterator::isLineBreak(unsigned position) const 137 152 { 138 if (isEnd(position)) 139 return false; 140 return m_style.preserveNewline && characterAt(position) == '\n'; 141 } 142 143 inline bool TextFragmentIterator::isEnd(unsigned position) const 144 { 145 return position >= m_flowContents.length(); 153 const auto& segment = *m_currentSegment; 154 ASSERT(segment.start <= position && position < segment.end); 155 return m_style.preserveNewline && segment.text[position - segment.start] == '\n'; 146 156 } 147 157
Note:
See TracChangeset
for help on using the changeset viewer.