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

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

Issue 2416993002: Introduce support for text-decoration-skip: ink (Closed)
Patch Set: Created 4 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/paint/InlineTextBoxPainter.h" 5 #include "core/paint/InlineTextBoxPainter.h"
6 6
7 #include "core/editing/CompositionUnderline.h" 7 #include "core/editing/CompositionUnderline.h"
8 #include "core/editing/Editor.h" 8 #include "core/editing/Editor.h"
9 #include "core/editing/markers/DocumentMarkerController.h" 9 #include "core/editing/markers/DocumentMarkerController.h"
10 #include "core/frame/LocalFrame.h" 10 #include "core/frame/LocalFrame.h"
11 #include "core/layout/LayoutTextCombine.h" 11 #include "core/layout/LayoutTextCombine.h"
12 #include "core/layout/LayoutTheme.h" 12 #include "core/layout/LayoutTheme.h"
13 #include "core/layout/api/LineLayoutAPIShim.h" 13 #include "core/layout/api/LineLayoutAPIShim.h"
14 #include "core/layout/api/LineLayoutBox.h" 14 #include "core/layout/api/LineLayoutBox.h"
15 #include "core/layout/line/InlineTextBox.h" 15 #include "core/layout/line/InlineTextBox.h"
16 #include "core/paint/PaintInfo.h" 16 #include "core/paint/PaintInfo.h"
17 #include "core/paint/TextPainter.h" 17 #include "core/paint/TextPainter.h"
18 #include "platform/graphics/GraphicsContextStateSaver.h" 18 #include "platform/graphics/GraphicsContextStateSaver.h"
19 #include "platform/graphics/paint/DrawingRecorder.h" 19 #include "platform/graphics/paint/DrawingRecorder.h"
20 #include "wtf/Optional.h" 20 #include "wtf/Optional.h"
21 21
22 namespace blink { 22 namespace blink {
23 23
24 static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition,
25 const FontMetrics& fontMetrics,
26 const InlineTextBox* inlineTextBox,
27 const float textDecorationThickness) {
28 // Compute the gap between the font and the underline. Use at least one
29 // pixel gap, if underline is thick then use a bigger gap.
eae 2016/10/13 18:18:54 Is this CSS or device pixels? If CSS pixel we prob
30 int gap = 0;
31
32 // Underline position of zero means draw underline on Baseline Position,
33 // in Blink we need at least 1-pixel gap to adding following check.
34 // Positive underline Position means underline should be drawn above baselin e
35 // and negative value means drawing below baseline, negating the value as in
36 // Blink
eae 2016/10/13 18:18:54 odd line-wrapping.
37 // downward Y-increases.
38
39 if (fontMetrics.underlinePosition())
40 gap = -fontMetrics.underlinePosition();
41 else
42 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f));
43
44 // FIXME: We support only horizontal text for now.
eae 2016/10/13 18:18:54 Is this still accurate? The test case includes ver
drott 2016/10/13 18:39:29 All good questions and remarks... This code is old
45 switch (underlinePosition) {
46 case TextUnderlinePositionAuto:
47 return fontMetrics.ascent() +
48 gap; // Position underline near the alphabetic baseline.
49 case TextUnderlinePositionUnder: {
50 // Position underline relative to the under edge of the lowest element's
51 // content box.
52 const LayoutUnit offset =
53 inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
54 if (offset > 0)
55 return (inlineTextBox->logicalHeight() + gap + offset).toInt();
56 return (inlineTextBox->logicalHeight() + gap).toInt();
57 }
58 }
59
60 NOTREACHED();
61 return fontMetrics.ascent() + gap;
62 }
63
64 static bool shouldSetDecorationAntialias(TextDecorationStyle decorationStyle) {
65 return decorationStyle == TextDecorationStyleDotted ||
66 decorationStyle == TextDecorationStyleDashed;
67 }
68
69 static bool shouldSetDecorationAntialias(TextDecorationStyle underline,
70 TextDecorationStyle overline,
71 TextDecorationStyle linethrough) {
72 return shouldSetDecorationAntialias(underline) ||
73 shouldSetDecorationAntialias(overline) ||
74 shouldSetDecorationAntialias(linethrough);
75 }
76
77 static StrokeStyle textDecorationStyleToStrokeStyle(
78 TextDecorationStyle decorationStyle) {
79 StrokeStyle strokeStyle = SolidStroke;
80 switch (decorationStyle) {
81 case TextDecorationStyleSolid:
82 strokeStyle = SolidStroke;
83 break;
84 case TextDecorationStyleDouble:
85 strokeStyle = DoubleStroke;
86 break;
87 case TextDecorationStyleDotted:
88 strokeStyle = DottedStroke;
89 break;
90 case TextDecorationStyleDashed:
91 strokeStyle = DashedStroke;
92 break;
93 case TextDecorationStyleWavy:
94 strokeStyle = WavyStroke;
95 break;
96 }
97
98 return strokeStyle;
99 }
100
101 static void adjustStepToDecorationLength(float& step,
102 float& controlPointDistance,
103 float length) {
104 DCHECK_GT(step, 0);
105
106 if (length <= 0)
107 return;
108
109 unsigned stepCount = static_cast<unsigned>(length / step);
110
111 // Each Bezier curve starts at the same pixel that the previous one
112 // ended. We need to subtract (stepCount - 1) pixels when calculating the
113 // length covered to account for that.
114 float uncoveredLength = length - (stepCount * step - (stepCount - 1));
115 float adjustment = uncoveredLength / stepCount;
116 step += adjustment;
117 controlPointDistance += adjustment;
118 }
119
120 class AppliedDecorationPainter final {
121 STACK_ALLOCATED();
122
123 public:
124 AppliedDecorationPainter(GraphicsContext& context,
125 FloatPoint startPoint,
126 float width,
127 LayoutObject::AppliedTextDecoration& decoration,
128 float thickness,
129 float doubleOffset,
130 int wavyOffsetFactor,
131 bool antialiasDecoration)
132 : m_context(context),
133 m_startPoint(startPoint),
134 m_width(width),
135 m_decoration(decoration),
136 m_thickness(thickness),
137 m_doubleOffset(doubleOffset),
138 m_wavyOffsetFactor(wavyOffsetFactor),
139 m_shouldAntialias(antialiasDecoration){};
140
141 void paint();
142 FloatRect decorationBounds();
143
144 private:
145 void strokeWavyTextDecoration();
146
147 Path prepareWavyStrokePath();
148 Path prepareDottedDashedStrokePath();
149
150 GraphicsContext& m_context;
151 FloatPoint m_startPoint;
152 float m_width;
153 LayoutObject::AppliedTextDecoration& m_decoration;
154 float m_thickness;
155 const float m_doubleOffset;
156 const int m_wavyOffsetFactor;
157 bool m_shouldAntialias;
158 };
159
160 Path AppliedDecorationPainter::prepareDottedDashedStrokePath() {
161 // These coordinate transforms need to match what's happening in
162 // GraphicsContext's drawLineForText and drawLine.
163 int y = floorf(m_startPoint.y() + std::max<float>(m_thickness / 2.0f, 0.5f));
164 Path strokePath;
165 FloatPoint roundedStartPoint(m_startPoint.x(), y);
166 FloatPoint roundedEndPoint(roundedStartPoint + FloatPoint(m_width, 0));
167 m_context.adjustLineToPixelBoundaries(roundedStartPoint, roundedEndPoint,
168 roundf(m_thickness),
169 m_context.getStrokeStyle());
170 strokePath.moveTo(roundedStartPoint);
171 strokePath.addLineTo(roundedEndPoint);
172 return strokePath;
173 }
174
175 FloatRect AppliedDecorationPainter::decorationBounds() {
176 StrokeData strokeData;
177 strokeData.setThickness(m_thickness);
178
179 switch (m_decoration.style) {
180 case TextDecorationStyleDotted:
181 case TextDecorationStyleDashed: {
182 strokeData.setStyle(textDecorationStyleToStrokeStyle(m_decoration.style));
183 return prepareDottedDashedStrokePath().strokeBoundingRect(
184 strokeData, Path::BoundsType::Exact);
185 }
186 case TextDecorationStyleWavy:
187 return prepareWavyStrokePath().strokeBoundingRect(
188 strokeData, Path::BoundsType::Exact);
189 break;
190 case TextDecorationStyleDouble:
191 if (m_doubleOffset > 0) {
192 return FloatRect(m_startPoint.x(), m_startPoint.y(), m_width,
193 m_doubleOffset + m_thickness);
194 }
195 return FloatRect(m_startPoint.x(), m_startPoint.y() + m_doubleOffset,
196 m_width, -m_doubleOffset + m_thickness);
197 break;
198 case TextDecorationStyleSolid:
199 return FloatRect(m_startPoint.x(), m_startPoint.y(), m_width,
200 m_thickness);
201 default:
202 break;
203 }
204 NOTREACHED();
205 return FloatRect();
206 }
207
208 void AppliedDecorationPainter::paint() {
209 m_context.setStrokeStyle(
210 textDecorationStyleToStrokeStyle(m_decoration.style));
211 m_context.setStrokeColor(m_decoration.color);
212
213 switch (m_decoration.style) {
214 case TextDecorationStyleWavy:
215 strokeWavyTextDecoration();
216 break;
217 case TextDecorationStyleDotted:
218 case TextDecorationStyleDashed:
219 m_context.setShouldAntialias(m_shouldAntialias);
220 // Fall through
221 default:
222 m_context.drawLineForText(m_startPoint, m_width);
223
224 if (m_decoration.style == TextDecorationStyleDouble) {
225 m_context.drawLineForText(m_startPoint + FloatPoint(0, m_doubleOffset),
226 m_width);
227 }
228 }
229 }
230
231 void AppliedDecorationPainter::strokeWavyTextDecoration() {
232 m_context.setShouldAntialias(true);
233 m_context.strokePath(prepareWavyStrokePath());
234 }
235
236 /*
237 * Prepare a path for a cubic Bezier curve and repeat the same pattern long the
238 * the decoration's axis. The start point (p1), controlPoint1, controlPoint2
239 * and end point (p2) of the Bezier curve form a diamond shape:
240 *
241 * step
242 * |-----------|
243 *
244 * controlPoint1
245 * +
246 *
247 *
248 * . .
249 * . .
250 * . .
251 * (x1, y1) p1 + . + p2 (x2, y2) - <--- Decoration's axis
252 * . . |
253 * . . |
254 * . . | controlPointDistance
255 * |
256 * |
257 * + -
258 * controlPoint2
259 *
260 * |-----------|
261 * step
262 */
263 Path AppliedDecorationPainter::prepareWavyStrokePath() {
264 FloatPoint p1(m_startPoint +
265 FloatPoint(0, m_doubleOffset + m_wavyOffsetFactor));
266 FloatPoint p2(m_startPoint + FloatPoint(m_width, 0));
267
268 m_context.adjustLineToPixelBoundaries(p1, p2, m_thickness,
269 m_context.getStrokeStyle());
270
271 Path path;
272 path.moveTo(p1);
273
274 // Distance between decoration's axis and Bezier curve's control points.
275 // The height of the curve is based on this distance. Use a minimum of 6
276 // pixels distance since
eae 2016/10/13 18:18:54 Odd line wrapping again here and below on line 280
277 // the actual curve passes approximately at half of that distance, that is 3
278 // pixels.
279 // The minimum height of the curve is also approximately 3 pixels. Increases
280 // the curve's height
281 // as strockThickness increases to make the curve looks better.
282 float controlPointDistance = 3 * std::max<float>(2, m_thickness);
283
284 // Increment used to form the diamond shape between start point (p1), control
285 // points and end point (p2) along the axis of the decoration. Makes the
286 // curve wider as strockThickness increases to make the curve looks better.
287 float step = 2 * std::max<float>(2, m_thickness);
288
289 bool isVerticalLine = (p1.x() == p2.x());
290
291 if (isVerticalLine) {
292 DCHECK(p1.x() == p2.x());
293
294 float xAxis = p1.x();
295 float y1;
296 float y2;
297
298 if (p1.y() < p2.y()) {
299 y1 = p1.y();
300 y2 = p2.y();
301 } else {
302 y1 = p2.y();
303 y2 = p1.y();
304 }
305
306 adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
307 FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
308 FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
309
310 for (float y = y1; y + 2 * step <= y2;) {
311 controlPoint1.setY(y + step);
312 controlPoint2.setY(y + step);
313 y += 2 * step;
314 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
315 }
316 } else {
317 DCHECK(p1.y() == p2.y());
318
319 float yAxis = p1.y();
320 float x1;
321 float x2;
322
323 if (p1.x() < p2.x()) {
324 x1 = p1.x();
325 x2 = p2.x();
326 } else {
327 x1 = p2.x();
328 x2 = p1.x();
329 }
330
331 adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
332 FloatPoint controlPoint1(0, yAxis + controlPointDistance);
333 FloatPoint controlPoint2(0, yAxis - controlPointDistance);
334
335 for (float x = x1; x + 2 * step <= x2;) {
336 controlPoint1.setX(x + step);
337 controlPoint2.setX(x + step);
338 x += 2 * step;
339 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
340 }
341 }
342 return path;
343 }
344
24 typedef WTF::HashMap<const InlineTextBox*, TextBlobPtr> 345 typedef WTF::HashMap<const InlineTextBox*, TextBlobPtr>
25 InlineTextBoxBlobCacheMap; 346 InlineTextBoxBlobCacheMap;
26 static InlineTextBoxBlobCacheMap* gTextBlobCache; 347 static InlineTextBoxBlobCacheMap* gTextBlobCache;
27 348
28 static const int misspellingLineThickness = 3; 349 static const int misspellingLineThickness = 3;
29 350
30 void InlineTextBoxPainter::removeFromTextBlobCache( 351 void InlineTextBoxPainter::removeFromTextBlobCache(
31 const InlineTextBox& inlineTextBox) { 352 const InlineTextBox& inlineTextBox) {
32 if (gTextBlobCache) 353 if (gTextBlobCache)
33 gTextBlobCache->remove(&inlineTextBox); 354 gTextBlobCache->remove(&inlineTextBox);
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 selectionStart < selectionEnd) { 595 selectionStart < selectionEnd) {
275 // paint only the text that is selected 596 // paint only the text that is selected
276 bool textBlobIsCacheable = selectionStart == 0 && selectionEnd == length; 597 bool textBlobIsCacheable = selectionStart == 0 && selectionEnd == length;
277 TextBlobPtr* cachedTextBlob = 0; 598 TextBlobPtr* cachedTextBlob = 0;
278 if (textBlobIsCacheable) 599 if (textBlobIsCacheable)
279 cachedTextBlob = addToTextBlobCache(m_inlineTextBox); 600 cachedTextBlob = addToTextBlobCache(m_inlineTextBox);
280 textPainter.paint(selectionStart, selectionEnd, length, selectionStyle, 601 textPainter.paint(selectionStart, selectionEnd, length, selectionStyle,
281 cachedTextBlob); 602 cachedTextBlob);
282 } 603 }
283 604
605 // If styleToUse has text-decoration-skip: ink;, then we need to compute ink
606 // skips here, pass them to paintDecoration, or pass the textrun or to
607 // paintDecoration. We probably need a new interface on Font.h that works
608 // on a TextRunPaintInfo to retrieve the skip intervals.
609
610 // On the other hand, we can compute different skip intervals based on the
611 // height and width of the underline or line-through, so it is probably
612 // better to compute them inside paintDecoration, where the underline
613 // positions are determined.
614
284 // Paint decorations 615 // Paint decorations
285 TextDecoration textDecorations = styleToUse.textDecorationsInEffect(); 616 TextDecoration textDecorations = styleToUse.textDecorationsInEffect();
286 if (textDecorations != TextDecorationNone && !paintSelectedTextOnly) { 617 if (textDecorations != TextDecorationNone && !paintSelectedTextOnly) {
287 GraphicsContextStateSaver stateSaver(context, false); 618 GraphicsContextStateSaver stateSaver(context, false);
619
288 TextPainter::updateGraphicsContext( 620 TextPainter::updateGraphicsContext(
289 context, textStyle, m_inlineTextBox.isHorizontal(), stateSaver); 621 context, textStyle, m_inlineTextBox.isHorizontal(), stateSaver);
622
623 // The matrix transform for the case of combinedText reverts the current
624 // rotation that is used for the combined painting of the text and
625 // restores painting along the baseline axis of the text.
290 if (combinedText) 626 if (combinedText)
291 context.concatCTM(TextPainter::rotation(boxRect, TextPainter::Clockwise)); 627 context.concatCTM(TextPainter::rotation(boxRect, TextPainter::Clockwise));
292 paintDecoration(paintInfo, boxOrigin, textDecorations); 628 paintDecoration(textPainter, paintInfo, boxOrigin, textDecorations);
293 if (combinedText) 629 if (combinedText)
294 context.concatCTM( 630 context.concatCTM(
295 TextPainter::rotation(boxRect, TextPainter::Counterclockwise)); 631 TextPainter::rotation(boxRect, TextPainter::Counterclockwise));
296 } 632 }
297 633
298 if (paintInfo.phase == PaintPhaseForeground) 634 if (paintInfo.phase == PaintPhaseForeground)
299 paintDocumentMarkers(paintInfo, boxOrigin, styleToUse, font, 635 paintDocumentMarkers(paintInfo, boxOrigin, styleToUse, font,
300 DocumentMarkerPaintPhase::Foreground); 636 DocumentMarkerPaintPhase::Foreground);
301 637
302 if (shouldRotate) 638 if (shouldRotate)
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after
669 LayoutRect& rect) { 1005 LayoutRect& rect) {
670 FloatRectOutsets outsets = FloatRectOutsets(); 1006 FloatRectOutsets outsets = FloatRectOutsets();
671 float spaceWidth = m_inlineTextBox.newlineSpaceWidth(); 1007 float spaceWidth = m_inlineTextBox.newlineSpaceWidth();
672 if (m_inlineTextBox.isLeftToRightDirection()) 1008 if (m_inlineTextBox.isLeftToRightDirection())
673 outsets.setRight(spaceWidth); 1009 outsets.setRight(spaceWidth);
674 else 1010 else
675 outsets.setLeft(spaceWidth); 1011 outsets.setLeft(spaceWidth);
676 rect.expand(outsets); 1012 rect.expand(outsets);
677 } 1013 }
678 1014
679 static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, 1015 void InlineTextBoxPainter::paintDecoration(TextPainter& textPainter,
680 const FontMetrics& fontMetrics, 1016 const PaintInfo& paintInfo,
681 const InlineTextBox* inlineTextBox,
682 const float textDecorationThickness) {
683 // Compute the gap between the font and the underline. Use at least one
684 // pixel gap, if underline is thick then use a bigger gap.
685 int gap = 0;
686
687 // Underline position of zero means draw underline on Baseline Position,
688 // in Blink we need at least 1-pixel gap to adding following check.
689 // Positive underline Position means underline should be drawn above baselin e
690 // and negative value means drawing below baseline, negating the value as in
691 // Blink downward Y-increases.
692
693 if (fontMetrics.underlinePosition())
694 gap = -fontMetrics.underlinePosition();
695 else
696 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f));
697
698 // FIXME: We support only horizontal text for now.
699 switch (underlinePosition) {
700 case TextUnderlinePositionAuto:
701 return fontMetrics.ascent() +
702 gap; // Position underline near the alphabetic baseline.
703 case TextUnderlinePositionUnder: {
704 // Position underline relative to the under edge of the lowest element's
705 // content box.
706 const LayoutUnit offset =
707 inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
708 if (offset > 0)
709 return (inlineTextBox->logicalHeight() + gap + offset).toInt();
710 return (inlineTextBox->logicalHeight() + gap).toInt();
711 }
712 }
713
714 ASSERT_NOT_REACHED();
715 return fontMetrics.ascent() + gap;
716 }
717
718 static bool shouldSetDecorationAntialias(TextDecorationStyle decorationStyle) {
719 return decorationStyle == TextDecorationStyleDotted ||
720 decorationStyle == TextDecorationStyleDashed;
721 }
722
723 static bool shouldSetDecorationAntialias(TextDecorationStyle underline,
724 TextDecorationStyle overline,
725 TextDecorationStyle linethrough) {
726 return shouldSetDecorationAntialias(underline) ||
727 shouldSetDecorationAntialias(overline) ||
728 shouldSetDecorationAntialias(linethrough);
729 }
730
731 static StrokeStyle textDecorationStyleToStrokeStyle(
732 TextDecorationStyle decorationStyle) {
733 StrokeStyle strokeStyle = SolidStroke;
734 switch (decorationStyle) {
735 case TextDecorationStyleSolid:
736 strokeStyle = SolidStroke;
737 break;
738 case TextDecorationStyleDouble:
739 strokeStyle = DoubleStroke;
740 break;
741 case TextDecorationStyleDotted:
742 strokeStyle = DottedStroke;
743 break;
744 case TextDecorationStyleDashed:
745 strokeStyle = DashedStroke;
746 break;
747 case TextDecorationStyleWavy:
748 strokeStyle = WavyStroke;
749 break;
750 }
751
752 return strokeStyle;
753 }
754
755 static void adjustStepToDecorationLength(float& step,
756 float& controlPointDistance,
757 float length) {
758 ASSERT(step > 0);
759
760 if (length <= 0)
761 return;
762
763 unsigned stepCount = static_cast<unsigned>(length / step);
764
765 // Each Bezier curve starts at the same pixel that the previous one
766 // ended. We need to subtract (stepCount - 1) pixels when calculating the
767 // length covered to account for that.
768 float uncoveredLength = length - (stepCount * step - (stepCount - 1));
769 float adjustment = uncoveredLength / stepCount;
770 step += adjustment;
771 controlPointDistance += adjustment;
772 }
773
774 /*
775 * Draw one cubic Bezier curve and repeat the same pattern long the the
776 * decoration's axis. The start point (p1), controlPoint1, controlPoint2 and
777 * end point (p2) of the Bezier curve form a diamond shape:
778 *
779 * step
780 * |-----------|
781 *
782 * controlPoint1
783 * +
784 *
785 *
786 * . .
787 * . .
788 * . .
789 * (x1, y1) p1 + . + p2 (x2, y2) - <--- Decoration's axis
790 * . . |
791 * . . |
792 * . . | controlPointDistance
793 * |
794 * |
795 * + -
796 * controlPoint2
797 *
798 * |-----------|
799 * step
800 */
801 static void strokeWavyTextDecoration(GraphicsContext& context,
802 FloatPoint p1,
803 FloatPoint p2,
804 float strokeThickness) {
805 context.adjustLineToPixelBoundaries(p1, p2, strokeThickness,
806 context.getStrokeStyle());
807
808 Path path;
809 path.moveTo(p1);
810
811 // Distance between decoration's axis and Bezier curve's control points.
812 // The height of the curve is based on this distance. Use a minimum of 6
813 // pixels distance since the actual curve passes approximately at half of that
814 // distance, that is 3 pixels. The minimum height of the curve is also
815 // approximately 3 pixels. Increases the curve's height
816 // as strockThickness increases to make the curve looks better.
817 float controlPointDistance = 3 * std::max<float>(2, strokeThickness);
818
819 // Increment used to form the diamond shape between start point (p1), control
820 // points and end point (p2) along the axis of the decoration. Makes the
821 // curve wider as strockThickness increases to make the curve looks better.
822 float step = 2 * std::max<float>(2, strokeThickness);
823
824 bool isVerticalLine = (p1.x() == p2.x());
825
826 if (isVerticalLine) {
827 ASSERT(p1.x() == p2.x());
828
829 float xAxis = p1.x();
830 float y1;
831 float y2;
832
833 if (p1.y() < p2.y()) {
834 y1 = p1.y();
835 y2 = p2.y();
836 } else {
837 y1 = p2.y();
838 y2 = p1.y();
839 }
840
841 adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
842 FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
843 FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
844
845 for (float y = y1; y + 2 * step <= y2;) {
846 controlPoint1.setY(y + step);
847 controlPoint2.setY(y + step);
848 y += 2 * step;
849 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
850 }
851 } else {
852 ASSERT(p1.y() == p2.y());
853
854 float yAxis = p1.y();
855 float x1;
856 float x2;
857
858 if (p1.x() < p2.x()) {
859 x1 = p1.x();
860 x2 = p2.x();
861 } else {
862 x1 = p2.x();
863 x2 = p1.x();
864 }
865
866 adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
867 FloatPoint controlPoint1(0, yAxis + controlPointDistance);
868 FloatPoint controlPoint2(0, yAxis - controlPointDistance);
869
870 for (float x = x1; x + 2 * step <= x2;) {
871 controlPoint1.setX(x + step);
872 controlPoint2.setX(x + step);
873 x += 2 * step;
874 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
875 }
876 }
877
878 context.setShouldAntialias(true);
879 context.strokePath(path);
880 }
881
882 static void paintAppliedDecoration(
883 GraphicsContext& context,
884 FloatPoint start,
885 float width,
886 float doubleOffset,
887 int wavyOffsetFactor,
888 LayoutObject::AppliedTextDecoration decoration,
889 float thickness,
890 bool antialiasDecoration,
891 bool isPrinting) {
892 context.setStrokeStyle(textDecorationStyleToStrokeStyle(decoration.style));
893 context.setStrokeColor(decoration.color);
894
895 switch (decoration.style) {
896 case TextDecorationStyleWavy:
897 strokeWavyTextDecoration(
898 context, start + FloatPoint(0, doubleOffset * wavyOffsetFactor),
899 start + FloatPoint(width, doubleOffset * wavyOffsetFactor),
900 thickness);
901 break;
902 case TextDecorationStyleDotted:
903 case TextDecorationStyleDashed:
904 context.setShouldAntialias(antialiasDecoration);
905 // Fall through
906 default:
907 context.drawLineForText(FloatPoint(start), width, isPrinting);
908
909 if (decoration.style == TextDecorationStyleDouble)
910 context.drawLineForText(start + FloatPoint(0, doubleOffset), width,
911 isPrinting);
912 }
913 }
914
915 void InlineTextBoxPainter::paintDecoration(const PaintInfo& paintInfo,
916 const LayoutPoint& boxOrigin, 1017 const LayoutPoint& boxOrigin,
917 TextDecoration deco) { 1018 TextDecoration deco) {
918 if (m_inlineTextBox.truncation() == cFullTruncation) 1019 if (m_inlineTextBox.truncation() == cFullTruncation)
919 return; 1020 return;
920 1021
921 GraphicsContext& context = paintInfo.context; 1022 GraphicsContext& context = paintInfo.context;
922 GraphicsContextStateSaver stateSaver(context); 1023 GraphicsContextStateSaver stateSaver(context);
923 1024
924 LayoutPoint localOrigin(boxOrigin); 1025 LayoutPoint localOrigin(boxOrigin);
925 1026
(...skipping 15 matching lines...) Expand all
941 1042
942 // Get the text decoration colors. 1043 // Get the text decoration colors.
943 LayoutObject::AppliedTextDecoration underline, overline, linethrough; 1044 LayoutObject::AppliedTextDecoration underline, overline, linethrough;
944 LayoutObject& textBoxLayoutObject = inlineLayoutObject(); 1045 LayoutObject& textBoxLayoutObject = inlineLayoutObject();
945 textBoxLayoutObject.getTextDecorations(deco, underline, overline, linethrough, 1046 textBoxLayoutObject.getTextDecorations(deco, underline, overline, linethrough,
946 true); 1047 true);
947 if (m_inlineTextBox.isFirstLineStyle()) 1048 if (m_inlineTextBox.isFirstLineStyle())
948 textBoxLayoutObject.getTextDecorations(deco, underline, overline, 1049 textBoxLayoutObject.getTextDecorations(deco, underline, overline,
949 linethrough, true, true); 1050 linethrough, true, true);
950 1051
951 // Use a special function for underlines to get the positioning exactly right.
952 bool isPrinting = paintInfo.isPrinting();
953
954 const ComputedStyle& styleToUse = 1052 const ComputedStyle& styleToUse =
955 textBoxLayoutObject.styleRef(m_inlineTextBox.isFirstLineStyle()); 1053 textBoxLayoutObject.styleRef(m_inlineTextBox.isFirstLineStyle());
956 float baseline = styleToUse.getFontMetrics().ascent(); 1054 float baseline = styleToUse.getFontMetrics().floatAscent();
957 1055
958 // Set the thick of the line to be 10% (or something else ?)of the computed 1056 // Set the thick of the line to be 10% (or something else ?)of the computed
959 // font size and not less than 1px. Using computedFontSize should take care 1057 // font size and not less than 1px. Using computedFontSize should take care
960 // of zoom as well. 1058 // of zoom as well.
961 1059
962 // Update Underline thickness, in case we have Faulty Font Metrics calculating 1060 // Update Underline thickness, in case we have Faulty Font Metrics calculating
963 // underline thickness by old method. 1061 // underline thickness by old method.
964 float textDecorationThickness = 1062 float textDecorationThickness =
965 styleToUse.getFontMetrics().underlineThickness(); 1063 styleToUse.getFontMetrics().underlineThickness();
966 int fontHeightInt = (int)(styleToUse.getFontMetrics().floatHeight() + 0.5); 1064 int fontHeightInt = (int)(styleToUse.getFontMetrics().floatHeight() + 0.5);
967 if ((textDecorationThickness == 0.f) || 1065 if ((textDecorationThickness == 0.f) ||
968 (textDecorationThickness >= (fontHeightInt >> 1))) 1066 (textDecorationThickness >= (fontHeightInt >> 1)))
969 textDecorationThickness = 1067 textDecorationThickness =
970 std::max(1.f, styleToUse.computedFontSize() / 10.f); 1068 std::max(1.f, styleToUse.computedFontSize() / 10.f);
971 1069
972 context.setStrokeThickness(textDecorationThickness); 1070 context.setStrokeThickness(textDecorationThickness);
973 1071
974 bool antialiasDecoration = shouldSetDecorationAntialias( 1072 bool antialiasDecoration = shouldSetDecorationAntialias(
975 overline.style, underline.style, linethrough.style); 1073 overline.style, underline.style, linethrough.style);
976 1074
977 // Offset between lines - always non-zero, so lines never cross each other. 1075 // Offset between lines - always non-zero, so lines never cross each other.
978 float doubleOffset = textDecorationThickness + 1.f; 1076 float doubleOffset = textDecorationThickness + 1.f;
1077 bool skipIntercepts =
1078 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk;
979 1079
980 if (deco & TextDecorationUnderline) { 1080 if (deco & TextDecorationUnderline) {
981 const int underlineOffset = computeUnderlineOffset( 1081 const int underlineOffset = computeUnderlineOffset(
982 styleToUse.getTextUnderlinePosition(), styleToUse.getFontMetrics(), 1082 styleToUse.getTextUnderlinePosition(), styleToUse.getFontMetrics(),
983 &m_inlineTextBox, textDecorationThickness); 1083 &m_inlineTextBox, textDecorationThickness);
984 paintAppliedDecoration( 1084 AppliedDecorationPainter decorationPainter(
985 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), 1085 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset),
986 width.toFloat(), doubleOffset, 1, underline, textDecorationThickness, 1086 width.toFloat(), underline, textDecorationThickness, doubleOffset, 1,
987 antialiasDecoration, isPrinting); 1087 antialiasDecoration);
1088 if (skipIntercepts) {
1089 textPainter.clipDecorationsStripe(
1090 -baseline + decorationPainter.decorationBounds().y() -
1091 FloatPoint(localOrigin).y(),
1092 decorationPainter.decorationBounds().height(),
1093 textDecorationThickness);
1094 }
1095 decorationPainter.paint();
988 } 1096 }
989 if (deco & TextDecorationOverline) { 1097 if (deco & TextDecorationOverline) {
990 paintAppliedDecoration(context, FloatPoint(localOrigin), width.toFloat(), 1098 AppliedDecorationPainter decorationPainter(
991 -doubleOffset, 1, overline, textDecorationThickness, 1099 context, FloatPoint(localOrigin), width.toFloat(), overline,
992 antialiasDecoration, isPrinting); 1100 textDecorationThickness, -doubleOffset, 1, antialiasDecoration);
1101 if (skipIntercepts) {
1102 textPainter.clipDecorationsStripe(
1103 -baseline + decorationPainter.decorationBounds().y() -
1104 FloatPoint(localOrigin).y(),
1105 decorationPainter.decorationBounds().height(),
1106 textDecorationThickness);
1107 }
1108 decorationPainter.paint();
993 } 1109 }
994 if (deco & TextDecorationLineThrough) { 1110 if (deco & TextDecorationLineThrough) {
995 const float lineThroughOffset = 2 * baseline / 3; 1111 const float lineThroughOffset = 2 * baseline / 3;
996 paintAppliedDecoration( 1112 AppliedDecorationPainter decorationPainter(
997 context, FloatPoint(localOrigin) + FloatPoint(0, lineThroughOffset), 1113 context, FloatPoint(localOrigin) + FloatPoint(0, lineThroughOffset),
998 width.toFloat(), doubleOffset, 0, linethrough, textDecorationThickness, 1114 width.toFloat(), linethrough, textDecorationThickness, doubleOffset, 0,
999 antialiasDecoration, isPrinting); 1115 antialiasDecoration);
1116 if (skipIntercepts) {
1117 textPainter.clipDecorationsStripe(
1118 -baseline + decorationPainter.decorationBounds().y() -
1119 FloatPoint(localOrigin).y(),
1120 decorationPainter.decorationBounds().height(),
1121 textDecorationThickness);
1122 }
1123 decorationPainter.paint();
1000 } 1124 }
1001 } 1125 }
1002 1126
1003 void InlineTextBoxPainter::paintCompositionUnderline( 1127 void InlineTextBoxPainter::paintCompositionUnderline(
1004 GraphicsContext& context, 1128 GraphicsContext& context,
1005 const LayoutPoint& boxOrigin, 1129 const LayoutPoint& boxOrigin,
1006 const CompositionUnderline& underline) { 1130 const CompositionUnderline& underline) {
1007 if (underline.color() == Color::transparent) 1131 if (underline.color() == Color::transparent)
1008 return; 1132 return;
1009 1133
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
1067 start += 1; 1191 start += 1;
1068 width -= 2; 1192 width -= 2;
1069 1193
1070 context.setStrokeColor(underline.color()); 1194 context.setStrokeColor(underline.color());
1071 context.setStrokeThickness(lineThickness); 1195 context.setStrokeThickness(lineThickness);
1072 context.drawLineForText( 1196 context.drawLineForText(
1073 FloatPoint( 1197 FloatPoint(
1074 boxOrigin.x() + start, 1198 boxOrigin.x() + start,
1075 (boxOrigin.y() + m_inlineTextBox.logicalHeight() - lineThickness) 1199 (boxOrigin.y() + m_inlineTextBox.logicalHeight() - lineThickness)
1076 .toFloat()), 1200 .toFloat()),
1077 width, m_inlineTextBox.getLineLayoutItem().document().printing()); 1201 width);
1078 } 1202 }
1079 1203
1080 void InlineTextBoxPainter::paintTextMatchMarkerForeground( 1204 void InlineTextBoxPainter::paintTextMatchMarkerForeground(
1081 const PaintInfo& paintInfo, 1205 const PaintInfo& paintInfo,
1082 const LayoutPoint& boxOrigin, 1206 const LayoutPoint& boxOrigin,
1083 DocumentMarker* marker, 1207 DocumentMarker* marker,
1084 const ComputedStyle& style, 1208 const ComputedStyle& style,
1085 const Font& font) { 1209 const Font& font) {
1086 if (!inlineLayoutObject().frame()->editor().markedTextMatchesAreHighlighted()) 1210 if (!inlineLayoutObject().frame()->editor().markedTextMatchesAreHighlighted())
1087 return; 1211 return;
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1138 GraphicsContextStateSaver stateSaver(context); 1262 GraphicsContextStateSaver stateSaver(context);
1139 1263
1140 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), 1264 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(),
1141 m_inlineTextBox.logicalHeight())); 1265 m_inlineTextBox.logicalHeight()));
1142 context.clip(FloatRect(boxRect)); 1266 context.clip(FloatRect(boxRect));
1143 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), 1267 context.drawHighlightForText(font, run, FloatPoint(boxOrigin),
1144 boxRect.height().toInt(), color, sPos, ePos); 1268 boxRect.height().toInt(), color, sPos, ePos);
1145 } 1269 }
1146 1270
1147 } // namespace blink 1271 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698