Flutter Engine
The Flutter Engine
Decorations.cpp
Go to the documentation of this file.
1// Copyright 2020 Google LLC.
4
5using namespace skia_private;
6
7namespace skia {
8namespace textlayout {
9
10namespace {
11void draw_line_as_rect(ParagraphPainter* painter, SkScalar x, SkScalar y, SkScalar width,
12 const ParagraphPainter::DecorationStyle& decorStyle) {
13 SkASSERT(decorStyle.skPaint().getPathEffect() == nullptr);
14 SkASSERT(decorStyle.skPaint().getStrokeCap() == SkPaint::kButt_Cap);
15 SkASSERT(decorStyle.skPaint().getStrokeWidth() > 0); // this trick won't work for hairlines
16
17 float radius = decorStyle.getStrokeWidth() * 0.5f;
18 painter->drawFilledRect({x, y - radius, x + width, y + radius}, decorStyle);
19}
20
21const float kDoubleDecorationSpacing = 3.0f;
22} // namespace
23
24void Decorations::paint(ParagraphPainter* painter, const TextStyle& textStyle, const TextLine::ClipContext& context, SkScalar baseline) {
26 return;
27 }
28
29 // Get thickness and position
30 calculateThickness(textStyle, context.run->font().refTypeface());
31
32 for (auto decoration : AllTextDecorations) {
33 if ((textStyle.getDecorationType() & decoration) == 0) {
34 continue;
35 }
36
37 calculatePosition(decoration,
38 decoration == TextDecoration::kOverline
39 ? context.run->correctAscent() - context.run->ascent()
40 : context.run->correctAscent());
41
42 calculatePaint(textStyle);
43
44 auto width = context.clip.width();
45 SkScalar x = context.clip.left();
46 SkScalar y = context.clip.top() + fPosition;
47
48 bool drawGaps = textStyle.getDecorationMode() == TextDecorationMode::kGaps &&
50
51 switch (textStyle.getDecorationStyle()) {
53 calculateWaves(textStyle, context.clip);
54 fPath.offset(x, y);
55 painter->drawPath(fPath, fDecorStyle);
56 break;
57 }
59 SkScalar bottom = y + kDoubleDecorationSpacing;
60 if (drawGaps) {
61 SkScalar left = x - context.fTextShift;
62 painter->translate(context.fTextShift, 0);
63 calculateGaps(context, SkRect::MakeXYWH(left, y, width, fThickness), baseline, fThickness);
64 painter->drawPath(fPath, fDecorStyle);
65 calculateGaps(context, SkRect::MakeXYWH(left, bottom, width, fThickness), baseline, fThickness);
66 painter->drawPath(fPath, fDecorStyle);
67 } else {
68 draw_line_as_rect(painter, x, y, width, fDecorStyle);
69 draw_line_as_rect(painter, x, bottom, width, fDecorStyle);
70 }
71 break;
72 }
75 if (drawGaps) {
76 SkScalar left = x - context.fTextShift;
77 painter->translate(context.fTextShift, 0);
78 calculateGaps(context, SkRect::MakeXYWH(left, y, width, fThickness), baseline, 0);
79 painter->drawPath(fPath, fDecorStyle);
80 } else {
81 painter->drawLine(x, y, x + width, y, fDecorStyle);
82 }
83 break;
85 if (drawGaps) {
86 SkScalar left = x - context.fTextShift;
87 painter->translate(context.fTextShift, 0);
88 calculateGaps(context, SkRect::MakeXYWH(left, y, width, fThickness), baseline, fThickness);
89 painter->drawPath(fPath, fDecorStyle);
90 } else {
91 draw_line_as_rect(painter, x, y, width, fDecorStyle);
92 }
93 break;
94 default:break;
95 }
96 }
97}
98
99void Decorations::calculateGaps(const TextLine::ClipContext& context, const SkRect& rect,
100 SkScalar baseline, SkScalar halo) {
101 // Create a special text blob for decorations
103 context.run->copyTo(builder,
104 SkToU32(context.pos),
105 context.size);
106 sk_sp<SkTextBlob> blob = builder.make();
107 if (!blob) {
108 // There is no text really
109 return;
110 }
111 // Since we do not shift down the text by {baseline}
112 // (it now happens on drawTextBlob but we do not draw text here)
113 // we have to shift up the bounds to compensate
114 // This baseline thing ends with getIntercepts
115 const SkScalar bounds[2] = {rect.fTop - baseline, rect.fBottom - baseline};
116 const SkPaint& decorPaint = fDecorStyle.skPaint();
117 auto count = blob->getIntercepts(bounds, nullptr, &decorPaint);
118 TArray<SkScalar> intersections(count);
119 intersections.resize(count);
120 blob->getIntercepts(bounds, intersections.data(), &decorPaint);
121
123 auto start = rect.fLeft;
124 path.moveTo(rect.fLeft, rect.fTop);
125 for (int i = 0; i < intersections.size(); i += 2) {
126 auto end = intersections[i] - halo;
127 if (end - start >= halo) {
128 start = intersections[i + 1] + halo;
129 path.lineTo(end, rect.fTop).moveTo(start, rect.fTop);
130 }
131 }
132 if (!intersections.empty() && (rect.fRight - start > halo)) {
133 path.lineTo(rect.fRight, rect.fTop);
134 }
135 fPath = path.detach();
136}
137
138// This is how flutter calculates the thickness
139void Decorations::calculateThickness(TextStyle textStyle, sk_sp<SkTypeface> typeface) {
140
141 textStyle.setTypeface(std::move(typeface));
142 textStyle.getFontMetrics(&fFontMetrics);
143
144 fThickness = textStyle.getFontSize() / 14.0f;
145
146 if ((fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kUnderlineThicknessIsValid_Flag) &&
147 fFontMetrics.fUnderlineThickness > 0) {
148 fThickness = fFontMetrics.fUnderlineThickness;
149 }
150
151 if (textStyle.getDecorationType() == TextDecoration::kLineThrough) {
152 if ((fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag) &&
153 fFontMetrics.fStrikeoutThickness > 0) {
154 fThickness = fFontMetrics.fStrikeoutThickness;
155 }
156 }
157 fThickness *= textStyle.getDecorationThicknessMultiplier();
158}
159
160// This is how flutter calculates the positioning
161void Decorations::calculatePosition(TextDecoration decoration, SkScalar ascent) {
162 switch (decoration) {
164 if ((fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kUnderlinePositionIsValid_Flag) &&
165 fFontMetrics.fUnderlinePosition > 0) {
166 fPosition = fFontMetrics.fUnderlinePosition;
167 } else {
168 fPosition = fThickness;
169 }
170 fPosition -= ascent;
171 break;
173 fPosition = - ascent;
174 break;
176 fPosition = (fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kStrikeoutPositionIsValid_Flag)
177 ? fFontMetrics.fStrikeoutPosition
178 : fFontMetrics.fXHeight / -2;
179 fPosition -= ascent;
180 break;
181 }
182 default:SkASSERT(false);
183 break;
184 }
185}
186
187void Decorations::calculatePaint(const TextStyle& textStyle) {
188 std::optional<ParagraphPainter::DashPathEffect> dashPathEffect;
189 SkScalar scaleFactor = textStyle.getFontSize() / 14.f;
190 switch (textStyle.getDecorationStyle()) {
191 // Note: the intervals are scaled by the thickness of the line, so it is
192 // possible to change spacing by changing the decoration_thickness
193 // property of TextStyle.
195 dashPathEffect.emplace(1.0f * scaleFactor, 1.5f * scaleFactor);
196 break;
197 }
198 // Note: the intervals are scaled by the thickness of the line, so it is
199 // possible to change spacing by changing the decoration_thickness
200 // property of TextStyle.
202 dashPathEffect.emplace(4.0f * scaleFactor, 2.0f * scaleFactor);
203 break;
204 }
205 default: break;
206 }
207
208 SkColor color = (textStyle.getDecorationColor() == SK_ColorTRANSPARENT)
209 ? textStyle.getColor()
210 : textStyle.getDecorationColor();
211
212 fDecorStyle = ParagraphPainter::DecorationStyle(color, fThickness, dashPathEffect);
213}
214
215void Decorations::calculateWaves(const TextStyle& textStyle, SkRect clip) {
216
217 fPath.reset();
218 int wave_count = 0;
219 SkScalar x_start = 0;
220 SkScalar quarterWave = fThickness;
221 fPath.moveTo(0, 0);
222 while (x_start + quarterWave * 2 < clip.width()) {
223 fPath.rQuadTo(quarterWave,
224 wave_count % 2 != 0 ? quarterWave : -quarterWave,
225 quarterWave * 2,
226 0);
227 x_start += quarterWave * 2;
228 ++wave_count;
229 }
230
231 // The rest of the wave
232 auto remaining = clip.width() - x_start;
233 if (remaining > 0) {
234 double x1 = remaining / 2;
235 double y1 = remaining / 2 * (wave_count % 2 == 0 ? -1 : 1);
236 double x2 = remaining;
237 double y2 = (remaining - remaining * remaining / (quarterWave * 2)) *
238 (wave_count % 2 == 0 ? -1 : 1);
239 fPath.rQuadTo(x1, y1, x2, y2);
240 }
241}
242
243} // namespace textlayout
244} // namespace skia
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
uint32_t SkColor
Definition: SkColor.h:37
constexpr SkColor SK_ColorTRANSPARENT
Definition: SkColor.h:99
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
constexpr uint32_t SkToU32(S x)
Definition: SkTo.h:26
sk_sp< SkTypeface > refTypeface() const
Definition: SkFont.h:237
@ kButt_Cap
no stroke extension
Definition: SkPaint.h:334
void offset(SkScalar dx, SkScalar dy, SkPath *dst) const
Definition: SkPath.cpp:1691
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
SkPath & reset()
Definition: SkPath.cpp:370
SkPath & rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2)
Definition: SkPath.cpp:759
int getIntercepts(const SkScalar bounds[2], SkScalar intervals[], const SkPaint *paint=nullptr) const
Definition: SkTextBlob.cpp:927
void paint(ParagraphPainter *painter, const TextStyle &textStyle, const TextLine::ClipContext &context, SkScalar baseline)
Definition: Decorations.cpp:24
virtual void translate(SkScalar dx, SkScalar dy)=0
virtual void drawPath(const SkPath &path, const DecorationStyle &decorStyle)=0
virtual void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const DecorationStyle &decorStyle)=0
void copyTo(SkTextBlobBuilder &builder, size_t pos, size_t size) const
Definition: Run.cpp:88
SkScalar ascent() const
Definition: Run.h:89
const SkFont & font() const
Definition: Run.h:95
SkScalar correctAscent() const
Definition: Run.h:92
TextDecorationMode getDecorationMode() const
Definition: TextStyle.h:214
TextDecorationStyle getDecorationStyle() const
Definition: TextStyle.h:216
TextDecoration getDecorationType() const
Definition: TextStyle.h:213
DlColor color
float SkScalar
Definition: extension.cpp:12
glong glong end
double y
double x
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
constexpr TextDecoration AllTextDecorations[]
Definition: TextStyle.h:47
Definition: DartTypes.h:13
int32_t width
SkScalar fStrikeoutPosition
distance from baseline to bottom of stroke, typically negative
Definition: SkFontMetrics.h:67
SkScalar fStrikeoutThickness
strikeout thickness
Definition: SkFontMetrics.h:66
uint32_t fFlags
FontMetricsFlags indicating which metrics are valid.
Definition: SkFontMetrics.h:52
SkScalar fXHeight
height of lower-case 'x', zero if unknown, typically negative
Definition: SkFontMetrics.h:62
SkScalar fUnderlineThickness
underline thickness
Definition: SkFontMetrics.h:64
SkScalar fUnderlinePosition
distance from baseline to top of stroke, typically positive
Definition: SkFontMetrics.h:65
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
constexpr float width() const
Definition: SkRect.h:762