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

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

Powered by Google App Engine
This is Rietveld 408576698