| 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" |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 const InlineTextBox& textBox) { | 29 const InlineTextBox& textBox) { |
| 30 const unsigned startOffset = marker.startOffset() > textBox.start() | 30 const unsigned startOffset = marker.startOffset() > textBox.start() |
| 31 ? marker.startOffset() - textBox.start() | 31 ? marker.startOffset() - textBox.start() |
| 32 : 0U; | 32 : 0U; |
| 33 const unsigned endOffset = | 33 const unsigned endOffset = |
| 34 std::min(marker.endOffset() - textBox.start(), textBox.len()); | 34 std::min(marker.endOffset() - textBox.start(), textBox.len()); |
| 35 return std::make_pair(startOffset, endOffset); | 35 return std::make_pair(startOffset, endOffset); |
| 36 } | 36 } |
| 37 } | 37 } |
| 38 | 38 |
| 39 enum class ResolvedUnderlinePosition { Roman, Under, Over }; |
| 40 |
| 41 static ResolvedUnderlinePosition resolveUnderlinePosition( |
| 42 const ComputedStyle& style, |
| 43 const InlineTextBox* inlineTextBox) { |
| 44 // |auto| should resolve to |under| to avoid drawing through glyphs in |
| 45 // scripts where it would not be appropriate (e.g., ideographs.) |
| 46 // However, this has performance implications. For now, we only work with |
| 47 // vertical text. |
| 48 switch (inlineTextBox->root().baselineType()) { |
| 49 default: |
| 50 NOTREACHED(); |
| 51 // Fall though. |
| 52 case AlphabeticBaseline: |
| 53 switch (style.getTextUnderlinePosition()) { |
| 54 default: |
| 55 NOTREACHED(); |
| 56 // Fall though. |
| 57 case TextUnderlinePositionAuto: |
| 58 return ResolvedUnderlinePosition::Roman; |
| 59 case TextUnderlinePositionUnder: |
| 60 return ResolvedUnderlinePosition::Under; |
| 61 } |
| 62 break; |
| 63 case IdeographicBaseline: |
| 64 // Compute language-appropriate default underline position. |
| 65 // https://drafts.csswg.org/css-text-decor-3/#default-stylesheet |
| 66 UScriptCode script = style.getFontDescription().script(); |
| 67 if (script == USCRIPT_KATAKANA_OR_HIRAGANA || script == USCRIPT_HANGUL) |
| 68 return ResolvedUnderlinePosition::Over; |
| 69 return ResolvedUnderlinePosition::Under; |
| 70 } |
| 71 } |
| 72 |
| 39 static LineLayoutItem enclosingUnderlineObject( | 73 static LineLayoutItem enclosingUnderlineObject( |
| 40 const InlineTextBox* inlineTextBox) { | 74 const InlineTextBox* inlineTextBox) { |
| 41 bool firstLine = inlineTextBox->isFirstLineStyle(); | 75 bool firstLine = inlineTextBox->isFirstLineStyle(); |
| 42 for (LineLayoutItem current = inlineTextBox->parent()->getLineLayoutItem(); | 76 for (LineLayoutItem current = inlineTextBox->parent()->getLineLayoutItem(); |
| 43 ;) { | 77 ;) { |
| 44 if (current.isLayoutBlock()) | 78 if (current.isLayoutBlock()) |
| 45 return current; | 79 return current; |
| 46 if (!current.isLayoutInline() || current.isRubyText()) | 80 if (!current.isLayoutInline() || current.isRubyText()) |
| 47 return nullptr; | 81 return nullptr; |
| 48 | 82 |
| 49 const ComputedStyle& styleToUse = current.styleRef(firstLine); | 83 const ComputedStyle& styleToUse = current.styleRef(firstLine); |
| 50 if (styleToUse.getTextDecoration() & TextDecorationUnderline) | 84 if (styleToUse.getTextDecoration() & TextDecorationUnderline) |
| 51 return current; | 85 return current; |
| 52 | 86 |
| 53 current = current.parent(); | 87 current = current.parent(); |
| 54 if (!current) | 88 if (!current) |
| 55 return current; | 89 return current; |
| 56 | 90 |
| 57 if (Node* node = current.node()) { | 91 if (Node* node = current.node()) { |
| 58 if (isHTMLAnchorElement(node) || node->hasTagName(HTMLNames::fontTag)) | 92 if (isHTMLAnchorElement(node) || node->hasTagName(HTMLNames::fontTag)) |
| 59 return current; | 93 return current; |
| 60 } | 94 } |
| 61 } | 95 } |
| 62 } | 96 } |
| 63 | 97 |
| 64 static LayoutUnit computeUnderlineOffsetForUnder( | 98 static int computeUnderlineOffsetForUnder(const ComputedStyle& style, |
| 65 const ComputedStyle& style, | 99 const InlineTextBox* inlineTextBox, |
| 66 const InlineTextBox* inlineTextBox) { | 100 bool isOverline = false) { |
| 67 const RootInlineBox& root = inlineTextBox->root(); | 101 const RootInlineBox& root = inlineTextBox->root(); |
| 68 LineLayoutItem decorationObject = enclosingUnderlineObject(inlineTextBox); | 102 LineLayoutItem decorationObject = enclosingUnderlineObject(inlineTextBox); |
| 103 LayoutUnit offset; |
| 69 if (style.isFlippedLinesWritingMode()) { | 104 if (style.isFlippedLinesWritingMode()) { |
| 70 LayoutUnit position = inlineTextBox->logicalTop(); | 105 LayoutUnit position = inlineTextBox->logicalTop(); |
| 71 return position - | 106 offset = |
| 72 root.minLogicalTopForUnderline(decorationObject, position); | 107 position - root.minLogicalTopForUnderline(decorationObject, position); |
| 73 } else { | 108 } else { |
| 74 LayoutUnit position = inlineTextBox->logicalBottom(); | 109 LayoutUnit position = inlineTextBox->logicalBottom(); |
| 75 return root.maxLogicalBottomForUnderline(decorationObject, position) - | 110 offset = root.maxLogicalBottomForUnderline(decorationObject, position) - |
| 76 position; | 111 position; |
| 77 } | 112 } |
| 113 if (isOverline) |
| 114 return std::min(-offset, LayoutUnit()).toInt(); |
| 115 return (inlineTextBox->logicalHeight() + std::max(offset, LayoutUnit())) |
| 116 .toInt(); |
| 117 } |
| 118 |
| 119 static int computeOverlineOffset(const ComputedStyle& style, |
| 120 const InlineTextBox* inlineTextBox) { |
| 121 return computeUnderlineOffsetForUnder(style, inlineTextBox, true); |
| 78 } | 122 } |
| 79 | 123 |
| 80 static int computeUnderlineOffsetForRoman(const FontMetrics& fontMetrics, | 124 static int computeUnderlineOffsetForRoman(const FontMetrics& fontMetrics, |
| 81 const float textDecorationThickness) { | 125 const float textDecorationThickness) { |
| 82 // Compute the gap between the font and the underline. Use at least one | 126 // Compute the gap between the font and the underline. Use at least one |
| 83 // pixel gap, if underline is thick then use a bigger gap. | 127 // pixel gap, if underline is thick then use a bigger gap. |
| 84 int gap = 0; | 128 int gap = 0; |
| 85 | 129 |
| 86 // Underline position of zero means draw underline on Baseline Position, | 130 // Underline position of zero means draw underline on Baseline Position, |
| 87 // in Blink we need at least 1-pixel gap to adding following check. | 131 // in Blink we need at least 1-pixel gap to adding following check. |
| 88 // Positive underline Position means underline should be drawn above baseline | 132 // Positive underline Position means underline should be drawn above baseline |
| 89 // and negative value means drawing below baseline, negating the value as in | 133 // and negative value means drawing below baseline, negating the value as in |
| 90 // Blink downward Y-increases. | 134 // Blink downward Y-increases. |
| 91 | 135 |
| 92 if (fontMetrics.underlinePosition()) | 136 if (fontMetrics.underlinePosition()) |
| 93 gap = -fontMetrics.underlinePosition(); | 137 gap = -fontMetrics.underlinePosition(); |
| 94 else | 138 else |
| 95 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f)); | 139 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f)); |
| 96 | 140 |
| 97 // Position underline near the alphabetic baseline. | 141 // Position underline near the alphabetic baseline. |
| 98 return fontMetrics.ascent() + gap; | 142 return fontMetrics.ascent() + gap; |
| 99 } | 143 } |
| 100 | 144 |
| 101 static int computeUnderlineOffset(const ComputedStyle& style, | 145 static int computeUnderlineOffset(ResolvedUnderlinePosition underlinePosition, |
| 146 const ComputedStyle& style, |
| 102 const FontMetrics& fontMetrics, | 147 const FontMetrics& fontMetrics, |
| 103 const InlineTextBox* inlineTextBox, | 148 const InlineTextBox* inlineTextBox, |
| 104 const float textDecorationThickness) { | 149 const float textDecorationThickness) { |
| 105 // FIXME: We support only horizontal text for now. | 150 switch (underlinePosition) { |
| 106 switch (style.getTextUnderlinePosition()) { | |
| 107 default: | 151 default: |
| 108 NOTREACHED(); | 152 NOTREACHED(); |
| 109 // Fall through. | 153 // Fall through. |
| 110 case TextUnderlinePositionAuto: | 154 case ResolvedUnderlinePosition::Roman: |
| 111 return computeUnderlineOffsetForRoman(fontMetrics, | 155 return computeUnderlineOffsetForRoman(fontMetrics, |
| 112 textDecorationThickness); | 156 textDecorationThickness); |
| 113 case TextUnderlinePositionUnder: { | 157 case ResolvedUnderlinePosition::Under: |
| 114 // Position underline at the under edge of the lowest element's | 158 // Position underline at the under edge of the lowest element's |
| 115 // content box. | 159 // content box. |
| 116 LayoutUnit offset = computeUnderlineOffsetForUnder(style, inlineTextBox); | 160 return computeUnderlineOffsetForUnder(style, inlineTextBox); |
| 117 offset = inlineTextBox->logicalHeight() + std::max(offset, LayoutUnit()); | |
| 118 return offset.toInt(); | |
| 119 } | |
| 120 } | 161 } |
| 121 } | 162 } |
| 122 | 163 |
| 123 static bool shouldSetDecorationAntialias( | 164 static bool shouldSetDecorationAntialias( |
| 124 const Vector<AppliedTextDecoration>& decorations) { | 165 const Vector<AppliedTextDecoration>& decorations) { |
| 125 for (const AppliedTextDecoration& decoration : decorations) { | 166 for (const AppliedTextDecoration& decoration : decorations) { |
| 126 TextDecorationStyle decorationStyle = decoration.style(); | 167 TextDecorationStyle decorationStyle = decoration.style(); |
| 127 if (decorationStyle == TextDecorationStyleDotted || | 168 if (decorationStyle == TextDecorationStyleDotted || |
| 128 decorationStyle == TextDecorationStyleDashed) | 169 decorationStyle == TextDecorationStyleDashed) |
| 129 return true; | 170 return true; |
| (...skipping 1004 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1134 | 1175 |
| 1135 context.setStrokeThickness(textDecorationThickness); | 1176 context.setStrokeThickness(textDecorationThickness); |
| 1136 | 1177 |
| 1137 bool antialiasDecoration = shouldSetDecorationAntialias(decorations); | 1178 bool antialiasDecoration = shouldSetDecorationAntialias(decorations); |
| 1138 | 1179 |
| 1139 // Offset between lines - always non-zero, so lines never cross each other. | 1180 // Offset between lines - always non-zero, so lines never cross each other. |
| 1140 float doubleOffset = textDecorationThickness + 1.f; | 1181 float doubleOffset = textDecorationThickness + 1.f; |
| 1141 bool skipIntercepts = | 1182 bool skipIntercepts = |
| 1142 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk; | 1183 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk; |
| 1143 | 1184 |
| 1185 // text-underline-position may flip underline and overline. |
| 1186 ResolvedUnderlinePosition underlinePosition = |
| 1187 resolveUnderlinePosition(styleToUse, &m_inlineTextBox); |
| 1188 bool flipUnderlineAndOverline = false; |
| 1189 if (underlinePosition == ResolvedUnderlinePosition::Over) { |
| 1190 flipUnderlineAndOverline = true; |
| 1191 underlinePosition = ResolvedUnderlinePosition::Under; |
| 1192 } |
| 1193 |
| 1144 for (const AppliedTextDecoration& decoration : decorations) { | 1194 for (const AppliedTextDecoration& decoration : decorations) { |
| 1145 TextDecoration lines = decoration.lines(); | 1195 TextDecoration lines = decoration.lines(); |
| 1196 if (flipUnderlineAndOverline) { |
| 1197 lines = static_cast<TextDecoration>( |
| 1198 lines ^ (TextDecorationUnderline | TextDecorationOverline)); |
| 1199 } |
| 1146 if ((lines & TextDecorationUnderline) && fontData) { | 1200 if ((lines & TextDecorationUnderline) && fontData) { |
| 1147 const int underlineOffset = | 1201 const int underlineOffset = computeUnderlineOffset( |
| 1148 computeUnderlineOffset(styleToUse, fontData->getFontMetrics(), | 1202 underlinePosition, styleToUse, fontData->getFontMetrics(), |
| 1149 &m_inlineTextBox, textDecorationThickness); | 1203 &m_inlineTextBox, textDecorationThickness); |
| 1150 AppliedDecorationPainter decorationPainter( | 1204 AppliedDecorationPainter decorationPainter( |
| 1151 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), | 1205 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), |
| 1152 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 1, | 1206 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 1, |
| 1153 antialiasDecoration); | 1207 antialiasDecoration); |
| 1154 if (skipIntercepts) { | 1208 if (skipIntercepts) { |
| 1155 textPainter.clipDecorationsStripe( | 1209 textPainter.clipDecorationsStripe( |
| 1156 -baseline + decorationPainter.decorationBounds().y() - | 1210 -baseline + decorationPainter.decorationBounds().y() - |
| 1157 FloatPoint(localOrigin).y(), | 1211 FloatPoint(localOrigin).y(), |
| 1158 decorationPainter.decorationBounds().height(), | 1212 decorationPainter.decorationBounds().height(), |
| 1159 textDecorationThickness); | 1213 textDecorationThickness); |
| 1160 } | 1214 } |
| 1161 decorationPainter.paint(); | 1215 decorationPainter.paint(); |
| 1162 } | 1216 } |
| 1163 if (lines & TextDecorationOverline) { | 1217 if (lines & TextDecorationOverline) { |
| 1218 const int overlineOffset = |
| 1219 computeOverlineOffset(styleToUse, &m_inlineTextBox); |
| 1164 AppliedDecorationPainter decorationPainter( | 1220 AppliedDecorationPainter decorationPainter( |
| 1165 context, FloatPoint(localOrigin), width.toFloat(), decoration, | 1221 context, FloatPoint(localOrigin) + FloatPoint(0, overlineOffset), |
| 1166 textDecorationThickness, -doubleOffset, 1, antialiasDecoration); | 1222 width.toFloat(), decoration, textDecorationThickness, -doubleOffset, |
| 1223 1, antialiasDecoration); |
| 1167 if (skipIntercepts) { | 1224 if (skipIntercepts) { |
| 1168 textPainter.clipDecorationsStripe( | 1225 textPainter.clipDecorationsStripe( |
| 1169 -baseline + decorationPainter.decorationBounds().y() - | 1226 -baseline + decorationPainter.decorationBounds().y() - |
| 1170 FloatPoint(localOrigin).y(), | 1227 FloatPoint(localOrigin).y(), |
| 1171 decorationPainter.decorationBounds().height(), | 1228 decorationPainter.decorationBounds().height(), |
| 1172 textDecorationThickness); | 1229 textDecorationThickness); |
| 1173 } | 1230 } |
| 1174 decorationPainter.paint(); | 1231 decorationPainter.paint(); |
| 1175 } | 1232 } |
| 1176 if (lines & TextDecorationLineThrough) { | 1233 if (lines & TextDecorationLineThrough) { |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1329 | 1386 |
| 1330 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), | 1387 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), |
| 1331 m_inlineTextBox.logicalHeight())); | 1388 m_inlineTextBox.logicalHeight())); |
| 1332 context.clip(FloatRect(boxRect)); | 1389 context.clip(FloatRect(boxRect)); |
| 1333 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), | 1390 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), |
| 1334 boxRect.height().toInt(), color, | 1391 boxRect.height().toInt(), color, |
| 1335 paintOffsets.first, paintOffsets.second); | 1392 paintOffsets.first, paintOffsets.second); |
| 1336 } | 1393 } |
| 1337 | 1394 |
| 1338 } // namespace blink | 1395 } // namespace blink |
| OLD | NEW |