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

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

Issue 2416993002: Introduce support for text-decoration-skip: ink (Closed)
Patch Set: Falling back to adding rebaseline expectation Created 4 years, 1 month 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 "core/style/AppliedTextDecoration.h" 18 #include "core/style/AppliedTextDecoration.h"
19 #include "platform/graphics/GraphicsContextStateSaver.h" 19 #include "platform/graphics/GraphicsContextStateSaver.h"
20 #include "platform/graphics/paint/DrawingRecorder.h" 20 #include "platform/graphics/paint/DrawingRecorder.h"
21 #include "wtf/Optional.h" 21 #include "wtf/Optional.h"
22 22
23 namespace blink { 23 namespace blink {
24 24
25 static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition,
26 const FontMetrics& fontMetrics,
27 const InlineTextBox* inlineTextBox,
28 const float textDecorationThickness) {
29 // Compute the gap between the font and the underline. Use at least one
30 // pixel gap, if underline is thick then use a bigger gap.
31 int gap = 0;
32
33 // Underline position of zero means draw underline on Baseline Position,
34 // in Blink we need at least 1-pixel gap to adding following check.
35 // Positive underline Position means underline should be drawn above baselin e
36 // and negative value means drawing below baseline, negating the value as in
37 // Blink
38 // downward Y-increases.
39
40 if (fontMetrics.underlinePosition())
41 gap = -fontMetrics.underlinePosition();
42 else
43 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f));
44
45 // FIXME: We support only horizontal text for now.
46 switch (underlinePosition) {
47 case TextUnderlinePositionAuto:
48 return fontMetrics.ascent() +
49 gap; // Position underline near the alphabetic baseline.
50 case TextUnderlinePositionUnder: {
51 // Position underline relative to the under edge of the lowest element's
52 // content box.
53 const LayoutUnit offset =
54 inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
55 if (offset > 0)
56 return (inlineTextBox->logicalHeight() + gap + offset).toInt();
57 return (inlineTextBox->logicalHeight() + gap).toInt();
58 }
59 }
60
61 NOTREACHED();
62 return fontMetrics.ascent() + gap;
63 }
64
65 static bool shouldSetDecorationAntialias(
66 const Vector<AppliedTextDecoration>& decorations) {
67 for (const AppliedTextDecoration& decoration : decorations) {
68 TextDecorationStyle decorationStyle = decoration.style();
69 if (decorationStyle == TextDecorationStyleDotted ||
70 decorationStyle == TextDecorationStyleDashed)
71 return true;
72 }
73 return false;
74 }
75
76 static StrokeStyle textDecorationStyleToStrokeStyle(
77 TextDecorationStyle decorationStyle) {
78 StrokeStyle strokeStyle = SolidStroke;
79 switch (decorationStyle) {
80 case TextDecorationStyleSolid:
81 strokeStyle = SolidStroke;
82 break;
83 case TextDecorationStyleDouble:
84 strokeStyle = DoubleStroke;
85 break;
86 case TextDecorationStyleDotted:
87 strokeStyle = DottedStroke;
88 break;
89 case TextDecorationStyleDashed:
90 strokeStyle = DashedStroke;
91 break;
92 case TextDecorationStyleWavy:
93 strokeStyle = WavyStroke;
94 break;
95 }
96
97 return strokeStyle;
98 }
99
100 static void adjustStepToDecorationLength(float& step,
101 float& controlPointDistance,
102 float length) {
103 DCHECK_GT(step, 0);
104
105 if (length <= 0)
106 return;
107
108 unsigned stepCount = static_cast<unsigned>(length / step);
109
110 // Each Bezier curve starts at the same pixel that the previous one
111 // ended. We need to subtract (stepCount - 1) pixels when calculating the
112 // length covered to account for that.
113 float uncoveredLength = length - (stepCount * step - (stepCount - 1));
114 float adjustment = uncoveredLength / stepCount;
115 step += adjustment;
116 controlPointDistance += adjustment;
117 }
118
119 class AppliedDecorationPainter final {
120 STACK_ALLOCATED();
121
122 public:
123 AppliedDecorationPainter(GraphicsContext& context,
124 FloatPoint startPoint,
125 float width,
126 const AppliedTextDecoration& decoration,
127 float thickness,
128 float doubleOffset,
129 int wavyOffsetFactor,
130 bool antialiasDecoration)
131 : m_context(context),
132 m_startPoint(startPoint),
133 m_width(width),
134 m_decoration(decoration),
135 m_thickness(thickness),
136 m_doubleOffset(doubleOffset),
137 m_wavyOffsetFactor(wavyOffsetFactor),
138 m_shouldAntialias(antialiasDecoration){};
139
140 void paint();
141 FloatRect decorationBounds();
142
143 private:
144 void strokeWavyTextDecoration();
145
146 Path prepareWavyStrokePath();
147 Path prepareDottedDashedStrokePath();
148
149 GraphicsContext& m_context;
150 FloatPoint m_startPoint;
151 float m_width;
152 const AppliedTextDecoration& m_decoration;
153 float m_thickness;
154 const float m_doubleOffset;
155 const int m_wavyOffsetFactor;
156 bool m_shouldAntialias;
157 };
158
159 Path AppliedDecorationPainter::prepareDottedDashedStrokePath() {
160 // These coordinate transforms need to match what's happening in
161 // GraphicsContext's drawLineForText and drawLine.
162 int y = floorf(m_startPoint.y() + std::max<float>(m_thickness / 2.0f, 0.5f));
163 Path strokePath;
164 FloatPoint roundedStartPoint(m_startPoint.x(), y);
165 FloatPoint roundedEndPoint(roundedStartPoint + FloatPoint(m_width, 0));
166 m_context.adjustLineToPixelBoundaries(roundedStartPoint, roundedEndPoint,
167 roundf(m_thickness),
168 m_context.getStrokeStyle());
169 strokePath.moveTo(roundedStartPoint);
170 strokePath.addLineTo(roundedEndPoint);
171 return strokePath;
172 }
173
174 FloatRect AppliedDecorationPainter::decorationBounds() {
175 StrokeData strokeData;
176 strokeData.setThickness(m_thickness);
177
178 switch (m_decoration.style()) {
179 case TextDecorationStyleDotted:
180 case TextDecorationStyleDashed: {
181 strokeData.setStyle(
182 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 +
267 FloatPoint(m_width, m_doubleOffset * m_wavyOffsetFactor));
268
269 m_context.adjustLineToPixelBoundaries(p1, p2, m_thickness,
270 m_context.getStrokeStyle());
271
272 Path path;
273 path.moveTo(p1);
274
275 // Distance between decoration's axis and Bezier curve's control points.
276 // The height of the curve is based on this distance. Use a minimum of 6
277 // pixels distance since
278 // the actual curve passes approximately at half of that distance, that is 3
279 // pixels.
280 // The minimum height of the curve is also approximately 3 pixels. Increases
281 // the curve's height
282 // as strockThickness increases to make the curve looks better.
283 float controlPointDistance = 3 * std::max<float>(2, m_thickness);
284
285 // Increment used to form the diamond shape between start point (p1), control
286 // points and end point (p2) along the axis of the decoration. Makes the
287 // curve wider as strockThickness increases to make the curve looks better.
288 float step = 2 * std::max<float>(2, m_thickness);
289
290 bool isVerticalLine = (p1.x() == p2.x());
291
292 if (isVerticalLine) {
293 DCHECK(p1.x() == p2.x());
294
295 float xAxis = p1.x();
296 float y1;
297 float y2;
298
299 if (p1.y() < p2.y()) {
300 y1 = p1.y();
301 y2 = p2.y();
302 } else {
303 y1 = p2.y();
304 y2 = p1.y();
305 }
306
307 adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
308 FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
309 FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
310
311 for (float y = y1; y + 2 * step <= y2;) {
312 controlPoint1.setY(y + step);
313 controlPoint2.setY(y + step);
314 y += 2 * step;
315 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
316 }
317 } else {
318 DCHECK(p1.y() == p2.y());
319
320 float yAxis = p1.y();
321 float x1;
322 float x2;
323
324 if (p1.x() < p2.x()) {
325 x1 = p1.x();
326 x2 = p2.x();
327 } else {
328 x1 = p2.x();
329 x2 = p1.x();
330 }
331
332 adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
333 FloatPoint controlPoint1(0, yAxis + controlPointDistance);
334 FloatPoint controlPoint2(0, yAxis - controlPointDistance);
335
336 for (float x = x1; x + 2 * step <= x2;) {
337 controlPoint1.setX(x + step);
338 controlPoint2.setX(x + step);
339 x += 2 * step;
340 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
341 }
342 }
343 return path;
344 }
345
25 typedef WTF::HashMap<const InlineTextBox*, TextBlobPtr> 346 typedef WTF::HashMap<const InlineTextBox*, TextBlobPtr>
26 InlineTextBoxBlobCacheMap; 347 InlineTextBoxBlobCacheMap;
27 static InlineTextBoxBlobCacheMap* gTextBlobCache; 348 static InlineTextBoxBlobCacheMap* gTextBlobCache;
28 349
29 static const int misspellingLineThickness = 3; 350 static const int misspellingLineThickness = 3;
30 351
31 void InlineTextBoxPainter::removeFromTextBlobCache( 352 void InlineTextBoxPainter::removeFromTextBlobCache(
32 const InlineTextBox& inlineTextBox) { 353 const InlineTextBox& inlineTextBox) {
33 if (gTextBlobCache) 354 if (gTextBlobCache)
34 gTextBlobCache->remove(&inlineTextBox); 355 gTextBlobCache->remove(&inlineTextBox);
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after
281 if (textBlobIsCacheable) 602 if (textBlobIsCacheable)
282 cachedTextBlob = addToTextBlobCache(m_inlineTextBox); 603 cachedTextBlob = addToTextBlobCache(m_inlineTextBox);
283 textPainter.paint(selectionStart, selectionEnd, length, selectionStyle, 604 textPainter.paint(selectionStart, selectionEnd, length, selectionStyle,
284 cachedTextBlob); 605 cachedTextBlob);
285 } 606 }
286 607
287 // Paint decorations 608 // Paint decorations
288 if (styleToUse.textDecorationsInEffect() != TextDecorationNone && 609 if (styleToUse.textDecorationsInEffect() != TextDecorationNone &&
289 !paintSelectedTextOnly) { 610 !paintSelectedTextOnly) {
290 GraphicsContextStateSaver stateSaver(context, false); 611 GraphicsContextStateSaver stateSaver(context, false);
612
291 TextPainter::updateGraphicsContext( 613 TextPainter::updateGraphicsContext(
292 context, textStyle, m_inlineTextBox.isHorizontal(), stateSaver); 614 context, textStyle, m_inlineTextBox.isHorizontal(), stateSaver);
615
293 if (combinedText) 616 if (combinedText)
294 context.concatCTM(TextPainter::rotation(boxRect, TextPainter::Clockwise)); 617 context.concatCTM(TextPainter::rotation(boxRect, TextPainter::Clockwise));
295 paintDecorations(paintInfo, boxOrigin, styleToUse.appliedTextDecorations()); 618 paintDecorations(textPainter, paintInfo, boxOrigin,
619 styleToUse.appliedTextDecorations());
296 if (combinedText) 620 if (combinedText)
297 context.concatCTM( 621 context.concatCTM(
298 TextPainter::rotation(boxRect, TextPainter::Counterclockwise)); 622 TextPainter::rotation(boxRect, TextPainter::Counterclockwise));
299 } 623 }
300 624
301 if (paintInfo.phase == PaintPhaseForeground) 625 if (paintInfo.phase == PaintPhaseForeground)
302 paintDocumentMarkers(paintInfo, boxOrigin, styleToUse, font, 626 paintDocumentMarkers(paintInfo, boxOrigin, styleToUse, font,
303 DocumentMarkerPaintPhase::Foreground); 627 DocumentMarkerPaintPhase::Foreground);
304 628
305 if (shouldRotate) 629 if (shouldRotate)
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 LayoutRect& rect) { 1000 LayoutRect& rect) {
677 FloatRectOutsets outsets = FloatRectOutsets(); 1001 FloatRectOutsets outsets = FloatRectOutsets();
678 float spaceWidth = m_inlineTextBox.newlineSpaceWidth(); 1002 float spaceWidth = m_inlineTextBox.newlineSpaceWidth();
679 if (m_inlineTextBox.isLeftToRightDirection()) 1003 if (m_inlineTextBox.isLeftToRightDirection())
680 outsets.setRight(spaceWidth); 1004 outsets.setRight(spaceWidth);
681 else 1005 else
682 outsets.setLeft(spaceWidth); 1006 outsets.setLeft(spaceWidth);
683 rect.expand(outsets); 1007 rect.expand(outsets);
684 } 1008 }
685 1009
686 static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition,
687 const FontMetrics& fontMetrics,
688 const InlineTextBox* inlineTextBox,
689 const float textDecorationThickness) {
690 // Compute the gap between the font and the underline. Use at least one
691 // pixel gap, if underline is thick then use a bigger gap.
692 int gap = 0;
693
694 // Underline position of zero means draw underline on Baseline Position,
695 // in Blink we need at least 1-pixel gap to adding following check.
696 // Positive underline Position means underline should be drawn above baselin e
697 // and negative value means drawing below baseline, negating the value as in
698 // Blink downward Y-increases.
699
700 if (fontMetrics.underlinePosition())
701 gap = -fontMetrics.underlinePosition();
702 else
703 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f));
704
705 // FIXME: We support only horizontal text for now.
706 switch (underlinePosition) {
707 case TextUnderlinePositionAuto:
708 return fontMetrics.ascent() +
709 gap; // Position underline near the alphabetic baseline.
710 case TextUnderlinePositionUnder: {
711 // Position underline relative to the under edge of the lowest element's
712 // content box.
713 const LayoutUnit offset =
714 inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
715 if (offset > 0)
716 return (inlineTextBox->logicalHeight() + gap + offset).toInt();
717 return (inlineTextBox->logicalHeight() + gap).toInt();
718 }
719 }
720
721 ASSERT_NOT_REACHED();
722 return fontMetrics.ascent() + gap;
723 }
724
725 static bool shouldSetDecorationAntialias(
726 const Vector<AppliedTextDecoration>& decorations) {
727 for (const AppliedTextDecoration& decoration : decorations) {
728 TextDecorationStyle decorationStyle = decoration.style();
729 if (decorationStyle == TextDecorationStyleDotted ||
730 decorationStyle == TextDecorationStyleDashed)
731 return true;
732 }
733 return false;
734 }
735
736 static StrokeStyle textDecorationStyleToStrokeStyle(
737 TextDecorationStyle decorationStyle) {
738 StrokeStyle strokeStyle = SolidStroke;
739 switch (decorationStyle) {
740 case TextDecorationStyleSolid:
741 strokeStyle = SolidStroke;
742 break;
743 case TextDecorationStyleDouble:
744 strokeStyle = DoubleStroke;
745 break;
746 case TextDecorationStyleDotted:
747 strokeStyle = DottedStroke;
748 break;
749 case TextDecorationStyleDashed:
750 strokeStyle = DashedStroke;
751 break;
752 case TextDecorationStyleWavy:
753 strokeStyle = WavyStroke;
754 break;
755 }
756
757 return strokeStyle;
758 }
759
760 static void adjustStepToDecorationLength(float& step,
761 float& controlPointDistance,
762 float length) {
763 DCHECK_GT(step, 0);
764
765 if (length <= 0)
766 return;
767
768 unsigned stepCount = static_cast<unsigned>(length / step);
769
770 // Each Bezier curve starts at the same pixel that the previous one
771 // ended. We need to subtract (stepCount - 1) pixels when calculating the
772 // length covered to account for that.
773 float uncoveredLength = length - (stepCount * step - (stepCount - 1));
774 float adjustment = uncoveredLength / stepCount;
775 step += adjustment;
776 controlPointDistance += adjustment;
777 }
778
779 /*
780 * Draw one cubic Bezier curve and repeat the same pattern long the the
781 * decoration's axis. The start point (p1), controlPoint1, controlPoint2 and
782 * end point (p2) of the Bezier curve form a diamond shape:
783 *
784 * step
785 * |-----------|
786 *
787 * controlPoint1
788 * +
789 *
790 *
791 * . .
792 * . .
793 * . .
794 * (x1, y1) p1 + . + p2 (x2, y2) - <--- Decoration's axis
795 * . . |
796 * . . |
797 * . . | controlPointDistance
798 * |
799 * |
800 * + -
801 * controlPoint2
802 *
803 * |-----------|
804 * step
805 */
806 static void strokeWavyTextDecoration(GraphicsContext& context,
807 FloatPoint p1,
808 FloatPoint p2,
809 float strokeThickness) {
810 context.adjustLineToPixelBoundaries(p1, p2, strokeThickness,
811 context.getStrokeStyle());
812
813 Path path;
814 path.moveTo(p1);
815
816 // Distance between decoration's axis and Bezier curve's control points.
817 // The height of the curve is based on this distance. Use a minimum of 6
818 // pixels distance since the actual curve passes approximately at half of that
819 // distance, that is 3 pixels. The minimum height of the curve is also
820 // approximately 3 pixels. Increases the curve's height
821 // as strockThickness increases to make the curve looks better.
822 float controlPointDistance = 3 * std::max<float>(2, strokeThickness);
823
824 // Increment used to form the diamond shape between start point (p1), control
825 // points and end point (p2) along the axis of the decoration. Makes the
826 // curve wider as strockThickness increases to make the curve looks better.
827 float step = 2 * std::max<float>(2, strokeThickness);
828
829 bool isVerticalLine = (p1.x() == p2.x());
830
831 if (isVerticalLine) {
832 DCHECK(p1.x() == p2.x());
833
834 float xAxis = p1.x();
835 float y1;
836 float y2;
837
838 if (p1.y() < p2.y()) {
839 y1 = p1.y();
840 y2 = p2.y();
841 } else {
842 y1 = p2.y();
843 y2 = p1.y();
844 }
845
846 adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
847 FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
848 FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
849
850 for (float y = y1; y + 2 * step <= y2;) {
851 controlPoint1.setY(y + step);
852 controlPoint2.setY(y + step);
853 y += 2 * step;
854 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
855 }
856 } else {
857 DCHECK(p1.y() == p2.y());
858
859 float yAxis = p1.y();
860 float x1;
861 float x2;
862
863 if (p1.x() < p2.x()) {
864 x1 = p1.x();
865 x2 = p2.x();
866 } else {
867 x1 = p2.x();
868 x2 = p1.x();
869 }
870
871 adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
872 FloatPoint controlPoint1(0, yAxis + controlPointDistance);
873 FloatPoint controlPoint2(0, yAxis - controlPointDistance);
874
875 for (float x = x1; x + 2 * step <= x2;) {
876 controlPoint1.setX(x + step);
877 controlPoint2.setX(x + step);
878 x += 2 * step;
879 path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
880 }
881 }
882
883 context.setShouldAntialias(true);
884 context.strokePath(path);
885 }
886
887 static void paintAppliedDecoration(GraphicsContext& context,
888 FloatPoint start,
889 float width,
890 float doubleOffset,
891 int wavyOffsetFactor,
892 AppliedTextDecoration decoration,
893 float thickness,
894 bool antialiasDecoration,
895 bool isPrinting) {
896 context.setStrokeStyle(textDecorationStyleToStrokeStyle(decoration.style()));
897 context.setStrokeColor(decoration.color());
898
899 switch (decoration.style()) {
900 case TextDecorationStyleWavy:
901 strokeWavyTextDecoration(
902 context, start + FloatPoint(0, doubleOffset * wavyOffsetFactor),
903 start + FloatPoint(width, doubleOffset * wavyOffsetFactor),
904 thickness);
905 break;
906 case TextDecorationStyleDotted:
907 case TextDecorationStyleDashed:
908 context.setShouldAntialias(antialiasDecoration);
909 // Fall through
910 default:
911 context.drawLineForText(FloatPoint(start), width, isPrinting);
912
913 if (decoration.style() == TextDecorationStyleDouble)
914 context.drawLineForText(start + FloatPoint(0, doubleOffset), width,
915 isPrinting);
916 }
917 }
918
919 void InlineTextBoxPainter::paintDecorations( 1010 void InlineTextBoxPainter::paintDecorations(
1011 TextPainter& textPainter,
920 const PaintInfo& paintInfo, 1012 const PaintInfo& paintInfo,
921 const LayoutPoint& boxOrigin, 1013 const LayoutPoint& boxOrigin,
922 const Vector<AppliedTextDecoration>& decorations) { 1014 const Vector<AppliedTextDecoration>& decorations) {
923 if (m_inlineTextBox.truncation() == cFullTruncation) 1015 if (m_inlineTextBox.truncation() == cFullTruncation)
924 return; 1016 return;
925 1017
926 GraphicsContext& context = paintInfo.context; 1018 GraphicsContext& context = paintInfo.context;
927 GraphicsContextStateSaver stateSaver(context); 1019 GraphicsContextStateSaver stateSaver(context);
928 1020
929 LayoutPoint localOrigin(boxOrigin); 1021 LayoutPoint localOrigin(boxOrigin);
930 1022
931 LayoutUnit width = m_inlineTextBox.logicalWidth(); 1023 LayoutUnit width = m_inlineTextBox.logicalWidth();
932 if (m_inlineTextBox.truncation() != cNoTruncation) { 1024 if (m_inlineTextBox.truncation() != cNoTruncation) {
933 bool ltr = m_inlineTextBox.isLeftToRightDirection(); 1025 bool ltr = m_inlineTextBox.isLeftToRightDirection();
934 bool flowIsLTR = 1026 bool flowIsLTR =
935 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); 1027 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection();
936 width = LayoutUnit(m_inlineTextBox.getLineLayoutItem().width( 1028 width = LayoutUnit(m_inlineTextBox.getLineLayoutItem().width(
937 ltr == flowIsLTR ? m_inlineTextBox.start() 1029 ltr == flowIsLTR ? m_inlineTextBox.start()
938 : m_inlineTextBox.truncation(), 1030 : m_inlineTextBox.truncation(),
939 ltr == flowIsLTR ? m_inlineTextBox.truncation() 1031 ltr == flowIsLTR ? m_inlineTextBox.truncation()
940 : m_inlineTextBox.len() - m_inlineTextBox.truncation(), 1032 : m_inlineTextBox.len() - m_inlineTextBox.truncation(),
941 m_inlineTextBox.textPos(), flowIsLTR ? LTR : RTL, 1033 m_inlineTextBox.textPos(), flowIsLTR ? LTR : RTL,
942 m_inlineTextBox.isFirstLineStyle())); 1034 m_inlineTextBox.isFirstLineStyle()));
943 if (!flowIsLTR) 1035 if (!flowIsLTR)
944 localOrigin.move(m_inlineTextBox.logicalWidth() - width, LayoutUnit()); 1036 localOrigin.move(m_inlineTextBox.logicalWidth() - width, LayoutUnit());
945 } 1037 }
946 1038
947 // Use a special function for underlines to get the positioning exactly right. 1039 LayoutObject& textBoxLayoutObject = inlineLayoutObject();
948 bool isPrinting = paintInfo.isPrinting();
949 1040
950 LayoutObject& textBoxLayoutObject = inlineLayoutObject();
951 const ComputedStyle& styleToUse = 1041 const ComputedStyle& styleToUse =
952 textBoxLayoutObject.styleRef(m_inlineTextBox.isFirstLineStyle()); 1042 textBoxLayoutObject.styleRef(m_inlineTextBox.isFirstLineStyle());
953 const SimpleFontData* fontData = styleToUse.font().primaryFont(); 1043 const SimpleFontData* fontData = styleToUse.font().primaryFont();
954 DCHECK(fontData); 1044 DCHECK(fontData);
955 float baseline = fontData ? fontData->getFontMetrics().ascent() : 0; 1045 float baseline = fontData ? fontData->getFontMetrics().floatAscent() : 0;
956 1046
957 // Set the thick of the line to be 10% (or something else ?)of the computed 1047 // Set the thick of the line to be 10% (or something else ?)of the computed
958 // font size and not less than 1px. Using computedFontSize should take care 1048 // font size and not less than 1px. Using computedFontSize should take care
959 // of zoom as well. 1049 // of zoom as well.
960 1050
961 // Update Underline thickness, in case we have Faulty Font Metrics calculating 1051 // Update Underline thickness, in case we have Faulty Font Metrics calculating
962 // underline thickness by old method. 1052 // underline thickness by old method.
963 float textDecorationThickness = 0.0; 1053 float textDecorationThickness = 0.0;
964 int fontHeightInt = 0; 1054 int fontHeightInt = 0;
965 if (fontData) { 1055 if (fontData) {
966 textDecorationThickness = fontData->getFontMetrics().underlineThickness(); 1056 textDecorationThickness = fontData->getFontMetrics().underlineThickness();
967 fontHeightInt = (int)(fontData->getFontMetrics().floatHeight() + 0.5); 1057 fontHeightInt = (int)(fontData->getFontMetrics().floatHeight() + 0.5);
968 } 1058 }
969 if ((textDecorationThickness == 0.f) || 1059 if ((textDecorationThickness == 0.f) ||
970 (textDecorationThickness >= (fontHeightInt >> 1))) 1060 (textDecorationThickness >= (fontHeightInt >> 1)))
971 textDecorationThickness = 1061 textDecorationThickness =
972 std::max(1.f, styleToUse.computedFontSize() / 10.f); 1062 std::max(1.f, styleToUse.computedFontSize() / 10.f);
973 1063
974 context.setStrokeThickness(textDecorationThickness); 1064 context.setStrokeThickness(textDecorationThickness);
975 1065
976 bool antialiasDecoration = shouldSetDecorationAntialias(decorations); 1066 bool antialiasDecoration = shouldSetDecorationAntialias(decorations);
977 1067
978 // Offset between lines - always non-zero, so lines never cross each other. 1068 // Offset between lines - always non-zero, so lines never cross each other.
979 float doubleOffset = textDecorationThickness + 1.f; 1069 float doubleOffset = textDecorationThickness + 1.f;
1070 bool skipIntercepts =
1071 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk;
980 1072
981 for (const AppliedTextDecoration& decoration : decorations) { 1073 for (const AppliedTextDecoration& decoration : decorations) {
982 TextDecoration lines = decoration.lines(); 1074 TextDecoration lines = decoration.lines();
983 if (lines & TextDecorationUnderline && fontData) { 1075 if ((lines & TextDecorationUnderline) && fontData) {
984 const int underlineOffset = computeUnderlineOffset( 1076 const int underlineOffset = computeUnderlineOffset(
985 styleToUse.getTextUnderlinePosition(), fontData->getFontMetrics(), 1077 styleToUse.getTextUnderlinePosition(), fontData->getFontMetrics(),
986 &m_inlineTextBox, textDecorationThickness); 1078 &m_inlineTextBox, textDecorationThickness);
987 paintAppliedDecoration( 1079 AppliedDecorationPainter decorationPainter(
988 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), 1080 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset),
989 width.toFloat(), doubleOffset, 1, decoration, textDecorationThickness, 1081 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 1,
990 antialiasDecoration, isPrinting); 1082 antialiasDecoration);
1083 if (skipIntercepts) {
1084 textPainter.clipDecorationsStripe(
1085 -baseline + decorationPainter.decorationBounds().y() -
1086 FloatPoint(localOrigin).y(),
1087 decorationPainter.decorationBounds().height(),
1088 textDecorationThickness);
1089 }
1090 decorationPainter.paint();
991 } 1091 }
992 if (lines & TextDecorationOverline) { 1092 if (lines & TextDecorationOverline) {
993 paintAppliedDecoration( 1093 AppliedDecorationPainter decorationPainter(
994 context, FloatPoint(localOrigin), width.toFloat(), -doubleOffset, 1, 1094 context, FloatPoint(localOrigin), width.toFloat(), decoration,
995 decoration, textDecorationThickness, antialiasDecoration, isPrinting); 1095 textDecorationThickness, -doubleOffset, 1, antialiasDecoration);
1096 if (skipIntercepts) {
1097 textPainter.clipDecorationsStripe(
1098 -baseline + decorationPainter.decorationBounds().y() -
1099 FloatPoint(localOrigin).y(),
1100 decorationPainter.decorationBounds().height(),
1101 textDecorationThickness);
1102 }
1103 decorationPainter.paint();
996 } 1104 }
997 if (lines & TextDecorationLineThrough) { 1105 if (lines & TextDecorationLineThrough) {
998 const float lineThroughOffset = 2 * baseline / 3; 1106 const float lineThroughOffset = 2 * baseline / 3;
999 paintAppliedDecoration( 1107 AppliedDecorationPainter decorationPainter(
1000 context, FloatPoint(localOrigin) + FloatPoint(0, lineThroughOffset), 1108 context, FloatPoint(localOrigin) + FloatPoint(0, lineThroughOffset),
1001 width.toFloat(), doubleOffset, 0, decoration, textDecorationThickness, 1109 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 0,
1002 antialiasDecoration, isPrinting); 1110 antialiasDecoration);
1111 if (skipIntercepts) {
1112 textPainter.clipDecorationsStripe(
1113 -baseline + decorationPainter.decorationBounds().y() -
1114 FloatPoint(localOrigin).y(),
1115 decorationPainter.decorationBounds().height(),
1116 textDecorationThickness);
1117 }
1118 decorationPainter.paint();
1003 } 1119 }
1004 } 1120 }
1005 } 1121 }
1006 1122
1007 void InlineTextBoxPainter::paintCompositionUnderline( 1123 void InlineTextBoxPainter::paintCompositionUnderline(
1008 GraphicsContext& context, 1124 GraphicsContext& context,
1009 const LayoutPoint& boxOrigin, 1125 const LayoutPoint& boxOrigin,
1010 const CompositionUnderline& underline) { 1126 const CompositionUnderline& underline) {
1011 if (underline.color() == Color::transparent) 1127 if (underline.color() == Color::transparent)
1012 return; 1128 return;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1074 start += 1; 1190 start += 1;
1075 width -= 2; 1191 width -= 2;
1076 1192
1077 context.setStrokeColor(underline.color()); 1193 context.setStrokeColor(underline.color());
1078 context.setStrokeThickness(lineThickness); 1194 context.setStrokeThickness(lineThickness);
1079 context.drawLineForText( 1195 context.drawLineForText(
1080 FloatPoint( 1196 FloatPoint(
1081 boxOrigin.x() + start, 1197 boxOrigin.x() + start,
1082 (boxOrigin.y() + m_inlineTextBox.logicalHeight() - lineThickness) 1198 (boxOrigin.y() + m_inlineTextBox.logicalHeight() - lineThickness)
1083 .toFloat()), 1199 .toFloat()),
1084 width, m_inlineTextBox.getLineLayoutItem().document().printing()); 1200 width);
1085 } 1201 }
1086 1202
1087 void InlineTextBoxPainter::paintTextMatchMarkerForeground( 1203 void InlineTextBoxPainter::paintTextMatchMarkerForeground(
1088 const PaintInfo& paintInfo, 1204 const PaintInfo& paintInfo,
1089 const LayoutPoint& boxOrigin, 1205 const LayoutPoint& boxOrigin,
1090 DocumentMarker* marker, 1206 DocumentMarker* marker,
1091 const ComputedStyle& style, 1207 const ComputedStyle& style,
1092 const Font& font) { 1208 const Font& font) {
1093 if (!inlineLayoutObject().frame()->editor().markedTextMatchesAreHighlighted()) 1209 if (!inlineLayoutObject().frame()->editor().markedTextMatchesAreHighlighted())
1094 return; 1210 return;
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
1151 GraphicsContextStateSaver stateSaver(context); 1267 GraphicsContextStateSaver stateSaver(context);
1152 1268
1153 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), 1269 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(),
1154 m_inlineTextBox.logicalHeight())); 1270 m_inlineTextBox.logicalHeight()));
1155 context.clip(FloatRect(boxRect)); 1271 context.clip(FloatRect(boxRect));
1156 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), 1272 context.drawHighlightForText(font, run, FloatPoint(boxOrigin),
1157 boxRect.height().toInt(), color, sPos, ePos); 1273 boxRect.height().toInt(), color, sPos, ePos);
1158 } 1274 }
1159 1275
1160 } // namespace blink 1276 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/paint/InlineTextBoxPainter.h ('k') | third_party/WebKit/Source/core/paint/TextPainter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698