1 // Copyright 2019 Google LLC.
2 #include "modules/skparagraph/src/Run.h"
3 #include <unicode/brkiter.h>
4 #include "include/core/SkFontMetrics.h"
5 #include "modules/skparagraph/src/ParagraphImpl.h"
6 #include <algorithm>
7 #include "src/utils/SkUTF.h"
8
9 namespace {
10
utf8_next(const char ** ptr,const char * end)11 SkUnichar utf8_next(const char** ptr, const char* end) {
12 SkUnichar val = SkUTF::NextUTF8(ptr, end);
13 return val < 0 ? 0xFFFD : val;
14 }
15
16 }
17
18 namespace skia {
19 namespace textlayout {
20
Run(ParagraphImpl * master,const SkShaper::RunHandler::RunInfo & info,size_t firstChar,SkScalar lineHeight,size_t index,SkScalar offsetX)21 Run::Run(ParagraphImpl* master,
22 const SkShaper::RunHandler::RunInfo& info,
23 size_t firstChar,
24 SkScalar lineHeight,
25 size_t index,
26 SkScalar offsetX)
27 : fMaster(master)
28 , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
29 , fClusterRange(EMPTY_CLUSTERS)
30 , fClusterStart(firstChar) {
31 fFont = info.fFont;
32 fHeightMultiplier = lineHeight;
33 fBidiLevel = info.fBidiLevel;
34 fAdvance = info.fAdvance;
35 fIndex = index;
36 fUtf8Range = info.utf8Range;
37 fOffset = SkVector::Make(offsetX, 0);
38 fGlyphs.push_back_n(info.glyphCount);
39 fBounds.push_back_n(info.glyphCount);
40 fPositions.push_back_n(info.glyphCount + 1);
41 fOffsets.push_back_n(info.glyphCount + 1);
42 fClusterIndexes.push_back_n(info.glyphCount + 1);
43 fShifts.push_back_n(info.glyphCount + 1, 0.0);
44 info.fFont.getMetrics(&fFontMetrics);
45 fSpaced = false;
46 // To make edge cases easier:
47 fPositions[info.glyphCount] = fOffset + fAdvance;
48 fOffsets[info.glyphCount] = { 0, 0};
49 fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
50 fEllipsis = false;
51 fPlaceholderIndex = std::numeric_limits<size_t>::max();
52 }
53
newRunBuffer()54 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
55 return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
56 }
57
commit()58 void Run::commit() {
59 fFont.getBounds(fGlyphs.data(), fGlyphs.size(), fBounds.data(), nullptr);
60 }
calculateWidth(size_t start,size_t end,bool clip) const61 SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
62 SkASSERT(start <= end);
63 // clip |= end == size(); // Clip at the end of the run?
64 SkScalar shift = 0;
65 if (fSpaced && end > start) {
66 shift = fShifts[clip ? end - 1 : end] - fShifts[start];
67 }
68 auto correction = 0.0f;
69 if (end > start) {
70 correction = fMaster->posShift(fIndex, clip ? end - 1 : end) -
71 fMaster->posShift(fIndex, start);
72 }
73 return posX(end) - posX(start) + shift + correction;
74 }
75
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size,SkVector runOffset) const76 void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector runOffset) const {
77 SkASSERT(pos + size <= this->size());
78 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
79 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
80 for (size_t i = 0; i < size; ++i) {
81 auto point = fPositions[i + pos];
82 auto offset = fOffsets[i + pos];
83 point.offset(offset.fX, offset.fY);
84 if (fSpaced) {
85 point.fX += fShifts[i + pos];
86 }
87 point.fX += fMaster->posShift(fIndex, i + pos);
88 blobBuffer.points()[i] = point + runOffset;
89 }
90 }
91
findLimitingClusters(TextRange text,bool extendToClusters) const92 std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool extendToClusters) const {
93
94 if (text.width() == 0) {
95 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
96 auto& cluster = fMaster->cluster(i);
97 if (cluster.textRange().end >= text.end && cluster.textRange().start <= text.start) {
98 return std::make_tuple(true, i, i);
99 }
100 }
101 return std::make_tuple(false, 0, 0);
102 }
103 Cluster* start = nullptr;
104 Cluster* end = nullptr;
105 if (extendToClusters) {
106 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
107 auto& cluster = fMaster->cluster(i);
108 auto clusterRange = cluster.textRange();
109 if (clusterRange.end <= text.start) {
110 continue;
111 } else if (clusterRange.start >= text.end) {
112 break;
113 }
114
115 TextRange s = TextRange(std::max(clusterRange.start, text.start),
116 std::min(clusterRange.end, text.end));
117 if (s.width() > 0) {
118 if (start == nullptr) {
119 start = &cluster;
120 }
121 end = &cluster;
122 }
123 }
124 } else {
125 // TODO: Do we need to use this branch?..
126 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
127 auto& cluster = fMaster->cluster(i);
128 if (cluster.textRange().end > text.start && start == nullptr) {
129 start = &cluster;
130 }
131 if (cluster.textRange().start < text.end) {
132 end = &cluster;
133 } else {
134 break;
135 }
136 }
137 }
138
139 if (start == nullptr || end == nullptr) {
140 return std::make_tuple(false, 0, 0);
141 }
142
143 if (!leftToRight()) {
144 std::swap(start, end);
145 }
146
147 size_t startIndex = start - fMaster->clusters().begin();
148 size_t endIndex = end - fMaster->clusters().begin();
149 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
150 }
151
iterateThroughClustersInTextOrder(const ClusterVisitor & visitor)152 void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
153 // Can't figure out how to do it with one code for both cases without 100 ifs
154 // Can't go through clusters because there are no cluster table yet
155 if (leftToRight()) {
156 size_t start = 0;
157 size_t cluster = this->clusterIndex(start);
158 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
159 auto nextCluster = this->clusterIndex(glyph);
160 if (nextCluster == cluster) {
161 continue;
162 }
163
164 visitor(start,
165 glyph,
166 fClusterStart + cluster,
167 fClusterStart + nextCluster,
168 this->calculateWidth(start, glyph, glyph == size()),
169 this->calculateHeight());
170
171 start = glyph;
172 cluster = nextCluster;
173 }
174 } else {
175 size_t glyph = this->size();
176 size_t cluster = this->fUtf8Range.begin();
177 for (int32_t start = this->size() - 1; start >= 0; --start) {
178 size_t nextCluster =
179 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
180 if (nextCluster == cluster) {
181 continue;
182 }
183
184 visitor(start,
185 glyph,
186 fClusterStart + cluster,
187 fClusterStart + nextCluster,
188 this->calculateWidth(start, glyph, glyph == 0),
189 this->calculateHeight());
190
191 glyph = start;
192 cluster = nextCluster;
193 }
194 }
195 }
196
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)197 SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
198 if (cluster->endPos() == cluster->startPos()) {
199 return 0;
200 }
201
202 fShifts[cluster->endPos() - 1] += space;
203 // Increment the run width
204 fSpaced = true;
205 fAdvance.fX += space;
206 // Increment the cluster width
207 cluster->space(space, space);
208
209 return space;
210 }
211
addSpacesEvenly(SkScalar space,Cluster * cluster)212 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
213 // Offset all the glyphs in the cluster
214 SkScalar shift = 0;
215 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
216 fShifts[i] += shift;
217 shift += space;
218 }
219 if (this->size() == cluster->endPos()) {
220 // To make calculations easier
221 fShifts[cluster->endPos()] += shift;
222 }
223 // Increment the run width
224 fSpaced = true;
225 fAdvance.fX += shift;
226 // Increment the cluster width
227 cluster->space(shift, space);
228 cluster->setHalfLetterSpacing(space / 2);
229
230 return shift;
231 }
232
shift(const Cluster * cluster,SkScalar offset)233 void Run::shift(const Cluster* cluster, SkScalar offset) {
234 if (offset == 0) {
235 return;
236 }
237
238 fSpaced = true;
239 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
240 fShifts[i] += offset;
241 }
242 if (this->size() == cluster->endPos()) {
243 // To make calculations easier
244 fShifts[cluster->endPos()] += offset;
245 }
246 }
247
updateMetrics(InternalLineMetrics * endlineMetrics)248 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
249
250 SkASSERT(isPlaceholder());
251 auto placeholderStyle = this->placeholderStyle();
252 // Difference between the placeholder baseline and the line bottom
253 SkScalar baselineAdjustment = 0;
254 switch (placeholderStyle->fBaseline) {
255 case TextBaseline::kAlphabetic:
256 break;
257
258 case TextBaseline::kIdeographic:
259 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
260 break;
261 }
262
263 auto height = placeholderStyle->fHeight;
264 auto offset = placeholderStyle->fBaselineOffset;
265
266 fFontMetrics.fLeading = 0;
267 switch (placeholderStyle->fAlignment) {
268 case PlaceholderAlignment::kBaseline:
269 fFontMetrics.fAscent = baselineAdjustment - offset;
270 fFontMetrics.fDescent = baselineAdjustment + height - offset;
271 break;
272
273 case PlaceholderAlignment::kAboveBaseline:
274 fFontMetrics.fAscent = baselineAdjustment - height;
275 fFontMetrics.fDescent = baselineAdjustment;
276 break;
277
278 case PlaceholderAlignment::kBelowBaseline:
279 fFontMetrics.fAscent = baselineAdjustment;
280 fFontMetrics.fDescent = baselineAdjustment + height;
281 break;
282
283 case PlaceholderAlignment::kTop:
284 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
285 break;
286
287 case PlaceholderAlignment::kBottom:
288 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
289 break;
290
291 case PlaceholderAlignment::kMiddle:
292 auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
293 fFontMetrics.fDescent = height/2.0 - mid;
294 fFontMetrics.fAscent = - height/2.0 - mid;
295 break;
296 }
297
298 // Make sure the placeholder can fit the line
299 endlineMetrics->add(this);
300 }
301
setIsWhiteSpaces()302 void Cluster::setIsWhiteSpaces() {
303
304 fWhiteSpaces = false;
305
306 auto span = fMaster->text(fTextRange);
307 const char* ch = span.begin();
308 while (ch < span.end()) {
309 auto unichar = utf8_next(&ch, span.end());
310 if (!u_isWhitespace(unichar)) {
311 return;
312 }
313 }
314 fWhiteSpaces = true;
315 }
316
sizeToChar(TextIndex ch) const317 SkScalar Cluster::sizeToChar(TextIndex ch) const {
318 if (ch < fTextRange.start || ch >= fTextRange.end) {
319 return 0;
320 }
321 auto shift = ch - fTextRange.start;
322 auto ratio = shift * 1.0 / fTextRange.width();
323
324 return SkDoubleToScalar(fWidth * ratio);
325 }
326
sizeFromChar(TextIndex ch) const327 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
328 if (ch < fTextRange.start || ch >= fTextRange.end) {
329 return 0;
330 }
331 auto shift = fTextRange.end - ch - 1;
332 auto ratio = shift * 1.0 / fTextRange.width();
333
334 return SkDoubleToScalar(fWidth * ratio);
335 }
336
roundPos(SkScalar s) const337 size_t Cluster::roundPos(SkScalar s) const {
338 auto ratio = (s * 1.0) / fWidth;
339 return sk_double_floor2int(ratio * size());
340 }
341
trimmedWidth(size_t pos) const342 SkScalar Cluster::trimmedWidth(size_t pos) const {
343 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
344 // We don't have to take in account cluster shift since it's the same for 0 and for pos
345 auto& run = fMaster->run(fRunIndex);
346 return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
347 }
348
positionX(size_t pos) const349 SkScalar Run::positionX(size_t pos) const {
350 return posX(pos) + fShifts[pos] + fMaster->posShift(fIndex, pos);
351 }
352
placeholderStyle() const353 PlaceholderStyle* Run::placeholderStyle() const {
354 if (isPlaceholder()) {
355 return &fMaster->placeholders()[fPlaceholderIndex].fStyle;
356 } else {
357 return nullptr;
358 }
359 }
360
run() const361 Run* Cluster::run() const {
362 if (fRunIndex >= fMaster->runs().size()) {
363 return nullptr;
364 }
365 return &fMaster->run(fRunIndex);
366 }
367
font() const368 SkFont Cluster::font() const {
369 return fMaster->run(fRunIndex).font();
370 }
371
Cluster(ParagraphImpl * master,RunIndex runIndex,size_t start,size_t end,SkSpan<const char> text,SkScalar width,SkScalar height)372 Cluster::Cluster(ParagraphImpl* master,
373 RunIndex runIndex,
374 size_t start,
375 size_t end,
376 SkSpan<const char> text,
377 SkScalar width,
378 SkScalar height)
379 : fMaster(master)
380 , fRunIndex(runIndex)
381 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
382 , fGraphemeRange(EMPTY_RANGE)
383 , fStart(start)
384 , fEnd(end)
385 , fWidth(width)
386 , fSpacing(0)
387 , fHeight(height)
388 , fHalfLetterSpacing(0.0)
389 , fWhiteSpaces(false)
390 , fBreakType(None) {
391 }
392
393 } // namespace textlayout
394 } // namespace skia
395