Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
102 SkTextBlobBuilder builder;
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
122 SkPathBuilder path;
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
147 fFontMetrics.fUnderlineThickness > 0) {
148 fThickness = fFontMetrics.fUnderlineThickness;
149 }
150
151 if (textStyle.getDecorationType() == TextDecoration::kLineThrough) {
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) {
165 fFontMetrics.fUnderlinePosition > 0) {
166 fPosition = fFontMetrics.fUnderlinePosition;
167 } else {
168 fPosition = fThickness;
169 }
170 fPosition -= ascent;
171 break;
173 fPosition = - ascent;
174 break;
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
SkColor4f color
#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:3824
static bool left(const SkPoint &p0, const SkPoint &p1)
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:1627
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & reset()
Definition SkPath.cpp:360
SkPath & rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2)
Definition SkPath.cpp:749
void paint(ParagraphPainter *painter, const TextStyle &textStyle, const TextLine::ClipContext &context, SkScalar baseline)
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
float SkScalar
Definition extension.cpp:12
glong glong end
double y
double x
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
int32_t width
SkScalar fStrikeoutPosition
distance from baseline to bottom of stroke, typically negative
SkScalar fStrikeoutThickness
strikeout thickness
uint32_t fFlags
FontMetricsFlags indicating which metrics are valid.
SkScalar fUnderlineThickness
underline thickness
@ kStrikeoutPositionIsValid_Flag
set if fStrikeoutPosition is valid
@ kStrikeoutThicknessIsValid_Flag
set if fStrikeoutThickness is valid
@ kUnderlinePositionIsValid_Flag
set if fUnderlinePosition is valid
@ kUnderlineThicknessIsValid_Flag
set if fUnderlineThickness is valid
SkScalar fUnderlinePosition
distance from baseline to top of stroke, typically positive
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