Flutter Engine
The Flutter Engine
Run.cpp
Go to the documentation of this file.
1// Copyright 2019 Google LLC.
12#include "src/base/SkUTF.h"
13
14namespace skia {
15namespace textlayout {
16
19 size_t firstChar,
20 SkScalar heightMultiplier,
21 bool useHalfLeading,
22 SkScalar baselineShift,
23 size_t index,
25 : fOwner(owner)
26 , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
27 , fClusterRange(EMPTY_CLUSTERS)
28 , fFont(info.fFont)
29 , fClusterStart(firstChar)
30 , fGlyphData(std::make_shared<GlyphData>())
31 , fGlyphs(fGlyphData->glyphs)
32 , fPositions(fGlyphData->positions)
33 , fOffsets(fGlyphData->offsets)
34 , fClusterIndexes(fGlyphData->clusterIndexes)
35 , fHeightMultiplier(heightMultiplier)
36 , fUseHalfLeading(useHalfLeading)
37 , fBaselineShift(baselineShift)
38{
39 fBidiLevel = info.fBidiLevel;
40 fAdvance = info.fAdvance;
41 fIndex = index;
42 fUtf8Range = info.utf8Range;
43 fOffset = SkVector::Make(offsetX, 0);
44
45 fGlyphs.push_back_n(info.glyphCount);
46 fPositions.push_back_n(info.glyphCount + 1);
47 fOffsets.push_back_n(info.glyphCount + 1);
48 fClusterIndexes.push_back_n(info.glyphCount + 1);
49 info.fFont.getMetrics(&fFontMetrics);
50
51 this->calculateMetrics();
52
53 // To make edge cases easier:
54 fPositions[info.glyphCount] = fOffset + fAdvance;
55 fOffsets[info.glyphCount] = {0, 0};
56 fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
57 fEllipsis = false;
58 fPlaceholderIndex = std::numeric_limits<size_t>::max();
59}
60
62 fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5;
63 fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5;
64 fCorrectLeading = 0;
65 if (SkScalarNearlyZero(fHeightMultiplier)) {
66 return;
67 }
68 const auto runHeight = fHeightMultiplier * fFont.getSize();
69 const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent;
70 if (fUseHalfLeading) {
71 const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2;
72 fCorrectAscent -= extraLeading;
73 fCorrectDescent += extraLeading;
74 } else {
75 const auto multiplier = runHeight / fontIntrinsicHeight;
76 fCorrectAscent *= multiplier;
77 fCorrectDescent *= multiplier;
78 }
79 // If we shift the baseline we need to make sure the shifted text fits the line
80 fCorrectAscent += fBaselineShift;
81 fCorrectDescent += fBaselineShift;
82}
83
85 return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
86}
87
88void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const {
89 SkASSERT(pos + size <= this->size());
90 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
91 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
92
93 for (size_t i = 0; i < size; ++i) {
94 auto point = fPositions[i + pos];
95 if (!fJustificationShifts.empty()) {
96 point.fX += fJustificationShifts[i + pos].fX;
97 }
98 point += fOffsets[i + pos];
99 blobBuffer.points()[i] = point;
100 }
101}
102
103// Find a cluster range from text range (within one run)
104// Cluster range is normalized ([start:end) start < end regardless of TextDirection
105// Boolean value in triple indicates whether the cluster range was found or not
106std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
107 if (text.width() == 0) {
108 // Special Flutter case for "\n" and "...\n"
109 if (text.end > this->fTextRange.start) {
110 ClusterIndex index = fOwner->clusterIndex(text.end - 1);
111 return std::make_tuple(true, index, index);
112 } else {
113 return std::make_tuple(false, 0, 0);
114 }
115 }
116
118 bool found = true;
119 // Deal with the case when either start or end are not align with glyph cluster edge
120 // In such case we shift the text range to the right
121 // (cutting from the left and adding to the right)
122 if (leftToRight()) {
123 // LTR: [start:end)
124 found = clusterRange.start != fClusterRange.end;
125 clusterRange.start = fOwner->clusterIndex(text.start);
126 clusterRange.end = fOwner->clusterIndex(text.end - 1);
127 } else {
128 // RTL: (start:end]
129 clusterRange.start = fOwner->clusterIndex(text.end);
130 clusterRange.end = fOwner->clusterIndex(text.start + 1);
131 found = clusterRange.end != fClusterRange.start;
132 }
133
134 return std::make_tuple(
135 found,
138}
139
140std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGlyphClusters(TextRange text) const {
143 return std::make_tuple(true, start, end);
144}
145
146// Adjust the text to grapheme edges so the first grapheme start is in the text and the last grapheme start is in the text
147// It actually means that the first grapheme is entirely in the text and the last grapheme does not have to be
148// 12345 234 2:2 -> 2,5 4:4
149std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const {
152 return std::make_tuple(true, start, end);
153}
154
156
157 for (size_t index = 0; index < fClusterRange.width(); ++index) {
158 auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1;
159 auto cluster = &fOwner->cluster(correctIndex);
160 visitor(cluster);
161 }
162}
163
165 // Increment the run width
166 fAdvance.fX += space;
167 // Increment the cluster width
168 cluster->space(space);
169}
170
172 SkScalar shift = 0;
173 for (size_t i = 0; i < this->size(); ++i) {
174 fPositions[i].fX += shift;
175 shift += space;
176 }
177 fPositions[this->size()].fX += shift;
178 fAdvance.fX += shift;
179 return shift;
180}
181
183 // Offset all the glyphs in the cluster
184 SkScalar shift = 0;
185 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
186 fPositions[i].fX += shift;
187 shift += space;
188 }
189 if (this->size() == cluster->endPos()) {
190 // To make calculations easier
191 fPositions[cluster->endPos()].fX += shift;
192 }
193 // Increment the run width
194 fAdvance.fX += shift;
195 // Increment the cluster width
196 cluster->space(shift);
197 cluster->setHalfLetterSpacing(space / 2);
198
199 return shift;
200}
201
202void Run::shift(const Cluster* cluster, SkScalar offset) {
203 if (offset == 0) {
204 return;
205 }
206 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
207 fPositions[i].fX += offset;
208 }
209 if (this->size() == cluster->endPos()) {
210 // To make calculations easier
211 fPositions[cluster->endPos()].fX += offset;
212 }
213}
214
215void Run::extend(const Cluster* cluster, SkScalar offset) {
216 // Extend the cluster at the end
217 fPositions[cluster->endPos()].fX += offset;
218}
219
221
223 auto placeholderStyle = this->placeholderStyle();
224 // Difference between the placeholder baseline and the line bottom
225 SkScalar baselineAdjustment = 0;
226 switch (placeholderStyle->fBaseline) {
228 break;
229
231 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
232 break;
233 }
234
237
238 fFontMetrics.fLeading = 0;
239 switch (placeholderStyle->fAlignment) {
241 fFontMetrics.fAscent = baselineAdjustment - offset;
242 fFontMetrics.fDescent = baselineAdjustment + height - offset;
243 break;
244
246 fFontMetrics.fAscent = baselineAdjustment - height;
247 fFontMetrics.fDescent = baselineAdjustment;
248 break;
249
251 fFontMetrics.fAscent = baselineAdjustment;
252 fFontMetrics.fDescent = baselineAdjustment + height;
253 break;
254
256 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
257 break;
258
260 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
261 break;
262
264 auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
265 fFontMetrics.fDescent = height/2.0 - mid;
266 fFontMetrics.fAscent = - height/2.0 - mid;
267 break;
268 }
269
270 this->calculateMetrics();
271
272 // Make sure the placeholder can fit the line
273 endlineMetrics->add(this);
274}
275
277 if (ch < fTextRange.start || ch >= fTextRange.end) {
278 return 0;
279 }
280 auto shift = ch - fTextRange.start;
281 auto ratio = shift * 1.0 / fTextRange.width();
282
283 return SkDoubleToScalar(fWidth * ratio);
284}
285
287 if (ch < fTextRange.start || ch >= fTextRange.end) {
288 return 0;
289 }
290 auto shift = fTextRange.end - ch - 1;
291 auto ratio = shift * 1.0 / fTextRange.width();
292
293 return SkDoubleToScalar(fWidth * ratio);
294}
295
297 auto ratio = (s * 1.0) / fWidth;
298 return sk_double_floor2int(ratio * size());
299}
300
302 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
303 // We don't have to take in account cluster shift since it's the same for 0 and for pos
304 auto& run = fOwner->run(fRunIndex);
305 return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
306}
307
309 return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
310}
311
313 if (isPlaceholder()) {
314 return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
315 } else {
316 return nullptr;
317 }
318}
319
320bool Run::isResolved() const {
321 for (auto& glyph :fGlyphs) {
322 if (glyph == 0) {
323 return false;
324 }
325 }
326 return true;
327}
328
330 if (fRunIndex >= fOwner->runs().size()) {
331 return nullptr;
332 }
333 return &fOwner->run(fRunIndex);
334}
335
337 SkASSERT(fRunIndex < fOwner->runs().size());
338 return fOwner->run(fRunIndex);
339}
340
342 SkASSERT(fRunIndex < fOwner->runs().size());
343 return fOwner->run(fRunIndex).font();
344}
345
347 return fOwner->codeUnitHasProperty(fTextRange.end,
348 SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
349}
350
352 return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
353}
354} // namespace textlayout
355} // namespace skia
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define sk_double_floor2int(x)
static void * sk_careful_memcpy(void *dst, const void *src, size_t len)
Definition: SkMalloc.h:125
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SkDoubleToScalar(x)
Definition: SkScalar.h:64
constexpr int SkToInt(S x)
Definition: SkTo.h:29
uint16_t SkGlyphID
Definition: SkTypes.h:179
Definition: SkFont.h:35
SkScalar getSize() const
Definition: SkFont.h:217
SkFont font() const
Definition: Run.cpp:341
size_t startPos() const
Definition: Run.h:316
SkScalar trimmedWidth(size_t pos) const
Definition: Run.cpp:301
size_t roundPos(SkScalar s) const
Definition: Run.cpp:296
SkScalar sizeFromChar(TextIndex ch) const
Definition: Run.cpp:286
bool isSoftBreak() const
Definition: Run.cpp:346
bool isGraphemeBreak() const
Definition: Run.cpp:351
SkScalar sizeToChar(TextIndex ch) const
Definition: Run.cpp:276
Run * runOrNull() const
Definition: Run.cpp:329
size_t endPos() const
Definition: Run.h:317
void space(SkScalar shift)
Definition: Run.h:301
size_t size() const
Definition: Run.h:320
void setHalfLetterSpacing(SkScalar halfLetterSpacing)
Definition: Run.h:322
Run & run() const
Definition: Run.cpp:336
SkScalar deltaBaselines() const
Definition: Run.h:493
Run & run(RunIndex runIndex)
ClusterIndex clusterIndex(TextIndex textIndex)
bool codeUnitHasProperty(size_t index, SkUnicode::CodeUnitFlags property) const
TextIndex findNextGlyphClusterBoundary(TextIndex utf8) const
Cluster & cluster(ClusterIndex clusterIndex)
SkSpan< Placeholder > placeholders()
TextIndex findPreviousGlyphClusterBoundary(TextIndex utf8) const
TextIndex findPreviousGraphemeBoundary(TextIndex utf8) const
TextIndex findNextGraphemeBoundary(TextIndex utf8) const
void calculateMetrics()
Definition: Run.cpp:61
size_t index() const
Definition: Run.h:98
void updateMetrics(InternalLineMetrics *endlineMetrics)
Definition: Run.cpp:220
void copyTo(SkTextBlobBuilder &builder, size_t pos, size_t size) const
Definition: Run.cpp:88
ClusterRange clusterRange() const
Definition: Run.h:109
bool isPlaceholder() const
Definition: Run.h:103
SkShaper::RunHandler::Buffer newRunBuffer()
Definition: Run.cpp:84
std::tuple< bool, TextIndex, TextIndex > findLimitingGlyphClusters(TextRange text) const
Definition: Run.cpp:140
SkScalar positionX(size_t pos) const
Definition: Run.cpp:308
void shift(SkScalar shiftX, SkScalar shiftY)
Definition: Run.h:81
std::tuple< bool, TextIndex, TextIndex > findLimitingGraphemes(TextRange text) const
Definition: Run.cpp:149
std::tuple< bool, ClusterIndex, ClusterIndex > findLimitingClusters(TextRange text) const
Definition: Run.cpp:106
const SkFont & font() const
Definition: Run.h:95
bool isResolved() const
Definition: Run.cpp:320
SkVector offset() const
Definition: Run.h:88
std::function< void(Cluster *cluster)> ClusterVisitor
Definition: Run.h:143
Run(ParagraphImpl *owner, const SkShaper::RunHandler::RunInfo &info, size_t firstChar, SkScalar heightMultiplier, bool useHalfLeading, SkScalar baselineShift, size_t index, SkScalar shiftX)
Definition: Run.cpp:17
void addSpacesAtTheEnd(SkScalar space, Cluster *cluster)
Definition: Run.cpp:164
void extend(const Cluster *cluster, SkScalar offset)
Definition: Run.cpp:215
void iterateThroughClusters(const ClusterVisitor &visitor)
Definition: Run.cpp:155
PlaceholderStyle * placeholderStyle() const
Definition: Run.cpp:312
SkScalar addSpacesEvenly(SkScalar space, Cluster *cluster)
Definition: Run.cpp:182
size_t size() const
Definition: Run.h:78
bool leftToRight() const
Definition: Run.h:96
SkScalar posX(size_t index) const
Definition: Run.h:75
T * push_back_n(int n)
Definition: SkTArray.h:267
bool empty() const
Definition: SkTArray.h:199
static const char * begin(const StringSlice &s)
Definition: editor.cpp:252
float SkScalar
Definition: extension.cpp:12
struct MyStruct s
glong glong end
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
std::u16string text
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
list offsets
Definition: mskp_parser.py:37
Definition: run.py:1
const SkRange< size_t > EMPTY_CLUSTERS
Definition: Run.h:38
@ kBaseline
Match the baseline of the placeholder with the baseline.
size_t TextIndex
Definition: TextStyle.h:336
size_t ClusterIndex
Definition: Run.h:35
Definition: DartTypes.h:13
Definition: ref_ptr.h:256
SkScalar offsetX
int32_t height
SeparatedVector2 offset
SkScalar fLeading
distance to add between lines, typically positive or zero
Definition: SkFontMetrics.h:57
SkScalar fAscent
distance to reserve above baseline, typically negative
Definition: SkFontMetrics.h:54
SkScalar fDescent
distance to reserve below baseline, typically positive
Definition: SkFontMetrics.h:55
float fX
x-axis value
Definition: SkPoint_impl.h:164
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
constexpr size_t end() const
Definition: SkShaper.h:204
PlaceholderAlignment fAlignment
Definition: TextStyle.h:138