Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(218)

Side by Side Diff: Source/core/paint/InlineTextBoxPainter.cpp

Issue 620243002: Move painting code from InlineTextBox to InlineTextBoxPainter. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Finished. Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "core/paint/InlineTextBoxPainter.h"
7
8 #include "core/dom/DocumentMarkerController.h"
9 #include "core/dom/RenderedDocumentMarker.h"
10 #include "core/editing/CompositionUnderline.h"
11 #include "core/editing/CompositionUnderlineRangeFilter.h"
12 #include "core/editing/Editor.h"
13 #include "core/editing/InputMethodController.h"
14 #include "core/frame/LocalFrame.h"
15 #include "core/paint/BoxPainter.h"
16 #include "core/rendering/InlineTextBox.h"
17 #include "core/rendering/PaintInfo.h"
18 #include "core/rendering/RenderBlock.h"
19 #include "core/rendering/RenderCombineText.h"
20 #include "core/rendering/RenderTheme.h"
21 #include "core/rendering/TextPainter.h"
22
23 namespace blink {
24
25 typedef WTF::HashMap<const InlineTextBox*, TextBlobPtr> InlineTextBoxBlobCacheMa p;
26 extern InlineTextBoxBlobCacheMap* gTextBlobCache;
27
28 static const int misspellingLineThickness = 3;
29
30 void InlineTextBoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintO ffset)
31 {
32 if (m_inlineTextBox.isLineBreak() || !paintInfo.shouldPaintWithinRoot(&m_inl ineTextBox.renderer()) || m_inlineTextBox.renderer().style()->visibility() != VI SIBLE
33 || m_inlineTextBox.truncation() == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_inlineTextBox.len())
34 return;
35
36 ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintP haseChildOutlines);
37
38 LayoutRect logicalVisualOverflow = m_inlineTextBox.logicalOverflowRect();
39 LayoutUnit logicalStart = logicalVisualOverflow.x() + (m_inlineTextBox.isHor izontal() ? paintOffset.x() : paintOffset.y());
40 LayoutUnit logicalExtent = logicalVisualOverflow.width();
41
42 LayoutUnit paintEnd = m_inlineTextBox.isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
43 LayoutUnit paintStart = m_inlineTextBox.isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
44
45 // When subpixel font scaling is enabled text runs are positioned at
46 // subpixel boundaries on the x-axis and thus there is no reason to
47 // snap the x value. We still round the y-axis to ensure consistent
48 // line heights.
49 LayoutPoint adjustedPaintOffset = RuntimeEnabledFeatures::subpixelFontScalin gEnabled()
50 ? LayoutPoint(paintOffset.x(), paintOffset.y().round())
51 : roundedIntPoint(paintOffset);
52
53 if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
54 return;
55
56 bool isPrinting = m_inlineTextBox.renderer().document().printing();
57
58 // Determine whether or not we're selected.
59 bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && m_inlineTextBox.selectionState() != RenderObject::SelectionNone;
60 if (!haveSelection && paintInfo.phase == PaintPhaseSelection) {
61 // When only painting the selection, don't bother to paint if there is n one.
62 return;
63 }
64
65 if (m_inlineTextBox.truncation() != cNoTruncation) {
66 if (m_inlineTextBox.renderer().containingBlock()->style()->isLeftToRight Direction() != m_inlineTextBox.isLeftToRightDirection()) {
67 // Make the visible fragment of text hug the edge closest to the res t of the run by moving the origin
68 // at which we start drawing text.
69 // e.g. In the case of LTR text truncated in an RTL Context, the cor rect behavior is:
70 // |Hello|CBA| -> |...He|CBA|
71 // In order to draw the fragment "He" aligned to the right edge of i t's box, we need to start drawing
72 // farther to the right.
73 // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
74 // truncated string i.e. |Hello|CBA| -> |...lo|CBA|
75 LayoutUnit widthOfVisibleText = m_inlineTextBox.renderer().width(m_i nlineTextBox.start(), m_inlineTextBox.truncation(), m_inlineTextBox.textPos(), m _inlineTextBox.isLeftToRightDirection() ? LTR : RTL, m_inlineTextBox.isFirstLine Style());
76 LayoutUnit widthOfHiddenText = m_inlineTextBox.logicalWidth() - widt hOfVisibleText;
77 // FIXME: The hit testing logic also needs to take this translation into account.
78 LayoutSize truncationOffset(m_inlineTextBox.isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0);
79 adjustedPaintOffset.move(m_inlineTextBox.isHorizontal() ? truncation Offset : truncationOffset.transposedSize());
80 }
81 }
82
83 GraphicsContext* context = paintInfo.context;
84 RenderStyle* styleToUse = m_inlineTextBox.renderer().style(m_inlineTextBox.i sFirstLineStyle());
85
86 adjustedPaintOffset.move(0, styleToUse->isHorizontalWritingMode() ? 0 : -m_i nlineTextBox.logicalHeight());
87
88 FloatPoint boxOrigin = m_inlineTextBox.locationIncludingFlipping();
89 boxOrigin.move(adjustedPaintOffset.x().toFloat(), adjustedPaintOffset.y().to Float());
90 FloatRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), m_in lineTextBox.logicalHeight()));
91
92 RenderCombineText* combinedText = styleToUse->hasTextCombine() && m_inlineTe xtBox.renderer().isCombineText() && toRenderCombineText(m_inlineTextBox.renderer ()).isCombined() ? &toRenderCombineText(m_inlineTextBox.renderer()) : 0;
93
94 bool shouldRotate = !m_inlineTextBox.isHorizontal() && !combinedText;
95 if (shouldRotate)
96 context->concatCTM(m_inlineTextBox.rotation(boxRect, InlineTextBox::Cloc kwise));
jbroman 2014/10/03 17:46:17 InlineTextBox::rotation is static. It seems funny
jbroman 2014/10/03 17:48:43 Alternatively, maybe rotation() belongs in InlineT
chrishtr 2014/10/03 18:10:36 Made it static.
97
98 // Determine whether or not we have composition underlines to draw.
99 bool containsComposition = m_inlineTextBox.renderer().node() && m_inlineText Box.renderer().frame()->inputMethodController().compositionNode() == m_inlineTex tBox.renderer().node();
100 bool useCustomUnderlines = containsComposition && m_inlineTextBox.renderer() .frame()->inputMethodController().compositionUsesCustomUnderlines();
101
102 // Determine text colors.
103 TextPainter::Style textStyle = TextPainter::textPaintingStyle(m_inlineTextBo x.renderer(), styleToUse, paintInfo.forceBlackText(), isPrinting);
104 TextPainter::Style selectionStyle = TextPainter::selectionPaintingStyle(m_in lineTextBox.renderer(), haveSelection, paintInfo.forceBlackText(), isPrinting, t extStyle);
105 bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection);
106 bool paintSelectedTextSeparately = !paintSelectedTextOnly && textStyle != se lectionStyle;
107
108 // Set our font.
109 const Font& font = styleToUse->font();
110
111 FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontM etrics().ascent());
112 if (combinedText)
113 combinedText->adjustTextOrigin(textOrigin, boxRect);
114
115 // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
116 // and composition highlights.
117 if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseT extClip && !isPrinting) {
118 if (containsComposition) {
119 paintCompositionBackgrounds(context, boxOrigin, styleToUse, font, us eCustomUnderlines);
120 }
121
122 paintDocumentMarkers(context, boxOrigin, styleToUse, font, true);
123
124 if (haveSelection && !useCustomUnderlines)
125 paintSelection(context, boxOrigin, styleToUse, font, selectionStyle. fillColor);
126 }
127
128 // 2. Now paint the foreground, including text and decorations like underlin e/overline (in quirks mode only).
129 int length = m_inlineTextBox.len();
130 int maximumLength;
131 StringView string;
132 if (!combinedText) {
133 string = m_inlineTextBox.renderer().text().createView();
134 if (static_cast<unsigned>(length) != string.length() || m_inlineTextBox. start())
135 string.narrow(m_inlineTextBox.start(), length);
136 maximumLength = m_inlineTextBox.renderer().textLength() - m_inlineTextBo x.start();
137 } else {
138 combinedText->getStringToRender(m_inlineTextBox.start(), string, length) ;
139 maximumLength = length;
140 }
141
142 StringBuilder charactersWithHyphen;
143 TextRun textRun = m_inlineTextBox.constructTextRun(styleToUse, font, string, maximumLength, m_inlineTextBox.hasHyphen() ? &charactersWithHyphen : 0);
144 if (m_inlineTextBox.hasHyphen())
145 length = textRun.length();
146
147 int selectionStart = 0;
148 int selectionEnd = 0;
149 if (paintSelectedTextOnly || paintSelectedTextSeparately)
150 m_inlineTextBox.selectionStartEnd(selectionStart, selectionEnd);
151
152 bool respectHyphen = selectionEnd == static_cast<int>(m_inlineTextBox.len()) && m_inlineTextBox.hasHyphen();
153 if (respectHyphen)
154 selectionEnd = textRun.length();
155
156 if (m_inlineTextBox.truncation() != cNoTruncation) {
157 selectionStart = std::min<int>(selectionStart, m_inlineTextBox.truncatio n());
158 selectionEnd = std::min<int>(selectionEnd, m_inlineTextBox.truncation()) ;
159 length = m_inlineTextBox.truncation();
160 }
161
162 TextPainter textPainter(context, font, textRun, textOrigin, boxRect, m_inlin eTextBox.isHorizontal());
163 TextEmphasisPosition emphasisMarkPosition;
164 bool hasTextEmphasis = m_inlineTextBox.getEmphasisMarkPosition(styleToUse, e mphasisMarkPosition);
165 if (hasTextEmphasis)
166 textPainter.setEmphasisMark(styleToUse->textEmphasisMarkString(), emphas isMarkPosition);
167 if (combinedText)
168 textPainter.setCombinedText(combinedText);
169
170 if (!paintSelectedTextOnly) {
171 // FIXME: Truncate right-to-left text correctly.
172 int startOffset = 0;
173 int endOffset = length;
174 if (paintSelectedTextSeparately && selectionStart < selectionEnd) {
175 startOffset = selectionEnd;
176 endOffset = selectionStart;
177 }
178
179 // FIXME: This cache should probably ultimately be held somewhere else.
180 // A hashmap is convenient to avoid a memory hit when the
181 // RuntimeEnabledFeature is off.
182 bool textBlobIsCacheable = RuntimeEnabledFeatures::textBlobEnabled() && startOffset == 0 && endOffset == length;
183 TextBlobPtr* cachedTextBlob = 0;
184 if (textBlobIsCacheable) {
185 if (!gTextBlobCache)
186 gTextBlobCache = new InlineTextBoxBlobCacheMap;
187 cachedTextBlob = &gTextBlobCache->add(&m_inlineTextBox, nullptr).sto redValue->value;
jbroman 2014/10/03 17:46:17 Sharing this member this way seems fishy. If I wer
chrishtr 2014/10/03 18:10:36 Good points. Went with option (b).
188 }
189 textPainter.paint(startOffset, endOffset, length, textStyle, cachedTextB lob);
190 }
191
192 if ((paintSelectedTextOnly || paintSelectedTextSeparately) && selectionStart < selectionEnd) {
193 // paint only the text that is selected
194 textPainter.paint(selectionStart, selectionEnd, length, selectionStyle);
195 }
196
197 // Paint decorations
198 TextDecoration textDecorations = styleToUse->textDecorationsInEffect();
199 if (textDecorations != TextDecorationNone && !paintSelectedTextOnly) {
200 GraphicsContextStateSaver stateSaver(*context, false);
201 TextPainter::updateGraphicsContext(context, textStyle, m_inlineTextBox.i sHorizontal(), stateSaver);
202 if (combinedText)
203 context->concatCTM(m_inlineTextBox.rotation(boxRect, InlineTextBox:: Clockwise));
204 paintDecoration(context, boxOrigin, textDecorations);
205 if (combinedText)
206 context->concatCTM(m_inlineTextBox.rotation(boxRect, InlineTextBox:: Counterclockwise));
207 }
208
209 if (paintInfo.phase == PaintPhaseForeground) {
210 paintDocumentMarkers(context, boxOrigin, styleToUse, font, false);
211
212 // Paint custom underlines for compositions.
213 if (useCustomUnderlines) {
214 const Vector<CompositionUnderline>& underlines = m_inlineTextBox.ren derer().frame()->inputMethodController().customCompositionUnderlines();
215 CompositionUnderlineRangeFilter filter(underlines, m_inlineTextBox.s tart(), m_inlineTextBox.end());
216 for (CompositionUnderlineRangeFilter::ConstIterator it = filter.begi n(); it != filter.end(); ++it) {
217 if (it->color == Color::transparent)
218 continue;
219 paintCompositionUnderline(context, boxOrigin, *it);
220 }
221 }
222 }
223
224 if (shouldRotate)
225 context->concatCTM(m_inlineTextBox.rotation(boxRect, InlineTextBox::Coun terclockwise));
226 }
227
228 unsigned InlineTextBoxPainter::underlinePaintStart(const CompositionUnderline& u nderline)
229 {
230 return std::max(static_cast<unsigned>(m_inlineTextBox.start()), underline.st artOffset);
231 }
232
233 unsigned InlineTextBoxPainter::underlinePaintEnd(const CompositionUnderline& und erline)
234 {
235 unsigned paintEnd = std::min(m_inlineTextBox.end() + 1, underline.endOffset) ; // end() points at the last char, not past it.
236 if (m_inlineTextBox.truncation() != cNoTruncation)
237 paintEnd = std::min(paintEnd, static_cast<unsigned>(m_inlineTextBox.star t() + m_inlineTextBox.truncation()));
238 return paintEnd;
239 }
240
241 void InlineTextBoxPainter::paintCompositionBackgrounds(GraphicsContext* pt, cons t FloatPoint& boxOrigin, RenderStyle* style, const Font& font, bool useCustomUnd erlines)
242 {
243 if (useCustomUnderlines) {
244 // Paint custom background highlights for compositions.
245 const Vector<CompositionUnderline>& underlines = m_inlineTextBox.rendere r().frame()->inputMethodController().customCompositionUnderlines();
246 CompositionUnderlineRangeFilter filter(underlines, m_inlineTextBox.start (), m_inlineTextBox.end());
247 for (CompositionUnderlineRangeFilter::ConstIterator it = filter.begin(); it != filter.end(); ++it) {
248 if (it->backgroundColor == Color::transparent)
249 continue;
250 paintSingleCompositionBackgroundRun(pt, boxOrigin, style, font, it-> backgroundColor, underlinePaintStart(*it), underlinePaintEnd(*it));
251 }
252
253 } else {
254 paintSingleCompositionBackgroundRun(pt, boxOrigin, style, font, RenderTh eme::theme().platformDefaultCompositionBackgroundColor(),
255 m_inlineTextBox.renderer().frame()->inputMethodController().composit ionStart(),
256 m_inlineTextBox.renderer().frame()->inputMethodController().composit ionEnd());
257 }
258 }
259
260 void InlineTextBoxPainter::paintSingleCompositionBackgroundRun(GraphicsContext* context, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font, Colo r backgroundColor, int startPos, int endPos)
261 {
262 int sPos = std::max(startPos - static_cast<int>(m_inlineTextBox.start()), 0) ;
263 int ePos = std::min(endPos - static_cast<int>(m_inlineTextBox.start()), stat ic_cast<int>(m_inlineTextBox.len()));
264 if (sPos >= ePos)
265 return;
266
267 int deltaY = m_inlineTextBox.renderer().style()->isFlippedLinesWritingMode() ? m_inlineTextBox.root().selectionBottom() - m_inlineTextBox.logicalBottom() : m_inlineTextBox.logicalTop() - m_inlineTextBox.root().selectionTop();
268 int selHeight = m_inlineTextBox.root().selectionHeight();
269 FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
270 context->drawHighlightForText(font, m_inlineTextBox.constructTextRun(style, font), localOrigin, selHeight, backgroundColor, sPos, ePos);
271 }
272
273 void InlineTextBoxPainter::paintDocumentMarkers(GraphicsContext* pt, const Float Point& boxOrigin, RenderStyle* style, const Font& font, bool background)
274 {
275 if (!m_inlineTextBox.renderer().node())
276 return;
277
278 DocumentMarkerVector markers = m_inlineTextBox.renderer().document().markers ().markersFor(m_inlineTextBox.renderer().node());
279 DocumentMarkerVector::const_iterator markerIt = markers.begin();
280
281 // Give any document markers that touch this run a chance to draw before the text has been drawn.
282 // Note end() points at the last char, not one past it like endOffset and ra nges do.
283 for ( ; markerIt != markers.end(); ++markerIt) {
284 DocumentMarker* marker = *markerIt;
285
286 // Paint either the background markers or the foreground markers, but no t both
287 switch (marker->type()) {
288 case DocumentMarker::Grammar:
289 case DocumentMarker::Spelling:
290 if (background)
291 continue;
292 break;
293 case DocumentMarker::TextMatch:
294 if (!background)
295 continue;
296 break;
297 default:
298 continue;
299 }
300
301 if (marker->endOffset() <= m_inlineTextBox.start()) {
302 // marker is completely before this run. This might be a marker tha t sits before the
303 // first run we draw, or markers that were within runs we skipped du e to truncation.
304 continue;
305 }
306 if (marker->startOffset() > m_inlineTextBox.end()) {
307 // marker is completely after this run, bail. A later run will pain t it.
308 break;
309 }
310
311 // marker intersects this run. Paint it.
312 switch (marker->type()) {
313 case DocumentMarker::Spelling:
314 m_inlineTextBox.paintDocumentMarker(pt, boxOrigin, marker, style, fo nt, false);
315 break;
316 case DocumentMarker::Grammar:
317 m_inlineTextBox.paintDocumentMarker(pt, boxOrigin, marker, style, fo nt, true);
318 break;
319 case DocumentMarker::TextMatch:
320 m_inlineTextBox.paintTextMatchMarker(pt, boxOrigin, marker, style, f ont);
321 break;
322 default:
323 ASSERT_NOT_REACHED();
324 }
325 }
326 }
327
328 static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentM arker::MarkerType markerType)
329 {
330 switch (markerType) {
331 case DocumentMarker::Spelling:
332 return GraphicsContext::DocumentMarkerSpellingLineStyle;
333 case DocumentMarker::Grammar:
334 return GraphicsContext::DocumentMarkerGrammarLineStyle;
335 default:
336 ASSERT_NOT_REACHED();
337 return GraphicsContext::DocumentMarkerSpellingLineStyle;
338 }
339 }
340
341 void InlineTextBoxPainter::paintDocumentMarker(GraphicsContext* pt, const FloatP oint& boxOrigin, DocumentMarker* marker, RenderStyle* style, const Font& font, b ool grammar)
342 {
343 // Never print spelling/grammar markers (5327887)
344 if (m_inlineTextBox.renderer().document().printing())
345 return;
346
347 if (m_inlineTextBox.truncation() == cFullTruncation)
348 return;
349
350 float start = 0; // start of line to draw, relative to tx
351 float width = m_inlineTextBox.logicalWidth(); // how much line to draw
352
353 // Determine whether we need to measure text
354 bool markerSpansWholeBox = true;
355 if (m_inlineTextBox.start() <= marker->startOffset())
356 markerSpansWholeBox = false;
357 if ((m_inlineTextBox.end() + 1) != marker->endOffset()) // end points at the last char, not past it
358 markerSpansWholeBox = false;
359 if (m_inlineTextBox.truncation() != cNoTruncation)
360 markerSpansWholeBox = false;
361
362 if (!markerSpansWholeBox || grammar) {
363 int startPosition = std::max<int>(marker->startOffset() - m_inlineTextBo x.start(), 0);
364 int endPosition = std::min<int>(marker->endOffset() - static_cast<int>(m _inlineTextBox.start()), m_inlineTextBox.len());
365
366 if (m_inlineTextBox.truncation() != cNoTruncation)
367 endPosition = std::min<int>(endPosition, m_inlineTextBox.truncation( ));
368
369 // Calculate start & width
370 int deltaY = m_inlineTextBox.renderer().style()->isFlippedLinesWritingMo de() ? m_inlineTextBox.root().selectionBottom() - m_inlineTextBox.logicalBottom( ) : m_inlineTextBox.logicalTop() - m_inlineTextBox.root().selectionTop();
371 int selHeight = m_inlineTextBox.root().selectionHeight();
372 FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
373 TextRun run = m_inlineTextBox.constructTextRun(style, font);
374
375 // FIXME: Convert the document markers to float rects.
376 IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, sta rtPoint, selHeight, startPosition, endPosition));
377 start = markerRect.x() - startPoint.x();
378 width = markerRect.width();
379
380 // Store rendered rects for bad grammar markers, so we can hit-test agai nst it elsewhere in order to
381 // display a toolTip. We don't do this for misspelling markers.
382 if (grammar) {
383 markerRect.move(-boxOrigin.x(), -boxOrigin.y());
384 markerRect = m_inlineTextBox.renderer().localToAbsoluteQuad(FloatRec t(markerRect)).enclosingBoundingBox();
385 toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
386 }
387 }
388
389 // IMPORTANT: The misspelling underline is not considered when calculating t he text bounds, so we have to
390 // make sure to fit within those bounds. This means the top pixel(s) of the underline will overlap the
391 // bottom pixel(s) of the glyphs in smaller font sizes. The alternatives ar e to increase the line spacing (bad!!)
392 // or decrease the underline thickness. The overlap is actually the most us eful, and matches what AppKit does.
393 // So, we generally place the underline at the bottom of the text, but in la rger fonts that's not so good so
394 // we pin to two pixels under the baseline.
395 int lineThickness = misspellingLineThickness;
396 int baseline = m_inlineTextBox.renderer().style(m_inlineTextBox.isFirstLineS tyle())->fontMetrics().ascent();
397 int descent = m_inlineTextBox.logicalHeight() - baseline;
398 int underlineOffset;
399 if (descent <= (2 + lineThickness)) {
400 // Place the underline at the very bottom of the text in small/medium fo nts.
401 underlineOffset = m_inlineTextBox.logicalHeight() - lineThickness;
402 } else {
403 // In larger fonts, though, place the underline up near the baseline to prevent a big gap.
404 underlineOffset = baseline + 2;
405 }
406 pt->drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y( ) + underlineOffset), width, lineStyleForMarkerType(marker->type()));
407 }
408
409 void InlineTextBoxPainter::paintSelection(GraphicsContext* context, const FloatP oint& boxOrigin, RenderStyle* style, const Font& font, Color textColor)
410 {
411 // See if we have a selection to paint at all.
412 int sPos, ePos;
413 m_inlineTextBox.selectionStartEnd(sPos, ePos);
414 if (sPos >= ePos)
415 return;
416
417 Color c = m_inlineTextBox.renderer().selectionBackgroundColor();
418 if (!c.alpha())
419 return;
420
421 // If the text color ends up being the same as the selection background, inv ert the selection
422 // background.
423 if (textColor == c)
424 c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
425
426 // If the text is truncated, let the thing being painted in the truncation
427 // draw its own highlight.
428 int length = m_inlineTextBox.truncation() != cNoTruncation ? m_inlineTextBox .truncation() : m_inlineTextBox.len();
429 StringView string = m_inlineTextBox.renderer().text().createView();
430
431 if (string.length() != static_cast<unsigned>(length) || m_inlineTextBox.star t())
432 string.narrow(m_inlineTextBox.start(), length);
433
434 StringBuilder charactersWithHyphen;
435 bool respectHyphen = ePos == length && m_inlineTextBox.hasHyphen();
436 TextRun textRun = m_inlineTextBox.constructTextRun(style, font, string, m_in lineTextBox.renderer().textLength() - m_inlineTextBox.start(), respectHyphen ? & charactersWithHyphen : 0);
437 if (respectHyphen)
438 ePos = textRun.length();
439
440 LayoutUnit selectionBottom = m_inlineTextBox.root().selectionBottom();
441 LayoutUnit selectionTop = m_inlineTextBox.root().selectionTopAdjustedForPrec edingBlock();
442
443 int deltaY = roundToInt(m_inlineTextBox.renderer().style()->isFlippedLinesWr itingMode() ? selectionBottom - m_inlineTextBox.logicalBottom() : m_inlineTextBo x.logicalTop() - selectionTop);
444 int selHeight = std::max(0, roundToInt(selectionBottom - selectionTop));
445
446 FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
447 FloatRect clipRect(localOrigin, FloatSize(m_inlineTextBox.logicalWidth(), se lHeight));
448
449 GraphicsContextStateSaver stateSaver(*context);
450 context->clip(clipRect);
451 context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, sPos , ePos);
452 }
453
454 static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, const float textDecorationThickness)
455 {
456 // Compute the gap between the font and the underline. Use at least one
457 // pixel gap, if underline is thick then use a bigger gap.
458 int gap = 0;
459
460 // Underline position of zero means draw underline on Baseline Position,
461 // in Blink we need at least 1-pixel gap to adding following check.
462 // Positive underline Position means underline should be drawn above baselin e
463 // and negative value means drawing below baseline, negating the value as in Blink
464 // downward Y-increases.
465
466 if (fontMetrics.underlinePosition())
467 gap = -fontMetrics.underlinePosition();
468 else
469 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f));
470
471 // FIXME: We support only horizontal text for now.
472 switch (underlinePosition) {
473 case TextUnderlinePositionAuto:
474 return fontMetrics.ascent() + gap; // Position underline near the alphab etic baseline.
475 case TextUnderlinePositionUnder: {
476 // Position underline relative to the under edge of the lowest element's content box.
477 const float offset = inlineTextBox->root().maxLogicalTop() - inlineTextB ox->logicalTop();
478 if (offset > 0)
479 return inlineTextBox->logicalHeight() + gap + offset;
480 return inlineTextBox->logicalHeight() + gap;
481 }
482 }
483
484 ASSERT_NOT_REACHED();
485 return fontMetrics.ascent() + gap;
486 }
487
488 static bool shouldSetDecorationAntialias(TextDecorationStyle decorationStyle)
489 {
490 return decorationStyle == TextDecorationStyleDotted || decorationStyle == Te xtDecorationStyleDashed;
491 }
492
493 static bool shouldSetDecorationAntialias(TextDecorationStyle underline, TextDeco rationStyle overline, TextDecorationStyle linethrough)
494 {
495 return shouldSetDecorationAntialias(underline) || shouldSetDecorationAntiali as(overline) || shouldSetDecorationAntialias(linethrough);
496 }
497
498 static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorati onStyle)
499 {
500 StrokeStyle strokeStyle = SolidStroke;
501 switch (decorationStyle) {
502 case TextDecorationStyleSolid:
503 strokeStyle = SolidStroke;
504 break;
505 case TextDecorationStyleDouble:
506 strokeStyle = DoubleStroke;
507 break;
508 case TextDecorationStyleDotted:
509 strokeStyle = DottedStroke;
510 break;
511 case TextDecorationStyleDashed:
512 strokeStyle = DashedStroke;
513 break;
514 case TextDecorationStyleWavy:
515 strokeStyle = WavyStroke;
516 break;
517 }
518
519 return strokeStyle;
520 }
521
522 static void adjustStepToDecorationLength(float& step, float& controlPointDistanc e, float length)
523 {
524 ASSERT(step > 0);
525
526 if (length <= 0)
527 return;
528
529 unsigned stepCount = static_cast<unsigned>(length / step);
530
531 // Each Bezier curve starts at the same pixel that the previous one
532 // ended. We need to subtract (stepCount - 1) pixels when calculating the
533 // length covered to account for that.
534 float uncoveredLength = length - (stepCount * step - (stepCount - 1));
535 float adjustment = uncoveredLength / stepCount;
536 step += adjustment;
537 controlPointDistance += adjustment;
538 }
539
540 /*
541 * Draw one cubic Bezier curve and repeat the same pattern long the the decorati on's axis.
542 * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve
543 * form a diamond shape:
544 *
545 * step
546 * |-----------|
547 *
548 * controlPoint1
549 * +
550 *
551 *
552 * . .
553 * . .
554 * . .
555 * (x1, y1) p1 + . + p2 (x2, y2) - <--- Decoration's axis
556 * . . |
557 * . . |
558 * . . | controlPointDistance
559 * |
560 * |
561 * + -
562 * controlPoint2
563 *
564 * |-----------|
565 * step
566 */
567 static void strokeWavyTextDecoration(GraphicsContext* context, FloatPoint p1, Fl oatPoint p2, float strokeThickness)
568 {
569 context->adjustLineToPixelBoundaries(p1, p2, strokeThickness, context->strok eStyle());
570
571 Path path;
572 path.moveTo(p1);
573
574 // Distance between decoration's axis and Bezier curve's control points.
575 // The height of the curve is based on this distance. Use a minimum of 6 pix els distance since
576 // the actual curve passes approximately at half of that distance, that is 3 pixels.
577 // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
578 // as strockThickness increases to make the curve looks better.
579 float controlPointDistance = 3 * std::max<float>(2, strokeThickness);
580
581 // Increment used to form the diamond shape between start point (p1), contro l
582 // points and end point (p2) along the axis of the decoration. Makes the
583 // curve wider as strockThickness increases to make the curve looks better.
584 float step = 2 * std::max<float>(2, strokeThickness);
585
586 bool isVerticalLine = (p1.x() == p2.x());
587
588 if (isVerticalLine) {
589 ASSERT(p1.x() == p2.x());
590
591 float xAxis = p1.x();
592 float y1;
593 float y2;
594
595 if (p1.y() < p2.y()) {
596 y1 = p1.y();
597 y2 = p2.y();
598 } else {
599 y1 = p2.y();
600 y2 = p1.y();
601 }
602
603 adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
604 FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
605 FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
606
607 for (float y = y1; y + 2 * step <= y2;) {
608 controlPoint1.setY(y + step);
609 controlPoint2.setY(y + step);
610 y += 2 * step;
611 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis , y));
612 }
613 } else {
614 ASSERT(p1.y() == p2.y());
615
616 float yAxis = p1.y();
617 float x1;
618 float x2;
619
620 if (p1.x() < p2.x()) {
621 x1 = p1.x();
622 x2 = p2.x();
623 } else {
624 x1 = p2.x();
625 x2 = p1.x();
626 }
627
628 adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
629 FloatPoint controlPoint1(0, yAxis + controlPointDistance);
630 FloatPoint controlPoint2(0, yAxis - controlPointDistance);
631
632 for (float x = x1; x + 2 * step <= x2;) {
633 controlPoint1.setX(x + step);
634 controlPoint2.setX(x + step);
635 x += 2 * step;
636 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yA xis));
637 }
638 }
639
640 context->setShouldAntialias(true);
641 context->strokePath(path);
642 }
643
644 static void paintAppliedDecoration(GraphicsContext* context, FloatPoint start, f loat width, float doubleOffset, int wavyOffsetFactor,
645 RenderObject::AppliedTextDecoration decoration, float thickness, bool antial iasDecoration, bool isPrinting)
646 {
647 context->setStrokeStyle(textDecorationStyleToStrokeStyle(decoration.style));
648 context->setStrokeColor(decoration.color);
649
650 switch (decoration.style) {
651 case TextDecorationStyleWavy:
652 strokeWavyTextDecoration(context, start + FloatPoint(0, doubleOffset * w avyOffsetFactor), start + FloatPoint(width, doubleOffset * wavyOffsetFactor), th ickness);
653 break;
654 case TextDecorationStyleDotted:
655 case TextDecorationStyleDashed:
656 context->setShouldAntialias(antialiasDecoration);
657 // Fall through
658 default:
659 context->drawLineForText(start, width, isPrinting);
660
661 if (decoration.style == TextDecorationStyleDouble)
662 context->drawLineForText(start + FloatPoint(0, doubleOffset), width, isPrinting);
663 }
664 }
665
666 void InlineTextBoxPainter::paintDecoration(GraphicsContext* context, const Float Point& boxOrigin, TextDecoration deco)
667 {
668 GraphicsContextStateSaver stateSaver(*context);
669
670 if (m_inlineTextBox.truncation() == cFullTruncation)
671 return;
672
673 FloatPoint localOrigin = boxOrigin;
674
675 float width = m_inlineTextBox.logicalWidth();
676 if (m_inlineTextBox.truncation() != cNoTruncation) {
677 width = m_inlineTextBox.renderer().width(m_inlineTextBox.start(), m_inli neTextBox.truncation(), m_inlineTextBox.textPos(), m_inlineTextBox.isLeftToRight Direction() ? LTR : RTL, m_inlineTextBox.isFirstLineStyle());
678 if (!m_inlineTextBox.isLeftToRightDirection())
679 localOrigin.move(m_inlineTextBox.logicalWidth() - width, 0);
680 }
681
682 // Get the text decoration colors.
683 RenderObject::AppliedTextDecoration underline, overline, linethrough;
684 m_inlineTextBox.renderer().getTextDecorations(deco, underline, overline, lin ethrough, true);
685 if (m_inlineTextBox.isFirstLineStyle())
686 m_inlineTextBox.renderer().getTextDecorations(deco, underline, overline, linethrough, true, true);
687
688 // Use a special function for underlines to get the positioning exactly righ t.
689 bool isPrinting = m_inlineTextBox.renderer().document().printing();
690
691 RenderStyle* styleToUse = m_inlineTextBox.renderer().style(m_inlineTextBox.i sFirstLineStyle());
692 int baseline = styleToUse->fontMetrics().ascent();
693
694 // Set the thick of the line to be 10% (or something else ?)of the computed font size and not less than 1px.
695 // Using computedFontSize should take care of zoom as well.
696
697 // Update Underline thickness, in case we have Faulty Font Metrics calculati ng underline thickness by old method.
698 float textDecorationThickness = styleToUse->fontMetrics().underlineThickness ();
699 int fontHeightInt = (int)(styleToUse->fontMetrics().floatHeight() + 0.5);
700 if ((textDecorationThickness == 0.f) || (textDecorationThickness >= (fontHei ghtInt >> 1)))
701 textDecorationThickness = std::max(1.f, styleToUse->computedFontSize() / 10.f);
702
703 context->setStrokeThickness(textDecorationThickness);
704
705 bool antialiasDecoration = shouldSetDecorationAntialias(overline.style, unde rline.style, linethrough.style)
706 && BoxPainter::shouldAntialiasLines(context);
707
708 // Offset between lines - always non-zero, so lines never cross each other.
709 float doubleOffset = textDecorationThickness + 1.f;
710
711 if (deco & TextDecorationUnderline) {
712 const int underlineOffset = computeUnderlineOffset(styleToUse->textUnder linePosition(), styleToUse->fontMetrics(), &m_inlineTextBox, textDecorationThick ness);
713 paintAppliedDecoration(context, localOrigin + FloatPoint(0, underlineOff set), width, doubleOffset, 1, underline, textDecorationThickness, antialiasDecor ation, isPrinting);
714 }
715 if (deco & TextDecorationOverline) {
716 paintAppliedDecoration(context, localOrigin, width, -doubleOffset, 1, ov erline, textDecorationThickness, antialiasDecoration, isPrinting);
717 }
718 if (deco & TextDecorationLineThrough) {
719 const float lineThroughOffset = 2 * baseline / 3;
720 paintAppliedDecoration(context, localOrigin + FloatPoint(0, lineThroughO ffset), width, doubleOffset, 0, linethrough, textDecorationThickness, antialiasD ecoration, isPrinting);
721 }
722 }
723
724 void InlineTextBoxPainter::paintCompositionUnderline(GraphicsContext* ctx, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
725 {
726 if (m_inlineTextBox.truncation() == cFullTruncation)
727 return;
728
729 unsigned paintStart = underlinePaintStart(underline);
730 unsigned paintEnd = underlinePaintEnd(underline);
731
732 // start of line to draw, relative to paintOffset.
733 float start = paintStart == static_cast<unsigned>(m_inlineTextBox.start()) ? 0 :
734 m_inlineTextBox.renderer().width(m_inlineTextBox.start(), paintStart - m _inlineTextBox.start(), m_inlineTextBox.textPos(), m_inlineTextBox.isLeftToRight Direction() ? LTR : RTL, m_inlineTextBox.isFirstLineStyle());
735 // how much line to draw
736 float width = (paintStart == static_cast<unsigned>(m_inlineTextBox.start()) && paintEnd == static_cast<unsigned>(m_inlineTextBox.end()) + 1) ? m_inlineTextB ox.logicalWidth() :
737 m_inlineTextBox.renderer().width(paintStart, paintEnd - paintStart, m_in lineTextBox.textPos() + start, m_inlineTextBox.isLeftToRightDirection() ? LTR : RTL, m_inlineTextBox.isFirstLineStyle());
738
739 // Thick marked text underlines are 2px thick as long as there is room for t he 2px line under the baseline.
740 // All other marked text underlines are 1px thick.
741 // If there's not enough space the underline will touch or overlap character s.
742 int lineThickness = 1;
743 int baseline = m_inlineTextBox.renderer().style(m_inlineTextBox.isFirstLineS tyle())->fontMetrics().ascent();
744 if (underline.thick && m_inlineTextBox.logicalHeight() - baseline >= 2)
745 lineThickness = 2;
746
747 // We need to have some space between underlines of subsequent clauses, beca use some input methods do not use different underline styles for those.
748 // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too.
749 start += 1;
750 width -= 2;
751
752 ctx->setStrokeColor(underline.color);
753 ctx->setStrokeThickness(lineThickness);
754 ctx->drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + m_inl ineTextBox.logicalHeight() - lineThickness), width, m_inlineTextBox.renderer().d ocument().printing());
755 }
756
757 void InlineTextBoxPainter::paintTextMatchMarker(GraphicsContext* pt, const Float Point& boxOrigin, DocumentMarker* marker, RenderStyle* style, const Font& font)
758 {
759 // Use same y positioning and height as for selection, so that when the sele ction and this highlight are on
760 // the same word there are no pieces sticking out.
761 int deltaY = m_inlineTextBox.renderer().style()->isFlippedLinesWritingMode() ? m_inlineTextBox.root().selectionBottom() - m_inlineTextBox.logicalBottom() : m_inlineTextBox.logicalTop() - m_inlineTextBox.root().selectionTop();
762 int selHeight = m_inlineTextBox.root().selectionHeight();
763
764 int sPos = std::max(marker->startOffset() - m_inlineTextBox.start(), (unsign ed)0);
765 int ePos = std::min(marker->endOffset() - m_inlineTextBox.start(), m_inlineT extBox.len());
766 TextRun run = m_inlineTextBox.constructTextRun(style, font);
767
768 // Always compute and store the rect associated with this marker. The comput ed rect is in absolute coordinates.
769 IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoin t(m_inlineTextBox.x(), m_inlineTextBox.root().selectionTop()), selHeight, sPos, ePos));
770 markerRect = m_inlineTextBox.renderer().localToAbsoluteQuad(FloatRect(marker Rect)).enclosingBoundingBox();
771 toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
772
773 // Optionally highlight the text
774 if (m_inlineTextBox.renderer().frame()->editor().markedTextMatchesAreHighlig hted()) {
775 Color color = marker->activeMatch() ?
776 RenderTheme::theme().platformActiveTextSearchHighlightColor() :
777 RenderTheme::theme().platformInactiveTextSearchHighlightColor();
778 GraphicsContextStateSaver stateSaver(*pt);
779 pt->clip(FloatRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_inlineTextBo x.logicalWidth(), selHeight));
780 pt->drawHighlightForText(font, run, FloatPoint(boxOrigin.x(), boxOrigin. y() - deltaY), selHeight, color, sPos, ePos);
781 }
782 }
783
784
785 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698