Changeset 226138 in webkit
- Timestamp:
- Dec 19, 2017, 1:48:19 PM (9 years ago)
- Location:
- trunk
- Files:
-
- 13 edited
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/rendering/InlineTextBox.cpp (modified) (14 diffs)
-
Source/WebCore/rendering/InlineTextBox.h (modified) (2 diffs)
-
Source/WebCore/rendering/MarkerSubrange.h (modified) (2 diffs)
-
Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp (modified) (1 diff)
-
Source/WebCore/rendering/TextDecorationPainter.cpp (modified) (1 diff)
-
Source/WebCore/rendering/TextDecorationPainter.h (modified) (3 diffs)
-
Source/WebCore/rendering/TextPaintStyle.cpp (modified) (3 diffs)
-
Source/WebCore/rendering/TextPaintStyle.h (modified) (2 diffs)
-
Source/WebCore/rendering/TextPainter.cpp (modified) (2 diffs)
-
Source/WebCore/rendering/TextPainter.h (modified) (2 diffs)
-
Tools/ChangeLog (modified) (1 diff)
-
Tools/TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r226137 r226138 1 2017-12-19 Daniel Bates <dabates@apple.com> 2 3 Implement InlineTextBox painting using marker subranges 4 https://bugs.webkit.org/show_bug.cgi?id=180984 5 <rdar://problem/36139364> 6 7 Reviewed by David Hyatt. 8 9 As a step towards implementing CSS Pseudo-Elements Module Level 4, explicitly decompose a 10 text line into subrange objects that may be styled (say, to give the appearance of selected 11 text) and painted. 12 13 No functionality changed. So, no new tests. 14 15 * rendering/InlineTextBox.cpp: 16 (WebCore::InlineTextBox::localSelectionRect const): Update comments. 17 18 (WebCore::InlineTextBox::MarkerSubrangeStyle::operator== const): 19 (WebCore::InlineTextBox::MarkerSubrangeStyle::operator!= const): 20 (WebCore::InlineTextBox::StyledMarkerSubrange::StyledMarkerSubrange): 21 Define a subclass to represent a marker subrange that has associated style information. 22 We will make use of the style information when painting the subrange. 23 24 (WebCore::createMarkerSubrangeFromSelectionInBox): Convenience function to instantiate a 25 MarkerSubrange instance from the current selection of a specified text box. 26 27 (WebCore::InlineTextBox::paint): Write in terms of MarkerSubrange. We keep composition backgrounds 28 and composition underlines as special cases for now. We represent all other subranges of the line 29 as MarkerSubrange instances, including unmarked text (i.e. non-selected text without an associated 30 document marker). The subranges for the gaps between document markers and selection are implicitly 31 created by subdividing the entire line. Support for PaintBehaviorExcludeSelection is implemented 32 by subdividing the entire line with the selection subrange and then removing the selection subrange 33 from the resulting list of subdivisions before painting with the resulting list. 34 35 (WebCore::InlineTextBox::paintPlatformDocumentMarkers): Renamed; formerly named paintDocumentMarkers. 36 (WebCore::InlineTextBox::paintPlatformDocumentMarker): Renamed; formerly named paintDocumentMarker. 37 38 (WebCore::InlineTextBox::computeStyleForUnmarkedMarkerSubrange const): Convenience function to 39 compute the style for unmarked text. 40 41 (WebCore::InlineTextBox::resolveStyleForSubrange): Added 42 43 (WebCore::InlineTextBox::subdivideAndResolveStyle): Subdivides the list of marker subranges and 44 coalesces subranges of the same type (e.g. selection) or with the same style. The latter strategy 45 preserves the optimization of drawing the text of the entire line in one draw command when we know 46 that the selected text looks identical to non-selected text. This optimization was performed in TextPainter::paint(). 47 48 (WebCore::InlineTextBox::collectSubrangesForDraggedContent): Added. 49 (WebCore::InlineTextBox::collectSubrangesForDocumentMarkers): Added. 50 (WebCore::InlineTextBox::textOriginFromBoxRect const): Added. 51 52 (WebCore::InlineTextBox::paintMarkerSubranges): 53 (WebCore::InlineTextBox::paintTextSubrangeBackground): 54 (WebCore::InlineTextBox::paintTextSubrangeForeground): 55 (WebCore::InlineTextBox::paintTextSubrangeDecoration): 56 Paint the marker subrange. 57 58 (WebCore::InlineTextBox::paintCompositionBackground): Moved function to group it with the logic to 59 paint composition underlines. Modified to pass clamped offsets to paintTextSubrangeBackground() as 60 paintTextSubrangeBackground() now expects them. 61 62 (WebCore::mirrorRTLSegment): Moved function to be above paintCompositionUnderline() as it is the only 63 function that makes use of it. 64 65 (WebCore::InlineTextBox::paintSelection): Deleted. 66 (WebCore::InlineTextBox::paintTextMatchMarker): Deleted. 67 (WebCore::InlineTextBox::paintDecoration): Deleted. 68 (WebCore::InlineTextBox::paintDocumentMarker): Deleted; renamed to paintPlatformDocumentMarker. 69 (WebCore::InlineTextBox::paintDocumentMarkers): Deleted; renamed to paintPlatformDocumentMarkers. 70 * rendering/InlineTextBox.h: 71 (WebCore::InlineTextBox::paintMarkerSubranges): Added. 72 * rendering/MarkerSubrange.h: 73 (WebCore::MarkerSubrange::isEmpty const): Added. 74 (WebCore::MarkerSubrange::operator!= const): Added. 75 (WebCore::MarkerSubrange::operator== const): Added. 76 * rendering/SimpleLineLayoutFunctions.cpp: 77 (WebCore::SimpleLineLayout::paintFlow): Updated code as we no longer need to pass the text run length 78 to TextPainter::paint(). Also modernize the code while I am here. 79 * rendering/TextDecorationPainter.cpp: 80 (WebCore::TextDecorationPainter::Styles::operator== const): Added. 81 (WebCore::TextDecorationPainter::TextDecorationPainter): Modified to take an optional TextDecorationPainter::Styles 82 instance as an argument and removed the unused parameter for PseudoId. 83 * rendering/TextDecorationPainter.h: 84 (WebCore::TextDecorationPainter::Styles::operator!= const): Added. 85 * rendering/TextPaintStyle.cpp: 86 (WebCore::TextPaintStyle::operator== const): Added. 87 (WebCore::computeTextSelectionPaintStyle): Removed the now unused out-arguments paintSelectedTextOnly, paintSelectedTextSeparately, 88 and paintNonSelectedTextOnly. The values stored in these out-arguments were intended to be used by the caller to 89 minimize the number of drawing commands. The value of paintSelectedTextSeparately indicated whether selected text 90 would look identical to non-selected text so that a caller could use one paint command to draw the entire line 91 instead of issuing two or more drawing commands to paint non-selected and selected text separately. We now 92 accomplish the same optimization by coalescing subrange styles in InlineTextBox::subdivideAndResolveStyle(). 93 The value of paintNonSelectedTextOnly, as its name states, indicated whether a caller should only paint non-selected 94 text and hence skip painting of selected text. This value was only used when painting dragged content (i.e. DocumentMarker::DraggedContent 95 markers) and its effect is now more directly achieved by the dragged content logic in InlineTextBox::paint(). 96 * rendering/TextPaintStyle.h: 97 (WebCore::TextPaintStyle::operator!= const): Added. 98 * rendering/TextPainter.cpp: 99 (WebCore::TextPainter::paint): Implemented in terms TextPainter::paintRange() now that InlineTextBox::paint() optimizes 100 for the number of drawing calls. See remarks for WebCore::computeTextSelectionPaintStyle() for more details. 101 * rendering/TextPainter.h: Group together related member functions. 102 (WebCore::TextPainter::setSelectionStyle): Deleted. 103 (WebCore::TextPainter::setSelectionShadow): Deleted. 104 1 105 2017-12-19 Daniel Bates <dabates@apple.com> 2 106 -
trunk/Source/WebCore/rendering/InlineTextBox.cpp
r225712 r226138 189 189 } 190 190 191 // FIXME: Share more code with paint Selection().191 // FIXME: Share more code with paintTextSubrangeBackground(). 192 192 LayoutRect InlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const 193 193 { … … 205 205 206 206 LayoutRect selectionRect = LayoutRect(LayoutPoint(logicalLeft(), selectionTop), LayoutSize(m_logicalWidth, selectionHeight)); 207 // Avoid computing the font widthwhen the entire line box is selected as an optimization.207 // Avoid measuring the text when the entire line box is selected as an optimization. 208 208 if (sPos || ePos != textRun.length()) 209 209 lineFont().adjustSelectionRectForText(textRun, selectionRect, sPos, ePos); 210 210 // FIXME: The computation of the snapped selection rect differs from the computation of this rect 211 // in paint Selection(). See <https://bugs.webkit.org/show_bug.cgi?id=138913>.211 // in paintTextSubrangeBackground(). See <https://bugs.webkit.org/show_bug.cgi?id=138913>. 212 212 IntRect snappedSelectionRect = enclosingIntRect(selectionRect); 213 213 LayoutUnit logicalWidth = snappedSelectionRect.width(); … … 393 393 } 394 394 395 struct InlineTextBox::MarkerSubrangeStyle { 396 bool operator==(const MarkerSubrangeStyle& other) const 397 { 398 return backgroundColor == other.backgroundColor && textStyles == other.textStyles 399 && textDecorationStyles == other.textDecorationStyles && textShadow == other.textShadow 400 && alpha == other.alpha; 401 } 402 bool operator!=(const MarkerSubrangeStyle& other) const { return !(*this == other); } 403 404 Color backgroundColor; 405 TextPaintStyle textStyles; 406 TextDecorationPainter::Styles textDecorationStyles; 407 const ShadowData* textShadow; 408 float alpha; 409 }; 410 411 struct InlineTextBox::StyledMarkerSubrange : MarkerSubrange { 412 StyledMarkerSubrange(const MarkerSubrange& marker) 413 : MarkerSubrange { marker } 414 { 415 } 416 417 MarkerSubrangeStyle style; 418 }; 419 420 static MarkerSubrange createMarkerSubrangeFromSelectionInBox(const InlineTextBox& box) 421 { 422 unsigned selectionStart; 423 unsigned selectionEnd; 424 std::tie(selectionStart, selectionEnd) = box.selectionStartEnd(); 425 if (selectionStart < selectionEnd) 426 return { selectionStart, selectionEnd, MarkerSubrange::Selection }; 427 return { }; 428 } 429 395 430 void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/) 396 431 { … … 459 494 bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines(); 460 495 461 // Determine the text colors and selection colors. 462 TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo); 463 464 bool paintSelectedTextOnly = false; 465 bool paintSelectedTextSeparately = false; 466 bool paintNonSelectedTextOnly = false; 467 const ShadowData* selectionShadow = nullptr; 468 469 // Text with custom underlines does not have selection background painted, so selection paint style is not appropriate for it. 470 TextPaintStyle selectionPaintStyle = haveSelection && !useCustomUnderlines ? computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, paintNonSelectedTextOnly, selectionShadow) : textPaintStyle; 471 472 // Set our font. 473 const FontCascade& font = lineFont(); 496 MarkerSubrangeStyle unmarkedStyle = computeStyleForUnmarkedMarkerSubrange(paintInfo); 497 474 498 // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection 475 499 // and composition underlines. … … 478 502 paintCompositionBackground(context, boxOrigin); 479 503 480 paintDocumentMarkers(context, boxOrigin, true); 481 482 if (haveSelection && !useCustomUnderlines) 483 paintSelection(context, boxOrigin, selectionPaintStyle.fillColor); 504 Vector<MarkerSubrange> subranges = collectSubrangesForDocumentMarkers(TextPaintPhase::Background); 505 #if ENABLE(TEXT_SELECTION) 506 if (haveSelection && !useCustomUnderlines && !context.paintingDisabled()) { 507 auto selectionSubrange = createMarkerSubrangeFromSelectionInBox(*this); 508 if (!selectionSubrange.isEmpty()) 509 subranges.append(WTFMove(selectionSubrange)); 510 } 511 #endif 512 auto styledSubranges = subdivideAndResolveStyle(subranges, unmarkedStyle, paintInfo); 513 paintMarkerSubranges(context, TextPaintPhase::Background, boxRect, styledSubranges); 484 514 } 485 515 … … 492 522 493 523 // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only). 494 auto text = this->text(); 495 TextRun textRun = createTextRun(text); 496 unsigned length = textRun.length(); 497 if (m_truncation != cNoTruncation) 498 length = m_truncation; 499 500 unsigned selectionStart = 0; 501 unsigned selectionEnd = 0; 502 if (haveSelection && (paintSelectedTextOnly || paintSelectedTextSeparately)) 503 std::tie(selectionStart, selectionEnd) = selectionStartEnd(); 504 505 float emphasisMarkOffset = 0; 506 bool emphasisMarkAbove; 507 bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove); 508 const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom(); 509 if (!emphasisMark.isEmpty()) 510 emphasisMarkOffset = emphasisMarkAbove ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark); 511 512 const ShadowData* textShadow = (paintInfo.forceTextColor()) ? nullptr : lineStyle.textShadow(); 513 514 FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); 515 if (combinedText) { 516 if (auto newOrigin = combinedText->computeTextOrigin(boxRect)) 517 textOrigin = newOrigin.value(); 518 } 519 520 if (isHorizontal()) 521 textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), renderer().document().deviceScaleFactor())); 522 else 523 textOrigin.setX(roundToDevicePixel(LayoutUnit(textOrigin.x()), renderer().document().deviceScaleFactor())); 524 525 TextPainter textPainter(context); 526 textPainter.setFont(font); 527 textPainter.setStyle(textPaintStyle); 528 textPainter.setSelectionStyle(selectionPaintStyle); 529 textPainter.setIsHorizontal(isHorizontal()); 530 textPainter.setShadow(textShadow); 531 textPainter.setSelectionShadow(selectionShadow); 532 textPainter.setEmphasisMark(emphasisMark, emphasisMarkOffset, combinedText); 533 534 auto draggedContentRanges = renderer().draggedContentRangesBetweenOffsets(m_start, m_start + m_len); 535 if (!draggedContentRanges.isEmpty() && !paintSelectedTextOnly && !paintNonSelectedTextOnly) { 536 // FIXME: Painting with text effects ranges currently only works if we're not also painting the selection. 537 // In the future, we may want to support this capability, but in the meantime, this isn't required by anything. 538 unsigned currentEnd = 0; 539 for (size_t index = 0; index < draggedContentRanges.size(); ++index) { 540 unsigned previousEnd = index ? std::min(draggedContentRanges[index - 1].second, length) : 0; 541 unsigned currentStart = draggedContentRanges[index].first - m_start; 542 currentEnd = std::min(draggedContentRanges[index].second - m_start, length); 543 544 if (previousEnd < currentStart) 545 textPainter.paintRange(textRun, boxRect, textOrigin, previousEnd, currentStart); 546 547 if (currentStart < currentEnd) { 548 context.save(); 549 context.setAlpha(0.25); 550 textPainter.paintRange(textRun, boxRect, textOrigin, currentStart, currentEnd); 551 context.restore(); 524 bool shouldPaintSelectionForeground = haveSelection && !useCustomUnderlines; 525 Vector<MarkerSubrange> subranges; 526 if (paintInfo.phase != PaintPhaseSelection) { 527 // The subranges for the gaps between document markers and selection are implicitly created by subdividing the entire line. 528 subranges.append({ clampedOffset(m_start), clampedOffset(end() + 1), MarkerSubrange::Unmarked }); 529 if (!isPrinting) { 530 subranges.appendVector(collectSubrangesForDocumentMarkers(TextPaintPhase::Foreground)); 531 532 bool shouldPaintDraggedContent = !(paintInfo.paintBehavior & PaintBehaviorExcludeSelection); 533 if (shouldPaintDraggedContent) { 534 auto subrangesForDraggedContent = collectSubrangesForDraggedContent(); 535 if (!subrangesForDraggedContent.isEmpty()) { 536 shouldPaintSelectionForeground = false; 537 subranges.appendVector(subrangesForDraggedContent); 538 } 552 539 } 553 540 } 554 if (currentEnd < length) 555 textPainter.paintRange(textRun, boxRect, textOrigin, currentEnd, length); 556 } else 557 textPainter.paint(textRun, length, boxRect, textOrigin, selectionStart, selectionEnd, paintSelectedTextOnly, paintSelectedTextSeparately, paintNonSelectedTextOnly); 541 } 542 // The selection subrange acts as a placeholder when computing the subranges for the gaps... 543 if (shouldPaintSelectionForeground) { 544 ASSERT(!isPrinting); 545 auto selectionSubrange = createMarkerSubrangeFromSelectionInBox(*this); 546 if (!selectionSubrange.isEmpty()) 547 subranges.append(WTFMove(selectionSubrange)); 548 } 549 550 auto styledSubranges = subdivideAndResolveStyle(subranges, unmarkedStyle, paintInfo); 551 552 // ... now remove the selection subrange if we are excluding selection. 553 if (!isPrinting && paintInfo.paintBehavior & PaintBehaviorExcludeSelection) 554 styledSubranges.removeAllMatching([] (const StyledMarkerSubrange& subrange) { return subrange.type == MarkerSubrange::Selection; }); 555 556 paintMarkerSubranges(context, TextPaintPhase::Foreground, boxRect, styledSubranges); 558 557 559 558 // Paint decorations 560 559 TextDecoration textDecorations = lineStyle.textDecorationsInEffect(); 561 560 if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) { 561 auto text = this->text(); 562 TextRun textRun = createTextRun(text); 563 unsigned length = textRun.length(); 564 if (m_truncation != cNoTruncation) 565 length = m_truncation; 566 unsigned selectionStart = 0; 567 unsigned selectionEnd = 0; 568 if (haveSelection) 569 std::tie(selectionStart, selectionEnd) = selectionStartEnd(); 570 562 571 FloatRect textDecorationSelectionClipOutRect; 563 572 if ((paintInfo.paintBehavior & PaintBehaviorExcludeSelection) && selectionStart < selectionEnd && selectionEnd <= length) { … … 566 575 float logicalWidthBeforeRange; 567 576 float logicalWidthAfterRange; 568 float logicalSelectionWidth = font.widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange);577 float logicalSelectionWidth = lineFont().widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange); 569 578 // FIXME: Do we need to handle vertical bottom to top text? 570 579 if (!isHorizontal()) { … … 579 588 } 580 589 } 581 paintDecoration(context, textRun, textOrigin, boxRect, textDecorations, textPaintStyle, textShadow, textDecorationSelectionClipOutRect); 582 } 583 590 paintMarkerSubranges(context, TextPaintPhase::Decoration, boxRect, styledSubranges, textDecorationSelectionClipOutRect); 591 } 592 593 // 3. Paint fancy decorations, including composition underlines and platform-specific underlines for spelling errors, grammar errors, et cetera. 584 594 if (paintInfo.phase == PaintPhaseForeground) { 585 paint DocumentMarkers(context, boxOrigin, false);595 paintPlatformDocumentMarkers(context, boxOrigin); 586 596 587 597 if (useCustomUnderlines) … … 627 637 } 628 638 629 void InlineTextBox::paintSelection(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& textColor) 630 { 631 #if ENABLE(TEXT_SELECTION) 632 if (context.paintingDisabled()) 633 return; 634 635 // See if we have a selection to paint at all. 636 unsigned selectionStart; 637 unsigned selectionEnd; 638 std::tie(selectionStart, selectionEnd) = selectionStartEnd(); 639 if (selectionStart >= selectionEnd) 640 return; 641 642 Color c = renderer().selectionBackgroundColor(); 643 if (!c.isValid() || c.alpha() == 0) 644 return; 645 646 // If the text color ends up being the same as the selection background, invert the selection 647 // background. 648 if (textColor == c) 649 c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); 650 651 GraphicsContextStateSaver stateSaver(context); 652 updateGraphicsContext(context, TextPaintStyle(c)); // Don't draw text at all! 653 654 // Note that if the text is truncated, we let the thing being painted in the truncation 655 // draw its own highlight. 656 auto text = this->text(); 657 TextRun textRun = createTextRun(text); 658 659 const RootInlineBox& rootBox = root(); 660 LayoutUnit selectionBottom = rootBox.selectionBottom(); 661 LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock(); 662 663 LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop; 664 LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop); 665 666 LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight); 667 lineFont().adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd); 668 669 // FIXME: Support painting combined text. 670 context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), c); 671 #else 672 UNUSED_PARAM(context); 673 UNUSED_PARAM(boxOrigin); 674 UNUSED_PARAM(textColor); 675 #endif 676 } 677 678 inline void InlineTextBox::paintTextSubrangeBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& color, unsigned startOffset, unsigned endOffset) 679 { 680 startOffset = clampedOffset(startOffset); 681 endOffset = clampedOffset(endOffset); 682 if (startOffset >= endOffset) 683 return; 684 685 GraphicsContextStateSaver stateSaver { context }; 686 updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all! 687 688 // Use same y positioning and height as for selection, so that when the selection and this subrange are on 689 // the same word there are no pieces sticking out. 690 LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); 691 LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, selectionHeight()); 692 693 auto text = this->text(); 694 TextRun textRun = createTextRun(text); 695 lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset); 696 697 // FIXME: Support painting combined text. 698 context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color); 699 } 700 701 void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin) 702 { 703 paintTextSubrangeBackground(context, boxOrigin, renderer().frame().editor().compositionStart(), renderer().frame().editor().compositionEnd(), Color::compositionFill); 704 } 705 706 void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange, bool isActiveMatch) 707 { 708 if (!renderer().frame().editor().markedTextMatchesAreHighlighted()) 709 return; 710 auto highlightColor = isActiveMatch ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor(); 711 paintTextSubrangeBackground(context, boxOrigin, highlightColor, subrange.startOffset, subrange.endOffset); 712 } 713 714 static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width) 715 { 716 if (direction == LTR) 717 return; 718 start = logicalWidth - width - start; 719 } 720 721 void InlineTextBox::paintDecoration(GraphicsContext& context, const TextRun& textRun, const FloatPoint& textOrigin, 722 const FloatRect& boxRect, TextDecoration decoration, TextPaintStyle textPaintStyle, const ShadowData* shadow, const FloatRect& clipOutRect) 723 { 724 if (m_truncation == cFullTruncation) 725 return; 726 727 updateGraphicsContext(context, textPaintStyle); 728 729 bool isCombinedText = combinedText(); 730 if (isCombinedText) 731 context.concatCTM(rotation(boxRect, Clockwise)); 732 733 float start = 0; 734 float width = m_logicalWidth; 735 if (m_truncation != cNoTruncation) { 736 width = renderer().width(m_start, m_truncation, textPos(), isFirstLine()); 737 mirrorRTLSegment(m_logicalWidth, direction(), start, width); 738 } 739 740 TextDecorationPainter decorationPainter(context, decoration, renderer(), isFirstLine()); 741 decorationPainter.setInlineTextBox(this); 742 decorationPainter.setFont(lineFont()); 743 decorationPainter.setWidth(width); 744 decorationPainter.setBaseline(lineStyle().fontMetrics().ascent()); 745 decorationPainter.setIsHorizontal(isHorizontal()); 746 decorationPainter.addTextShadow(shadow); 747 748 FloatPoint localOrigin = boxRect.location(); 749 localOrigin.move(start, 0); 750 751 { 752 GraphicsContextStateSaver stateSaver { context, false }; 753 if (!clipOutRect.isEmpty()) { 754 stateSaver.save(); 755 context.clipOut(clipOutRect); 756 } 757 decorationPainter.paintTextDecoration(textRun, textOrigin, localOrigin); 758 } 759 760 if (isCombinedText) 761 context.concatCTM(rotation(boxRect, Counterclockwise)); 762 } 763 764 void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange) 639 void InlineTextBox::paintPlatformDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin) 640 { 641 for (auto& subrange : subdivide(collectSubrangesForDocumentMarkers(TextPaintPhase::Foreground), OverlapStrategy::Frontmost)) 642 paintPlatformDocumentMarker(context, boxOrigin, subrange); 643 } 644 645 void InlineTextBox::paintPlatformDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange) 765 646 { 766 647 // Never print spelling/grammar markers (5327887) … … 774 655 float width = m_logicalWidth; // how much line to draw 775 656 776 // Determine whether we need to measure text 777 bool markerSpansWholeBox = true; 778 if (m_start <= subrange.startOffset) 779 markerSpansWholeBox = false; 780 if ((end() + 1) != subrange.endOffset) // End points at the last char, not past it 781 markerSpansWholeBox = false; 782 if (m_truncation != cNoTruncation) 783 markerSpansWholeBox = false; 784 785 if (!markerSpansWholeBox) { 786 unsigned startPosition = clampedOffset(subrange.startOffset); 787 unsigned endPosition = clampedOffset(subrange.endOffset); 788 657 // Avoid measuring the text when the entire line box is selected as an optimization. 658 if (subrange.startOffset || subrange.endOffset != clampedOffset(end() + 1)) { 789 659 // Calculate start & width 790 660 int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); … … 795 665 796 666 LayoutRect selectionRect = LayoutRect(startPoint, FloatSize(0, selHeight)); 797 lineFont().adjustSelectionRectForText(run, selectionRect, s tartPosition, endPosition);667 lineFont().adjustSelectionRectForText(run, selectionRect, subrange.startOffset, subrange.endOffset); 798 668 IntRect markerRect = enclosingIntRect(selectionRect); 799 669 start = markerRect.x() - startPoint.x(); 800 670 width = markerRect.width(); 801 671 } 802 672 803 673 auto lineStyleForSubrangeType = [] (MarkerSubrange::Type type) { 804 674 switch (type) { … … 839 709 underlineOffset = baseline + 2; 840 710 } 841 // FIXME: Support painting combined text.842 711 context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForSubrangeType(subrange.type)); 843 712 } 844 713 845 void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, bool background) 846 { 714 auto InlineTextBox::computeStyleForUnmarkedMarkerSubrange(const PaintInfo& paintInfo) const -> MarkerSubrangeStyle 715 { 716 auto& lineStyle = this->lineStyle(); 717 718 MarkerSubrangeStyle style; 719 style.textDecorationStyles = TextDecorationPainter::stylesForRenderer(renderer(), lineStyle.textDecorationsInEffect(), isFirstLine()); 720 style.textStyles = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo); 721 style.textShadow = paintInfo.forceTextColor() ? nullptr : lineStyle.textShadow(); 722 style.alpha = 1; 723 return style; 724 } 725 726 auto InlineTextBox::resolveStyleForSubrange(const MarkerSubrange& subrange, const MarkerSubrangeStyle& baseStyle, const PaintInfo& paintInfo) -> StyledMarkerSubrange 727 { 728 MarkerSubrangeStyle style = baseStyle; 729 switch (subrange.type) { 730 case MarkerSubrange::Correction: 731 case MarkerSubrange::DictationAlternatives: 732 #if PLATFORM(IOS) 733 // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard. 734 case MarkerSubrange::DictationPhraseWithAlternatives: 735 #endif 736 case MarkerSubrange::GrammarError: 737 case MarkerSubrange::SpellingError: 738 case MarkerSubrange::Unmarked: 739 break; 740 case MarkerSubrange::DraggedContent: 741 style.alpha = 0.25; 742 break; 743 case MarkerSubrange::Selection: { 744 const ShadowData* selectionShadow = nullptr; 745 style.textStyles = computeTextSelectionPaintStyle(style.textStyles, renderer(), lineStyle(), paintInfo, selectionShadow); 746 style.textShadow = selectionShadow; 747 748 Color selectionBackgroundColor = renderer().selectionBackgroundColor(); 749 style.backgroundColor = selectionBackgroundColor; 750 if (selectionBackgroundColor.isValid() && selectionBackgroundColor.alpha() && style.textStyles.fillColor == selectionBackgroundColor) 751 style.backgroundColor = { 0xff - selectionBackgroundColor.red(), 0xff - selectionBackgroundColor.green(), 0xff - selectionBackgroundColor.blue() }; 752 break; 753 } 754 case MarkerSubrange::TextMatch: 755 style.backgroundColor = subrange.marker->isActiveMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor(); 756 break; 757 } 758 StyledMarkerSubrange styledSubrange = subrange; 759 styledSubrange.style = WTFMove(style); 760 return styledSubrange; 761 } 762 763 auto InlineTextBox::subdivideAndResolveStyle(const Vector<MarkerSubrange>& subrangesToSubdivide, const MarkerSubrangeStyle& baseStyle, const PaintInfo& paintInfo) -> Vector<StyledMarkerSubrange> 764 { 765 if (subrangesToSubdivide.isEmpty()) 766 return { }; 767 768 auto areAdjacentSubrangesWithSameStyle = [] (const StyledMarkerSubrange& a, const StyledMarkerSubrange& b) { 769 return a.endOffset == b.startOffset && a.style == b.style; 770 }; 771 772 auto subranges = subdivide(subrangesToSubdivide, OverlapStrategy::FrontmostWithLongestEffectiveRange); 773 774 // Coallesce styles of adjacent subranges to minimize the number of drawing commands. 775 Vector<StyledMarkerSubrange> styledSubranges; 776 styledSubranges.reserveInitialCapacity(subranges.size()); 777 styledSubranges.uncheckedAppend(resolveStyleForSubrange(subranges[0], baseStyle, paintInfo)); 778 for (auto it = subranges.begin() + 1, end = subranges.end(); it != end; ++it) { 779 StyledMarkerSubrange& previousStyledSubrange = styledSubranges.last(); 780 auto currentStyledSubrange = resolveStyleForSubrange(*it, baseStyle, paintInfo); 781 if (areAdjacentSubrangesWithSameStyle(previousStyledSubrange, currentStyledSubrange)) { 782 previousStyledSubrange.endOffset = currentStyledSubrange.endOffset; 783 continue; 784 } 785 styledSubranges.uncheckedAppend(currentStyledSubrange); 786 } 787 return styledSubranges; 788 } 789 790 Vector<MarkerSubrange> InlineTextBox::collectSubrangesForDraggedContent() 791 { 792 using DraggendContentRange = std::pair<unsigned, unsigned>; 793 auto draggedContentRanges = renderer().draggedContentRangesBetweenOffsets(m_start, m_start + m_len); 794 Vector<MarkerSubrange> result = draggedContentRanges.map([this] (const DraggendContentRange& range) -> MarkerSubrange { 795 return { clampedOffset(range.first), clampedOffset(range.second), MarkerSubrange::DraggedContent }; 796 }); 797 return result; 798 } 799 800 Vector<MarkerSubrange> InlineTextBox::collectSubrangesForDocumentMarkers(TextPaintPhase phase) 801 { 802 ASSERT(phase == TextPaintPhase::Background || phase == TextPaintPhase::Foreground); 847 803 if (!renderer().textNode()) 848 return ;804 return { }; 849 805 850 806 Vector<RenderedDocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode()); … … 877 833 // Note end() points at the last char, not one past it like endOffset and ranges do. 878 834 for (auto* marker : markers) { 879 // Paint either the background markers or the foreground markers, but not both835 // Collect either the background markers or the foreground markers, but not both 880 836 switch (marker->type()) { 881 case DocumentMarker::Grammar:882 case DocumentMarker::Spelling:883 case DocumentMarker::CorrectionIndicator:884 case DocumentMarker::Replacement:885 case DocumentMarker::DictationAlternatives:837 case DocumentMarker::Grammar: 838 case DocumentMarker::Spelling: 839 case DocumentMarker::CorrectionIndicator: 840 case DocumentMarker::Replacement: 841 case DocumentMarker::DictationAlternatives: 886 842 #if PLATFORM(IOS) 887 // FIXME: Remove the PLATFORM(IOS)-guard.888 case DocumentMarker::DictationPhraseWithAlternatives:843 // FIXME: Remove the PLATFORM(IOS)-guard. 844 case DocumentMarker::DictationPhraseWithAlternatives: 889 845 #endif 890 if (background) 891 continue; 892 break; 893 case DocumentMarker::TextMatch: 846 if (phase == TextPaintPhase::Background) 847 continue; 848 break; 849 case DocumentMarker::TextMatch: 850 if (!renderer().frame().editor().markedTextMatchesAreHighlighted()) 851 continue; 852 FALLTHROUGH; 894 853 #if ENABLE(TELEPHONE_NUMBER_DETECTION) 895 case DocumentMarker::TelephoneNumber:854 case DocumentMarker::TelephoneNumber: 896 855 #endif 897 if (!background) 898 continue; 899 break; 900 default: 856 if (phase == TextPaintPhase::Foreground) 901 857 continue; 902 } 903 904 if (marker->endOffset() <= start()) 905 // marker is completely before this run. This might be a marker that sits before the 858 break; 859 default: 860 continue; 861 } 862 863 if (marker->endOffset() <= start()) { 864 // Marker is completely before this run. This might be a marker that sits before the 906 865 // first run we draw, or markers that were within runs we skipped due to truncation. 907 866 continue; 908 909 if (marker->startOffset() > end()) 910 // marker is completely after this run, bail. A later run will paint it. 867 } 868 869 if (marker->startOffset() > end()) { 870 // Marker is completely after this run, bail. A later run will paint it. 911 871 break; 912 913 // marker intersects this run. Paint it. 872 } 873 874 // Marker intersects this run. Collect it. 914 875 switch (marker->type()) { 915 case DocumentMarker::Spelling:916 case DocumentMarker::CorrectionIndicator:917 case DocumentMarker::DictationAlternatives:918 case DocumentMarker::Grammar:876 case DocumentMarker::Spelling: 877 case DocumentMarker::CorrectionIndicator: 878 case DocumentMarker::DictationAlternatives: 879 case DocumentMarker::Grammar: 919 880 #if PLATFORM(IOS) 920 // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.921 case DocumentMarker::DictationPhraseWithAlternatives:881 // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard. 882 case DocumentMarker::DictationPhraseWithAlternatives: 922 883 #endif 923 case DocumentMarker::TextMatch:924 subranges.uncheckedAppend({ marker->startOffset(), marker->endOffset(), markerTypeForSubrangeType(marker->type()), marker });925 break;926 case DocumentMarker::Replacement:927 break;884 case DocumentMarker::TextMatch: 885 subranges.uncheckedAppend({ clampedOffset(marker->startOffset()), clampedOffset(marker->endOffset()), markerTypeForSubrangeType(marker->type()), marker }); 886 break; 887 case DocumentMarker::Replacement: 888 break; 928 889 #if ENABLE(TELEPHONE_NUMBER_DETECTION) 929 case DocumentMarker::TelephoneNumber:930 break;890 case DocumentMarker::TelephoneNumber: 891 break; 931 892 #endif 932 default: 933 ASSERT_NOT_REACHED(); 934 } 935 } 936 937 for (auto& subrange : subdivide(subranges, OverlapStrategy::Frontmost)) { 938 if (subrange.type == MarkerSubrange::TextMatch) 939 paintTextMatchMarker(context, boxOrigin, subrange, subrange.marker->isActiveMatch()); 940 else 941 paintDocumentMarker(context, boxOrigin, subrange); 942 } 893 default: 894 ASSERT_NOT_REACHED(); 895 } 896 } 897 return subranges; 898 } 899 900 FloatPoint InlineTextBox::textOriginFromBoxRect(const FloatRect& boxRect) const 901 { 902 FloatPoint textOrigin { boxRect.x(), boxRect.y() + lineFont().fontMetrics().ascent() }; 903 if (auto* combinedText = this->combinedText()) { 904 if (auto newOrigin = combinedText->computeTextOrigin(boxRect)) 905 textOrigin = newOrigin.value(); 906 } 907 if (isHorizontal()) 908 textOrigin.setY(roundToDevicePixel(LayoutUnit { textOrigin.y() }, renderer().document().deviceScaleFactor())); 909 else 910 textOrigin.setX(roundToDevicePixel(LayoutUnit { textOrigin.x() }, renderer().document().deviceScaleFactor())); 911 return textOrigin; 912 } 913 914 void InlineTextBox::paintMarkerSubranges(GraphicsContext& context, TextPaintPhase phase, const FloatRect& boxRect, const Vector<StyledMarkerSubrange>& subranges, const FloatRect& decorationClipOutRect) 915 { 916 switch (phase) { 917 case TextPaintPhase::Background: 918 for (auto& subrange : subranges) 919 paintTextSubrangeBackground(context, boxRect.location(), subrange.style.backgroundColor, subrange.startOffset, subrange.endOffset); 920 return; 921 case TextPaintPhase::Foreground: 922 for (auto& subrange : subranges) 923 paintTextSubrangeForeground(context, boxRect, subrange); 924 return; 925 case TextPaintPhase::Decoration: 926 for (auto& subrange : subranges) 927 paintTextSubrangeDecoration(context, boxRect, decorationClipOutRect, subrange); 928 return; 929 } 930 } 931 932 void InlineTextBox::paintTextSubrangeBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& color, unsigned clampedStartOffset, unsigned clampedEndOffset) 933 { 934 if (clampedStartOffset >= clampedEndOffset) 935 return; 936 937 GraphicsContextStateSaver stateSaver { context }; 938 updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all! 939 940 // Note that if the text is truncated, we let the thing being painted in the truncation 941 // draw its own highlight. 942 auto text = this->text(); 943 TextRun textRun = createTextRun(text); 944 945 const RootInlineBox& rootBox = root(); 946 LayoutUnit selectionBottom = rootBox.selectionBottom(); 947 LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock(); 948 949 // Use same y positioning and height as for selection, so that when the selection and this subrange are on 950 // the same word there are no pieces sticking out. 951 LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop; 952 LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop); 953 954 LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight); 955 lineFont().adjustSelectionRectForText(textRun, selectionRect, clampedStartOffset, clampedEndOffset); 956 957 // FIXME: Support painting combined text. See <https://bugs.webkit.org/show_bug.cgi?id=180993>. 958 context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color); 959 } 960 961 void InlineTextBox::paintTextSubrangeForeground(GraphicsContext& context, const FloatRect& boxRect, const StyledMarkerSubrange& subrange) 962 { 963 if (subrange.startOffset >= subrange.endOffset) 964 return; 965 966 const FontCascade& font = lineFont(); 967 const RenderStyle& lineStyle = this->lineStyle(); 968 969 float emphasisMarkOffset = 0; 970 bool emphasisMarkAbove; 971 bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove); 972 const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom(); 973 if (!emphasisMark.isEmpty()) 974 emphasisMarkOffset = emphasisMarkAbove ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark); 975 976 TextPainter textPainter { context }; 977 textPainter.setFont(font); 978 textPainter.setStyle(subrange.style.textStyles); 979 textPainter.setIsHorizontal(isHorizontal()); 980 textPainter.setShadow(subrange.style.textShadow); 981 textPainter.setEmphasisMark(emphasisMark, emphasisMarkOffset, combinedText()); 982 983 GraphicsContextStateSaver stateSaver { context, false }; 984 if (subrange.type == MarkerSubrange::DraggedContent) { 985 stateSaver.save(); 986 context.setAlpha(subrange.style.alpha); 987 } 988 // TextPainter wants the box rectangle and text origin of the entire line box. 989 auto text = this->text(); 990 textPainter.paintRange(createTextRun(text), boxRect, textOriginFromBoxRect(boxRect), subrange.startOffset, subrange.endOffset); 991 } 992 993 void InlineTextBox::paintTextSubrangeDecoration(GraphicsContext& context, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkerSubrange& subrange) 994 { 995 if (m_truncation == cFullTruncation) 996 return; 997 998 updateGraphicsContext(context, subrange.style.textStyles); 999 1000 bool isCombinedText = combinedText(); 1001 if (isCombinedText) 1002 context.concatCTM(rotation(boxRect, Clockwise)); 1003 1004 // 1. Compute text selection 1005 unsigned startOffset = subrange.startOffset; 1006 unsigned endOffset = subrange.endOffset; 1007 if (startOffset >= endOffset) 1008 return; 1009 1010 // Note that if the text is truncated, we let the thing being painted in the truncation 1011 // draw its own decoration. 1012 auto text = this->text(); 1013 TextRun textRun = createTextRun(text); 1014 1015 // Avoid measuring the text when the entire line box is selected as an optimization. 1016 FloatRect snappedSelectionRect = boxRect; 1017 if (startOffset || endOffset != textRun.length()) { 1018 LayoutRect selectionRect = { boxRect.x(), boxRect.y(), boxRect.width(), boxRect.height() }; 1019 lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset); 1020 snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()); 1021 } 1022 1023 // 2. Paint 1024 TextDecorationPainter decorationPainter { context, static_cast<unsigned>(lineStyle().textDecorationsInEffect()), renderer(), isFirstLine(), subrange.style.textDecorationStyles }; 1025 decorationPainter.setInlineTextBox(this); 1026 decorationPainter.setFont(lineFont()); 1027 decorationPainter.setWidth(snappedSelectionRect.width()); 1028 decorationPainter.setBaseline(lineStyle().fontMetrics().ascent()); 1029 decorationPainter.setIsHorizontal(isHorizontal()); 1030 decorationPainter.addTextShadow(subrange.style.textShadow); 1031 1032 { 1033 GraphicsContextStateSaver stateSaver { context, false }; 1034 if (!clipOutRect.isEmpty()) { 1035 stateSaver.save(); 1036 context.clipOut(clipOutRect); 1037 } 1038 decorationPainter.paintTextDecoration(textRun.subRun(startOffset, endOffset - startOffset), textOriginFromBoxRect(snappedSelectionRect), snappedSelectionRect.location()); 1039 } 1040 1041 if (isCombinedText) 1042 context.concatCTM(rotation(boxRect, Counterclockwise)); 1043 } 1044 1045 void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin) 1046 { 1047 paintTextSubrangeBackground(context, boxOrigin, clampedOffset(renderer().frame().editor().compositionStart()), clampedOffset(renderer().frame().editor().compositionEnd()), Color::compositionFill); 943 1048 } 944 1049 … … 965 1070 break; // Underline also runs into the next run. Bail now, no more marker advancement. 966 1071 } 1072 } 1073 1074 static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width) 1075 { 1076 if (direction == LTR) 1077 return; 1078 start = logicalWidth - width - start; 967 1079 } 968 1080 -
trunk/Source/WebCore/rendering/InlineTextBox.h
r225712 r226138 152 152 153 153 private: 154 void paintDecoration(GraphicsContext&, const TextRun&, const FloatPoint& textOrigin, const FloatRect& boxRect, 155 TextDecoration, TextPaintStyle, const ShadowData*, const FloatRect& clipOutRect); 156 void paintSelection(GraphicsContext&, const FloatPoint& boxOrigin, const Color&); 157 158 void paintDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&); 159 void paintDocumentMarkers(GraphicsContext&, const FloatPoint& boxOrigin, bool background); 160 void paintTextMatchMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&, bool isActiveMatch); 154 struct MarkerSubrangeStyle; 155 struct StyledMarkerSubrange; 156 157 enum class TextPaintPhase { Background, Foreground, Decoration }; 158 159 Vector<MarkerSubrange> collectSubrangesForDraggedContent(); 160 Vector<MarkerSubrange> collectSubrangesForDocumentMarkers(TextPaintPhase); 161 162 MarkerSubrangeStyle computeStyleForUnmarkedMarkerSubrange(const PaintInfo&) const; 163 StyledMarkerSubrange resolveStyleForSubrange(const MarkerSubrange&, const MarkerSubrangeStyle& baseStyle, const PaintInfo&); 164 Vector<StyledMarkerSubrange> subdivideAndResolveStyle(const Vector<MarkerSubrange>&, const MarkerSubrangeStyle& baseStyle, const PaintInfo&); 165 166 FloatPoint textOriginFromBoxRect(const FloatRect&) const; 167 168 void paintMarkerSubranges(GraphicsContext&, TextPaintPhase, const FloatRect& boxRect, const Vector<StyledMarkerSubrange>&, const FloatRect& decorationClipOutRect = { }); 169 170 void paintPlatformDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&); 171 void paintPlatformDocumentMarkers(GraphicsContext&, const FloatPoint& boxOrigin); 161 172 162 173 void paintCompositionBackground(GraphicsContext&, const FloatPoint& boxOrigin); … … 164 175 void paintCompositionUnderline(GraphicsContext&, const FloatPoint& boxOrigin, const CompositionUnderline&) const; 165 176 166 void paintTextSubrangeBackground(GraphicsContext&, const FloatPoint& boxOrigin, const Color&, unsigned startOffset, unsigned endOffset); 177 void paintTextSubrangeBackground(GraphicsContext&, const FloatPoint& boxOrigin, const Color&, unsigned clampedStartOffset, unsigned clampedEndOffset); 178 void paintTextSubrangeForeground(GraphicsContext&, const FloatRect& boxRect, const StyledMarkerSubrange&); 179 void paintTextSubrangeDecoration(GraphicsContext&, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkerSubrange&); 167 180 168 181 const RenderCombineText* combinedText() const; -
trunk/Source/WebCore/rendering/MarkerSubrange.h
r226137 r226138 46 46 #endif 47 47 Selection, 48 DraggedContent, 48 49 }; 49 50 #if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES) … … 61 62 Type type; 62 63 const RenderedDocumentMarker* marker { nullptr }; 64 65 bool isEmpty() const { return endOffset <= startOffset; } 66 bool operator!=(const MarkerSubrange& other) const { return !(*this == other); } 67 bool operator==(const MarkerSubrange& other) const 68 { 69 return startOffset == other.startOffset && endOffset == other.endOffset && type == other.type && marker == other.marker; 70 } 63 71 }; 64 72 -
trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp
r225117 r226138 118 118 textWithHyphen = run.textWithHyphen(); 119 119 // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout. 120 TextRun textRun (run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior());120 TextRun textRun { run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior() }; 121 121 textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); 122 FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor));123 textPainter.paint(textRun, textRun.length(),rect, textOrigin);122 FloatPoint textOrigin { rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor) }; 123 textPainter.paint(textRun, rect, textOrigin); 124 124 if (textDecorationPainter) { 125 125 textDecorationPainter->setWidth(rect.width()); -
trunk/Source/WebCore/rendering/TextDecorationPainter.cpp
r224780 r226138 242 242 } 243 243 244 TextDecorationPainter::TextDecorationPainter(GraphicsContext& context, unsigned decorations, const RenderText& renderer, bool isFirstLine, PseudoId pseudoId) 244 bool TextDecorationPainter::Styles::operator==(const Styles& other) const 245 { 246 return underlineColor == other.underlineColor && overlineColor == other.overlineColor && linethroughColor == other.linethroughColor 247 && underlineStyle == other.underlineStyle && overlineStyle == other.overlineStyle && linethroughStyle == other.linethroughStyle; 248 } 249 250 TextDecorationPainter::TextDecorationPainter(GraphicsContext& context, unsigned decorations, const RenderText& renderer, bool isFirstLine, std::optional<Styles> styles) 245 251 : m_context { context } 246 252 , m_decorations { OptionSet<TextDecoration>::fromRaw(decorations) } 247 253 , m_wavyOffset { wavyOffsetFromDecoration() } 248 254 , m_isPrinting { renderer.document().printing() } 249 , m_styles { styles ForRenderer(renderer, decorations, isFirstLine, pseudoId) }255 , m_styles { styles ? *WTFMove(styles) : stylesForRenderer(renderer, decorations, isFirstLine, NOPSEUDO) } 250 256 , m_lineStyle { isFirstLine ? renderer.firstLineStyle() : renderer.style() } 251 257 { -
trunk/Source/WebCore/rendering/TextDecorationPainter.h
r222889 r226138 43 43 public: 44 44 // FIXME: Make decorations an OptionSet<TextDecoration>. See <https://bugs.webkit.org/show_bug.cgi?id=176844>. 45 TextDecorationPainter(GraphicsContext&, unsigned decorations, const RenderText&, bool isFirstLine, PseudoId = NOPSEUDO); 45 struct Styles; 46 TextDecorationPainter(GraphicsContext&, unsigned decorations, const RenderText&, bool isFirstLine, std::optional<Styles> = std::nullopt); 46 47 47 48 void setInlineTextBox(const InlineTextBox* inlineTextBox) { m_inlineTextBox = inlineTextBox; } … … 55 56 56 57 struct Styles { 58 bool operator==(const Styles&) const; 59 bool operator!=(const Styles& other) const { return !(*this == other); } 60 57 61 Color underlineColor; 58 62 Color overlineColor; … … 64 68 // FIXME: Make requestedDecorations an OptionSet<TextDecoration>. See <https://bugs.webkit.org/show_bug.cgi?id=176844>. 65 69 static Styles stylesForRenderer(const RenderObject&, unsigned requestedDecorations, bool firstLineStyle = false, PseudoId = NOPSEUDO); 66 70 67 71 private: 68 72 GraphicsContext& m_context; -
trunk/Source/WebCore/rendering/TextPaintStyle.cpp
r224780 r226138 46 46 } 47 47 48 bool TextPaintStyle::operator==(const TextPaintStyle& other) const 49 { 50 return fillColor == other.fillColor && strokeColor == other.strokeColor && emphasisMarkColor == other.emphasisMarkColor 51 && strokeWidth == other.strokeWidth && paintOrder == other.paintOrder && lineJoin == other.lineJoin 52 #if ENABLE(LETTERPRESS) 53 && useLetterpressEffect == other.useLetterpressEffect 54 #endif 55 && lineCap == other.lineCap && miterLimit == other.miterLimit; 56 } 57 48 58 bool textColorIsLegibleAgainstBackgroundColor(const Color& textColor, const Color& backgroundColor) 49 59 { … … 123 133 } 124 134 125 TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle& textPaintStyle, const RenderText& renderer, const RenderStyle& lineStyle, const PaintInfo& paintInfo, bool& paintSelectedTextOnly, bool& paintSelectedTextSeparately, bool& paintNonSelectedTextOnly, const ShadowData*& selectionShadow) 126 { 127 paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection); 128 paintSelectedTextSeparately = paintInfo.paintBehavior & PaintBehaviorExcludeSelection; 129 paintNonSelectedTextOnly = paintInfo.paintBehavior & PaintBehaviorExcludeSelection; 135 TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle& textPaintStyle, const RenderText& renderer, const RenderStyle& lineStyle, const PaintInfo& paintInfo, const ShadowData*& selectionShadow) 136 { 130 137 selectionShadow = (paintInfo.forceTextColor()) ? nullptr : lineStyle.textShadow(); 131 138 … … 134 141 #if ENABLE(TEXT_SELECTION) 135 142 Color foreground = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : renderer.selectionForegroundColor(); 136 if (foreground.isValid() && foreground != selectionPaintStyle.fillColor) { 137 if (!paintSelectedTextOnly) 138 paintSelectedTextSeparately = true; 143 if (foreground.isValid() && foreground != selectionPaintStyle.fillColor) 139 144 selectionPaintStyle.fillColor = foreground; 140 }141 145 142 146 Color emphasisMarkForeground = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : renderer.selectionEmphasisMarkColor(); 143 if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionPaintStyle.emphasisMarkColor) { 144 if (!paintSelectedTextOnly) 145 paintSelectedTextSeparately = true; 147 if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionPaintStyle.emphasisMarkColor) 146 148 selectionPaintStyle.emphasisMarkColor = emphasisMarkForeground; 147 }148 149 149 150 if (auto* pseudoStyle = renderer.getCachedPseudoStyle(SELECTION)) { 150 151 const ShadowData* shadow = paintInfo.forceTextColor() ? nullptr : pseudoStyle->textShadow(); 151 if (shadow != selectionShadow) { 152 if (!paintSelectedTextOnly) 153 paintSelectedTextSeparately = true; 152 if (shadow != selectionShadow) 154 153 selectionShadow = shadow; 155 }156 154 157 155 auto viewportSize = renderer.frame().view() ? renderer.frame().view()->size() : IntSize(); 158 156 float strokeWidth = pseudoStyle->computedStrokeWidth(viewportSize); 159 if (strokeWidth != selectionPaintStyle.strokeWidth) { 160 if (!paintSelectedTextOnly) 161 paintSelectedTextSeparately = true; 157 if (strokeWidth != selectionPaintStyle.strokeWidth) 162 158 selectionPaintStyle.strokeWidth = strokeWidth; 163 }164 159 165 160 Color stroke = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : pseudoStyle->computedStrokeColor(); 166 if (stroke != selectionPaintStyle.strokeColor) { 167 if (!paintSelectedTextOnly) 168 paintSelectedTextSeparately = true; 161 if (stroke != selectionPaintStyle.strokeColor) 169 162 selectionPaintStyle.strokeColor = stroke; 170 }171 163 } 172 164 #else -
trunk/Source/WebCore/rendering/TextPaintStyle.h
r219033 r226138 43 43 TextPaintStyle(const Color&); 44 44 45 bool operator==(const TextPaintStyle&) const; 46 bool operator!=(const TextPaintStyle& other) const { return !(*this == other); } 47 45 48 Color fillColor; 46 49 Color strokeColor; … … 58 61 bool textColorIsLegibleAgainstBackgroundColor(const Color& textColor, const Color& backgroundColor); 59 62 TextPaintStyle computeTextPaintStyle(const Frame&, const RenderStyle&, const PaintInfo&); 60 TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle&, const RenderText&, const RenderStyle&, const PaintInfo&, bool& paintSelectedTextOnly, bool& paintSelectedTextSeparately, bool& paintNonSelectedTextOnly,const ShadowData*& selectionShadow);63 TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle&, const RenderText&, const RenderStyle&, const PaintInfo&, const ShadowData*& selectionShadow); 61 64 62 65 enum FillColorType { UseNormalFillColor, UseEmphasisMarkColor }; -
trunk/Source/WebCore/rendering/TextPainter.cpp
r223552 r226138 181 181 } 182 182 183 void TextPainter::paint(const TextRun& textRun, const FloatRect& boxRect, const FloatPoint& textOrigin) 184 { 185 paintRange(textRun, boxRect, textOrigin, 0, textRun.length()); 186 } 187 183 188 void TextPainter::paintRange(const TextRun& textRun, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned start, unsigned end) 184 189 { … … 190 195 paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, start, end, m_style, m_shadow); 191 196 } 192 193 void TextPainter::paint(const TextRun& textRun, unsigned length, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned selectionStart, unsigned selectionEnd,194 bool paintSelectedTextOnly, bool paintSelectedTextSeparately, bool paintNonSelectedTextOnly)195 {196 ASSERT(m_font);197 ASSERT(length <= textRun.length());198 if (!paintSelectedTextOnly) {199 // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side200 // effect, so only when we know we're stroking, do a save/restore.201 GraphicsContextStateSaver stateSaver(m_context, m_style.strokeWidth > 0);202 updateGraphicsContext(m_context, m_style);203 bool fullPaint = !paintSelectedTextSeparately || selectionEnd <= selectionStart;204 if (fullPaint)205 paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, 0, length, m_style, m_shadow);206 else {207 // Paint the before and after selection parts.208 if (selectionStart > 0)209 paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, 0, selectionStart, m_style, m_shadow);210 if (selectionEnd < length)211 paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, selectionEnd, length, m_style, m_shadow);212 }213 }214 215 if (paintNonSelectedTextOnly)216 return;217 218 // Paint only the text that is selected.219 if ((paintSelectedTextOnly || paintSelectedTextSeparately) && selectionStart < selectionEnd) {220 GraphicsContextStateSaver stateSaver(m_context, m_selectionStyle.strokeWidth > 0);221 updateGraphicsContext(m_context, m_selectionStyle);222 paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, selectionStart, selectionEnd, m_selectionStyle, m_selectionShadow);223 }224 }225 197 226 198 } // namespace WebCore -
trunk/Source/WebCore/rendering/TextPainter.h
r223552 r226138 49 49 public: 50 50 TextPainter(GraphicsContext&); 51 51 52 52 void setStyle(const TextPaintStyle& textPaintStyle) { m_style = textPaintStyle; } 53 void setSelectionStyle(const TextPaintStyle& selectionPaintStyle) { m_selectionStyle = selectionPaintStyle; }54 55 53 void setShadow(const ShadowData* shadow) { m_shadow = shadow; } 56 void setSelectionShadow(const ShadowData* selectionShadow) { m_selectionShadow = selectionShadow; }57 58 54 void setFont(const FontCascade& font) { m_font = &font; } 59 60 55 void setIsHorizontal(bool isHorizontal) { m_textBoxIsHorizontal = isHorizontal; } 61 62 56 void setEmphasisMark(const AtomicString& mark, float offset, const RenderCombineText*); 63 57 58 void paint(const TextRun&, const FloatRect& boxRect, const FloatPoint& textOrigin); 64 59 void paintRange(const TextRun&, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned start, unsigned end); 65 void paint(const TextRun&, unsigned length, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned selectionStart = 0, unsigned selectionEnd = 0, bool paintSelectedTextOnly = false, bool paintSelectedTextSeparately = false, bool paintNonSelectedTextOnly = false);66 60 67 61 private: … … 76 70 const FontCascade* m_font { nullptr }; 77 71 TextPaintStyle m_style; 78 TextPaintStyle m_selectionStyle;79 72 const ShadowData* m_shadow { nullptr }; 80 const ShadowData* m_selectionShadow { nullptr };81 73 AtomicString m_emphasisMark; 82 74 const RenderCombineText* m_combinedText { nullptr }; -
trunk/Tools/ChangeLog
r226137 r226138 1 2017-12-19 Daniel Bates <dabates@apple.com> 2 3 Implement InlineTextBox painting using marker subranges 4 https://bugs.webkit.org/show_bug.cgi?id=180984 5 <rdar://problem/36139364> 6 7 Reviewed by David Hyatt. 8 9 Update unit tests now that we use subranges for the painting of dragged content. 10 11 * TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp: 12 (WebCore::operator<<): 13 (WebCore::operator==): Deleted; moved to class MarkerSubrange. 14 1 15 2017-12-19 Daniel Bates <dabates@apple.com> 2 16 -
trunk/Tools/TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp
r226137 r226138 46 46 return os << "DictationPhraseWithAlternatives"; 47 47 #endif 48 case MarkerSubrange::DraggedContent: 49 return os << "DraggedContent"; 48 50 case MarkerSubrange::GrammarError: 49 51 return os << "GrammarError"; … … 67 69 } 68 70 69 bool operator==(const MarkerSubrange& a, const MarkerSubrange& b)70 {71 return a.startOffset == b.startOffset && a.endOffset == b.endOffset && a.type == b.type && a.marker == b.marker;72 }73 74 71 } 75 72
Note:
See TracChangeset
for help on using the changeset viewer.