1 // Copyright 2019 Google LLC.
2 #include "include/core/SkFontMetrics.h"
3 #include "include/core/SkTextBlob.h"
4 #include "include/private/base/SkFloatingPoint.h"
5 #include "include/private/base/SkMalloc.h"
6 #include "include/private/base/SkTo.h"
7 #include "modules/skparagraph/include/DartTypes.h"
8 #include "modules/skparagraph/include/TextStyle.h"
9 #include "modules/skparagraph/src/ParagraphImpl.h"
10 #include "modules/skparagraph/src/Run.h"
11 #include "modules/skshaper/include/SkShaper.h"
12 #include "src/base/SkUTF.h"
13
14 namespace skia {
15 namespace textlayout {
16
Run(ParagraphImpl * owner,const SkShaper::RunHandler::RunInfo & info,size_t firstChar,SkScalar heightMultiplier,bool useHalfLeading,SkScalar baselineShift,size_t index,SkScalar offsetX)17 Run::Run(ParagraphImpl* owner,
18 const SkShaper::RunHandler::RunInfo& info,
19 size_t firstChar,
20 SkScalar heightMultiplier,
21 bool useHalfLeading,
22 SkScalar baselineShift,
23 size_t index,
24 SkScalar offsetX)
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
calculateMetrics()61 void Run::calculateMetrics() {
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
newRunBuffer()84 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
85 return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
86 }
87
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size) const88 void 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
findLimitingClusters(TextRange text) const106 std::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
117 ClusterRange clusterRange;
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,
136 clusterRange.start,
137 clusterRange.end);
138 }
139
findLimitingGlyphClusters(TextRange text) const140 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGlyphClusters(TextRange text) const {
141 TextIndex start = fOwner->findPreviousGlyphClusterBoundary(text.start);
142 TextIndex end = fOwner->findNextGlyphClusterBoundary(text.end);
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
findLimitingGraphemes(TextRange text) const149 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const {
150 TextIndex start = fOwner->findPreviousGraphemeBoundary(text.start);
151 TextIndex end = fOwner->findNextGraphemeBoundary(text.end);
152 return std::make_tuple(true, start, end);
153 }
154
iterateThroughClusters(const ClusterVisitor & visitor)155 void Run::iterateThroughClusters(const ClusterVisitor& visitor) {
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
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)164 void Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
165 // Increment the run width
166 fAdvance.fX += space;
167 // Increment the cluster width
168 cluster->space(space);
169 }
170
addSpacesEvenly(SkScalar space)171 SkScalar Run::addSpacesEvenly(SkScalar space) {
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
addSpacesEvenly(SkScalar space,Cluster * cluster)182 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
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
shift(const Cluster * cluster,SkScalar offset)202 void 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
extend(const Cluster * cluster,SkScalar offset)215 void Run::extend(const Cluster* cluster, SkScalar offset) {
216 // Extend the cluster at the end
217 fPositions[cluster->endPos()].fX += offset;
218 }
219
updateMetrics(InternalLineMetrics * endlineMetrics)220 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
221
222 SkASSERT(isPlaceholder());
223 auto placeholderStyle = this->placeholderStyle();
224 // Difference between the placeholder baseline and the line bottom
225 SkScalar baselineAdjustment = 0;
226 switch (placeholderStyle->fBaseline) {
227 case TextBaseline::kAlphabetic:
228 break;
229
230 case TextBaseline::kIdeographic:
231 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
232 break;
233 }
234
235 auto height = placeholderStyle->fHeight;
236 auto offset = placeholderStyle->fBaselineOffset;
237
238 fFontMetrics.fLeading = 0;
239 switch (placeholderStyle->fAlignment) {
240 case PlaceholderAlignment::kBaseline:
241 fFontMetrics.fAscent = baselineAdjustment - offset;
242 fFontMetrics.fDescent = baselineAdjustment + height - offset;
243 break;
244
245 case PlaceholderAlignment::kAboveBaseline:
246 fFontMetrics.fAscent = baselineAdjustment - height;
247 fFontMetrics.fDescent = baselineAdjustment;
248 break;
249
250 case PlaceholderAlignment::kBelowBaseline:
251 fFontMetrics.fAscent = baselineAdjustment;
252 fFontMetrics.fDescent = baselineAdjustment + height;
253 break;
254
255 case PlaceholderAlignment::kTop:
256 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
257 break;
258
259 case PlaceholderAlignment::kBottom:
260 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
261 break;
262
263 case PlaceholderAlignment::kMiddle:
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
sizeToChar(TextIndex ch) const276 SkScalar Cluster::sizeToChar(TextIndex ch) const {
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
sizeFromChar(TextIndex ch) const286 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
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
roundPos(SkScalar s) const296 size_t Cluster::roundPos(SkScalar s) const {
297 auto ratio = (s * 1.0) / fWidth;
298 return sk_double_floor2int(ratio * size());
299 }
300
trimmedWidth(size_t pos) const301 SkScalar Cluster::trimmedWidth(size_t pos) const {
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
positionX(size_t pos) const308 SkScalar Run::positionX(size_t pos) const {
309 return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
310 }
311
placeholderStyle() const312 PlaceholderStyle* Run::placeholderStyle() const {
313 if (isPlaceholder()) {
314 return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
315 } else {
316 return nullptr;
317 }
318 }
319
isResolved() const320 bool Run::isResolved() const {
321 for (auto& glyph :fGlyphs) {
322 if (glyph == 0) {
323 return false;
324 }
325 }
326 return true;
327 }
328
runOrNull() const329 Run* Cluster::runOrNull() const {
330 if (fRunIndex >= fOwner->runs().size()) {
331 return nullptr;
332 }
333 return &fOwner->run(fRunIndex);
334 }
335
run() const336 Run& Cluster::run() const {
337 SkASSERT(fRunIndex < fOwner->runs().size());
338 return fOwner->run(fRunIndex);
339 }
340
font() const341 SkFont Cluster::font() const {
342 SkASSERT(fRunIndex < fOwner->runs().size());
343 return fOwner->run(fRunIndex).font();
344 }
345
isSoftBreak() const346 bool Cluster::isSoftBreak() const {
347 return fOwner->codeUnitHasProperty(fTextRange.end,
348 SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
349 }
350
isGraphemeBreak() const351 bool Cluster::isGraphemeBreak() const {
352 return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
353 }
354 } // namespace textlayout
355 } // namespace skia
356