Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/paint/InlineTextBoxPainter.h" | 5 #include "core/paint/InlineTextBoxPainter.h" |
| 6 | 6 |
| 7 #include "core/editing/CompositionUnderline.h" | 7 #include "core/editing/CompositionUnderline.h" |
| 8 #include "core/editing/Editor.h" | 8 #include "core/editing/Editor.h" |
| 9 #include "core/editing/markers/DocumentMarkerController.h" | 9 #include "core/editing/markers/DocumentMarkerController.h" |
| 10 #include "core/frame/LocalFrame.h" | 10 #include "core/frame/LocalFrame.h" |
| 11 #include "core/layout/LayoutTextCombine.h" | 11 #include "core/layout/LayoutTextCombine.h" |
| 12 #include "core/layout/LayoutTheme.h" | 12 #include "core/layout/LayoutTheme.h" |
| 13 #include "core/layout/api/LineLayoutAPIShim.h" | 13 #include "core/layout/api/LineLayoutAPIShim.h" |
| 14 #include "core/layout/api/LineLayoutBox.h" | 14 #include "core/layout/api/LineLayoutBox.h" |
| 15 #include "core/layout/line/InlineTextBox.h" | 15 #include "core/layout/line/InlineTextBox.h" |
| 16 #include "core/paint/PaintInfo.h" | 16 #include "core/paint/PaintInfo.h" |
| 17 #include "core/paint/TextPainter.h" | 17 #include "core/paint/TextPainter.h" |
| 18 #include "core/style/AppliedTextDecoration.h" | |
| 18 #include "platform/graphics/GraphicsContextStateSaver.h" | 19 #include "platform/graphics/GraphicsContextStateSaver.h" |
| 19 #include "platform/graphics/paint/DrawingRecorder.h" | 20 #include "platform/graphics/paint/DrawingRecorder.h" |
| 20 #include "wtf/Optional.h" | 21 #include "wtf/Optional.h" |
| 21 | 22 |
| 22 namespace blink { | 23 namespace blink { |
| 23 | 24 |
| 24 typedef WTF::HashMap<const InlineTextBox*, TextBlobPtr> | 25 typedef WTF::HashMap<const InlineTextBox*, TextBlobPtr> |
| 25 InlineTextBoxBlobCacheMap; | 26 InlineTextBoxBlobCacheMap; |
| 26 static InlineTextBoxBlobCacheMap* gTextBlobCache; | 27 static InlineTextBoxBlobCacheMap* gTextBlobCache; |
| 27 | 28 |
| (...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 277 // paint only the text that is selected | 278 // paint only the text that is selected |
| 278 bool textBlobIsCacheable = selectionStart == 0 && selectionEnd == length; | 279 bool textBlobIsCacheable = selectionStart == 0 && selectionEnd == length; |
| 279 TextBlobPtr* cachedTextBlob = 0; | 280 TextBlobPtr* cachedTextBlob = 0; |
| 280 if (textBlobIsCacheable) | 281 if (textBlobIsCacheable) |
| 281 cachedTextBlob = addToTextBlobCache(m_inlineTextBox); | 282 cachedTextBlob = addToTextBlobCache(m_inlineTextBox); |
| 282 textPainter.paint(selectionStart, selectionEnd, length, selectionStyle, | 283 textPainter.paint(selectionStart, selectionEnd, length, selectionStyle, |
| 283 cachedTextBlob); | 284 cachedTextBlob); |
| 284 } | 285 } |
| 285 | 286 |
| 286 // Paint decorations | 287 // Paint decorations |
| 287 TextDecoration textDecorations = styleToUse.textDecorationsInEffect(); | 288 if (styleToUse.textDecorationsInEffect() != TextDecorationNone && |
| 288 if (textDecorations != TextDecorationNone && !paintSelectedTextOnly) { | 289 !paintSelectedTextOnly) { |
| 289 GraphicsContextStateSaver stateSaver(context, false); | 290 GraphicsContextStateSaver stateSaver(context, false); |
| 290 TextPainter::updateGraphicsContext( | 291 TextPainter::updateGraphicsContext( |
| 291 context, textStyle, m_inlineTextBox.isHorizontal(), stateSaver); | 292 context, textStyle, m_inlineTextBox.isHorizontal(), stateSaver); |
| 292 if (combinedText) | 293 if (combinedText) |
| 293 context.concatCTM(TextPainter::rotation(boxRect, TextPainter::Clockwise)); | 294 context.concatCTM(TextPainter::rotation(boxRect, TextPainter::Clockwise)); |
| 294 paintDecoration(paintInfo, boxOrigin, textDecorations); | 295 paintDecorations(paintInfo, boxOrigin, styleToUse.appliedTextDecorations()); |
| 295 if (combinedText) | 296 if (combinedText) |
| 296 context.concatCTM( | 297 context.concatCTM( |
| 297 TextPainter::rotation(boxRect, TextPainter::Counterclockwise)); | 298 TextPainter::rotation(boxRect, TextPainter::Counterclockwise)); |
| 298 } | 299 } |
| 299 | 300 |
| 300 if (paintInfo.phase == PaintPhaseForeground) | 301 if (paintInfo.phase == PaintPhaseForeground) |
| 301 paintDocumentMarkers(paintInfo, boxOrigin, styleToUse, font, | 302 paintDocumentMarkers(paintInfo, boxOrigin, styleToUse, font, |
| 302 DocumentMarkerPaintPhase::Foreground); | 303 DocumentMarkerPaintPhase::Foreground); |
| 303 | 304 |
| 304 if (shouldRotate) | 305 if (shouldRotate) |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 714 if (offset > 0) | 715 if (offset > 0) |
| 715 return (inlineTextBox->logicalHeight() + gap + offset).toInt(); | 716 return (inlineTextBox->logicalHeight() + gap + offset).toInt(); |
| 716 return (inlineTextBox->logicalHeight() + gap).toInt(); | 717 return (inlineTextBox->logicalHeight() + gap).toInt(); |
| 717 } | 718 } |
| 718 } | 719 } |
| 719 | 720 |
| 720 ASSERT_NOT_REACHED(); | 721 ASSERT_NOT_REACHED(); |
| 721 return fontMetrics.ascent() + gap; | 722 return fontMetrics.ascent() + gap; |
| 722 } | 723 } |
| 723 | 724 |
| 724 static bool shouldSetDecorationAntialias(TextDecorationStyle decorationStyle) { | 725 static bool shouldSetDecorationAntialias( |
| 725 return decorationStyle == TextDecorationStyleDotted || | 726 const Vector<AppliedTextDecoration>& decorations) { |
| 726 decorationStyle == TextDecorationStyleDashed; | 727 for (const AppliedTextDecoration& decoration : decorations) { |
| 727 } | 728 TextDecorationStyle decorationStyle = decoration.style(); |
| 728 | 729 if (decorationStyle == TextDecorationStyleDotted || |
| 729 static bool shouldSetDecorationAntialias(TextDecorationStyle underline, | 730 decorationStyle == TextDecorationStyleDashed) |
| 730 TextDecorationStyle overline, | 731 return true; |
| 731 TextDecorationStyle linethrough) { | 732 } |
| 732 return shouldSetDecorationAntialias(underline) || | 733 return false; |
| 733 shouldSetDecorationAntialias(overline) || | |
| 734 shouldSetDecorationAntialias(linethrough); | |
| 735 } | 734 } |
| 736 | 735 |
| 737 static StrokeStyle textDecorationStyleToStrokeStyle( | 736 static StrokeStyle textDecorationStyleToStrokeStyle( |
| 738 TextDecorationStyle decorationStyle) { | 737 TextDecorationStyle decorationStyle) { |
| 739 StrokeStyle strokeStyle = SolidStroke; | 738 StrokeStyle strokeStyle = SolidStroke; |
| 740 switch (decorationStyle) { | 739 switch (decorationStyle) { |
| 741 case TextDecorationStyleSolid: | 740 case TextDecorationStyleSolid: |
| 742 strokeStyle = SolidStroke; | 741 strokeStyle = SolidStroke; |
| 743 break; | 742 break; |
| 744 case TextDecorationStyleDouble: | 743 case TextDecorationStyleDouble: |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 878 controlPoint2.setX(x + step); | 877 controlPoint2.setX(x + step); |
| 879 x += 2 * step; | 878 x += 2 * step; |
| 880 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis)); | 879 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis)); |
| 881 } | 880 } |
| 882 } | 881 } |
| 883 | 882 |
| 884 context.setShouldAntialias(true); | 883 context.setShouldAntialias(true); |
| 885 context.strokePath(path); | 884 context.strokePath(path); |
| 886 } | 885 } |
| 887 | 886 |
| 888 static void paintAppliedDecoration( | 887 static void paintAppliedDecoration(GraphicsContext& context, |
| 889 GraphicsContext& context, | 888 FloatPoint start, |
| 890 FloatPoint start, | 889 float width, |
| 891 float width, | 890 float doubleOffset, |
| 892 float doubleOffset, | 891 int wavyOffsetFactor, |
| 893 int wavyOffsetFactor, | 892 AppliedTextDecoration decoration, |
| 894 LayoutObject::AppliedTextDecoration decoration, | 893 float thickness, |
| 895 float thickness, | 894 bool antialiasDecoration, |
| 896 bool antialiasDecoration, | 895 bool isPrinting) { |
| 897 bool isPrinting) { | 896 context.setStrokeStyle(textDecorationStyleToStrokeStyle(decoration.style())); |
| 898 context.setStrokeStyle(textDecorationStyleToStrokeStyle(decoration.style)); | 897 context.setStrokeColor(decoration.color()); |
| 899 context.setStrokeColor(decoration.color); | |
| 900 | 898 |
| 901 switch (decoration.style) { | 899 switch (decoration.style()) { |
| 902 case TextDecorationStyleWavy: | 900 case TextDecorationStyleWavy: |
| 903 strokeWavyTextDecoration( | 901 strokeWavyTextDecoration( |
| 904 context, start + FloatPoint(0, doubleOffset * wavyOffsetFactor), | 902 context, start + FloatPoint(0, doubleOffset * wavyOffsetFactor), |
| 905 start + FloatPoint(width, doubleOffset * wavyOffsetFactor), | 903 start + FloatPoint(width, doubleOffset * wavyOffsetFactor), |
| 906 thickness); | 904 thickness); |
| 907 break; | 905 break; |
| 908 case TextDecorationStyleDotted: | 906 case TextDecorationStyleDotted: |
| 909 case TextDecorationStyleDashed: | 907 case TextDecorationStyleDashed: |
| 910 context.setShouldAntialias(antialiasDecoration); | 908 context.setShouldAntialias(antialiasDecoration); |
| 911 // Fall through | 909 // Fall through |
| 912 default: | 910 default: |
| 913 context.drawLineForText(FloatPoint(start), width, isPrinting); | 911 context.drawLineForText(FloatPoint(start), width, isPrinting); |
| 914 | 912 |
| 915 if (decoration.style == TextDecorationStyleDouble) | 913 if (decoration.style() == TextDecorationStyleDouble) |
| 916 context.drawLineForText(start + FloatPoint(0, doubleOffset), width, | 914 context.drawLineForText(start + FloatPoint(0, doubleOffset), width, |
| 917 isPrinting); | 915 isPrinting); |
| 918 } | 916 } |
| 919 } | 917 } |
| 920 | 918 |
| 921 void InlineTextBoxPainter::paintDecoration(const PaintInfo& paintInfo, | 919 void InlineTextBoxPainter::paintDecorations( |
| 922 const LayoutPoint& boxOrigin, | 920 const PaintInfo& paintInfo, |
| 923 TextDecoration deco) { | 921 const LayoutPoint& boxOrigin, |
| 922 const Vector<AppliedTextDecoration>& decorations) { | |
| 924 if (m_inlineTextBox.truncation() == cFullTruncation) | 923 if (m_inlineTextBox.truncation() == cFullTruncation) |
| 925 return; | 924 return; |
| 926 | 925 |
| 927 GraphicsContext& context = paintInfo.context; | 926 GraphicsContext& context = paintInfo.context; |
| 928 GraphicsContextStateSaver stateSaver(context); | 927 GraphicsContextStateSaver stateSaver(context); |
| 929 | 928 |
| 930 LayoutPoint localOrigin(boxOrigin); | 929 LayoutPoint localOrigin(boxOrigin); |
| 931 | 930 |
| 932 LayoutUnit width = m_inlineTextBox.logicalWidth(); | 931 LayoutUnit width = m_inlineTextBox.logicalWidth(); |
| 933 if (m_inlineTextBox.truncation() != cNoTruncation) { | 932 if (m_inlineTextBox.truncation() != cNoTruncation) { |
| 934 bool ltr = m_inlineTextBox.isLeftToRightDirection(); | 933 bool ltr = m_inlineTextBox.isLeftToRightDirection(); |
| 935 bool flowIsLTR = | 934 bool flowIsLTR = |
| 936 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); | 935 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); |
| 937 width = LayoutUnit(m_inlineTextBox.getLineLayoutItem().width( | 936 width = LayoutUnit(m_inlineTextBox.getLineLayoutItem().width( |
| 938 ltr == flowIsLTR ? m_inlineTextBox.start() | 937 ltr == flowIsLTR ? m_inlineTextBox.start() |
| 939 : m_inlineTextBox.truncation(), | 938 : m_inlineTextBox.truncation(), |
| 940 ltr == flowIsLTR ? m_inlineTextBox.truncation() | 939 ltr == flowIsLTR ? m_inlineTextBox.truncation() |
| 941 : m_inlineTextBox.len() - m_inlineTextBox.truncation(), | 940 : m_inlineTextBox.len() - m_inlineTextBox.truncation(), |
| 942 m_inlineTextBox.textPos(), flowIsLTR ? LTR : RTL, | 941 m_inlineTextBox.textPos(), flowIsLTR ? LTR : RTL, |
| 943 m_inlineTextBox.isFirstLineStyle())); | 942 m_inlineTextBox.isFirstLineStyle())); |
| 944 if (!flowIsLTR) | 943 if (!flowIsLTR) |
| 945 localOrigin.move(m_inlineTextBox.logicalWidth() - width, LayoutUnit()); | 944 localOrigin.move(m_inlineTextBox.logicalWidth() - width, LayoutUnit()); |
| 946 } | 945 } |
| 947 | 946 |
| 948 // Get the text decoration colors. | |
| 949 LayoutObject::AppliedTextDecoration underline, overline, linethrough; | |
| 950 LayoutObject& textBoxLayoutObject = inlineLayoutObject(); | |
| 951 textBoxLayoutObject.getTextDecorations(deco, underline, overline, linethrough, | |
| 952 true); | |
| 953 if (m_inlineTextBox.isFirstLineStyle()) | |
| 954 textBoxLayoutObject.getTextDecorations(deco, underline, overline, | |
| 955 linethrough, true, true); | |
| 956 | |
| 957 // Use a special function for underlines to get the positioning exactly right. | 947 // Use a special function for underlines to get the positioning exactly right. |
| 958 bool isPrinting = paintInfo.isPrinting(); | 948 bool isPrinting = paintInfo.isPrinting(); |
| 959 | 949 |
| 950 LayoutObject& textBoxLayoutObject = inlineLayoutObject(); | |
| 960 const ComputedStyle& styleToUse = | 951 const ComputedStyle& styleToUse = |
| 961 textBoxLayoutObject.styleRef(m_inlineTextBox.isFirstLineStyle()); | 952 textBoxLayoutObject.styleRef(m_inlineTextBox.isFirstLineStyle()); |
| 962 const SimpleFontData* fontData = styleToUse.font().primaryFont(); | 953 const SimpleFontData* fontData = styleToUse.font().primaryFont(); |
| 963 DCHECK(fontData); | 954 DCHECK(fontData); |
| 964 float baseline = fontData ? fontData->getFontMetrics().ascent() : 0; | 955 float baseline = fontData ? fontData->getFontMetrics().ascent() : 0; |
| 965 | 956 |
| 966 // Set the thick of the line to be 10% (or something else ?)of the computed | 957 // Set the thick of the line to be 10% (or something else ?)of the computed |
| 967 // font size and not less than 1px. Using computedFontSize should take care | 958 // font size and not less than 1px. Using computedFontSize should take care |
| 968 // of zoom as well. | 959 // of zoom as well. |
| 969 | 960 |
| 970 // Update Underline thickness, in case we have Faulty Font Metrics calculating | 961 // Update Underline thickness, in case we have Faulty Font Metrics calculating |
| 971 // underline thickness by old method. | 962 // underline thickness by old method. |
| 972 float textDecorationThickness = 0.0; | 963 float textDecorationThickness = 0.0; |
| 973 int fontHeightInt = 0; | 964 int fontHeightInt = 0; |
| 974 if (fontData) { | 965 if (fontData) { |
| 975 textDecorationThickness = fontData->getFontMetrics().underlineThickness(); | 966 textDecorationThickness = fontData->getFontMetrics().underlineThickness(); |
| 976 fontHeightInt = (int)(fontData->getFontMetrics().floatHeight() + 0.5); | 967 fontHeightInt = (int)(fontData->getFontMetrics().floatHeight() + 0.5); |
| 977 } | 968 } |
| 978 if ((textDecorationThickness == 0.f) || | 969 if ((textDecorationThickness == 0.f) || |
| 979 (textDecorationThickness >= (fontHeightInt >> 1))) | 970 (textDecorationThickness >= (fontHeightInt >> 1))) |
| 980 textDecorationThickness = | 971 textDecorationThickness = |
| 981 std::max(1.f, styleToUse.computedFontSize() / 10.f); | 972 std::max(1.f, styleToUse.computedFontSize() / 10.f); |
| 982 | 973 |
| 983 context.setStrokeThickness(textDecorationThickness); | 974 context.setStrokeThickness(textDecorationThickness); |
| 984 | 975 |
| 985 bool antialiasDecoration = shouldSetDecorationAntialias( | 976 bool antialiasDecoration = shouldSetDecorationAntialias(decorations); |
| 986 overline.style, underline.style, linethrough.style); | |
| 987 | 977 |
| 988 // Offset between lines - always non-zero, so lines never cross each other. | 978 // Offset between lines - always non-zero, so lines never cross each other. |
| 989 float doubleOffset = textDecorationThickness + 1.f; | 979 float doubleOffset = textDecorationThickness + 1.f; |
| 990 | 980 |
| 991 if ((deco & TextDecorationUnderline) && fontData) { | 981 for (const AppliedTextDecoration& decoration : decorations) { |
|
Timothy Loh
2016/11/17 15:09:42
I don't think you meant to drop the fontData check
| |
| 992 const int underlineOffset = computeUnderlineOffset( | 982 TextDecoration lines = decoration.lines(); |
| 993 styleToUse.getTextUnderlinePosition(), fontData->getFontMetrics(), | 983 if (lines & TextDecorationUnderline) { |
| 994 &m_inlineTextBox, textDecorationThickness); | 984 const int underlineOffset = computeUnderlineOffset( |
| 995 paintAppliedDecoration( | 985 styleToUse.getTextUnderlinePosition(), fontData->getFontMetrics(), |
| 996 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), | 986 &m_inlineTextBox, textDecorationThickness); |
| 997 width.toFloat(), doubleOffset, 1, underline, textDecorationThickness, | 987 paintAppliedDecoration( |
| 998 antialiasDecoration, isPrinting); | 988 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), |
| 999 } | 989 width.toFloat(), doubleOffset, 1, decoration, textDecorationThickness, |
| 1000 if (deco & TextDecorationOverline) { | 990 antialiasDecoration, isPrinting); |
| 1001 paintAppliedDecoration(context, FloatPoint(localOrigin), width.toFloat(), | 991 } |
| 1002 -doubleOffset, 1, overline, textDecorationThickness, | 992 if (lines & TextDecorationOverline) { |
| 1003 antialiasDecoration, isPrinting); | 993 paintAppliedDecoration( |
| 1004 } | 994 context, FloatPoint(localOrigin), width.toFloat(), -doubleOffset, 1, |
| 1005 if (deco & TextDecorationLineThrough) { | 995 decoration, textDecorationThickness, antialiasDecoration, isPrinting); |
| 1006 const float lineThroughOffset = 2 * baseline / 3; | 996 } |
| 1007 paintAppliedDecoration( | 997 if (lines & TextDecorationLineThrough) { |
| 1008 context, FloatPoint(localOrigin) + FloatPoint(0, lineThroughOffset), | 998 const float lineThroughOffset = 2 * baseline / 3; |
| 1009 width.toFloat(), doubleOffset, 0, linethrough, textDecorationThickness, | 999 paintAppliedDecoration( |
| 1010 antialiasDecoration, isPrinting); | 1000 context, FloatPoint(localOrigin) + FloatPoint(0, lineThroughOffset), |
| 1001 width.toFloat(), doubleOffset, 0, decoration, textDecorationThickness, | |
| 1002 antialiasDecoration, isPrinting); | |
| 1003 } | |
| 1011 } | 1004 } |
| 1012 } | 1005 } |
| 1013 | 1006 |
| 1014 void InlineTextBoxPainter::paintCompositionUnderline( | 1007 void InlineTextBoxPainter::paintCompositionUnderline( |
| 1015 GraphicsContext& context, | 1008 GraphicsContext& context, |
| 1016 const LayoutPoint& boxOrigin, | 1009 const LayoutPoint& boxOrigin, |
| 1017 const CompositionUnderline& underline) { | 1010 const CompositionUnderline& underline) { |
| 1018 if (underline.color() == Color::transparent) | 1011 if (underline.color() == Color::transparent) |
| 1019 return; | 1012 return; |
| 1020 | 1013 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1158 GraphicsContextStateSaver stateSaver(context); | 1151 GraphicsContextStateSaver stateSaver(context); |
| 1159 | 1152 |
| 1160 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), | 1153 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), |
| 1161 m_inlineTextBox.logicalHeight())); | 1154 m_inlineTextBox.logicalHeight())); |
| 1162 context.clip(FloatRect(boxRect)); | 1155 context.clip(FloatRect(boxRect)); |
| 1163 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), | 1156 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), |
| 1164 boxRect.height().toInt(), color, sPos, ePos); | 1157 boxRect.height().toInt(), color, sPos, ePos); |
| 1165 } | 1158 } |
| 1166 | 1159 |
| 1167 } // namespace blink | 1160 } // namespace blink |
| OLD | NEW |