blob: 0fbcc07e796228570edb32f1bc6803f380ed7394 [file] [log] [blame]
Julia Lavrovaa3552c52019-05-30 16:12:56 -04001// Copyright 2019 Google LLC.
2#include "modules/skparagraph/src/Run.h"
3#include <unicode/brkiter.h>
4#include "include/core/SkFontMetrics.h"
Julia Lavrova5207f352019-06-21 12:22:32 -04005#include "modules/skparagraph/src/ParagraphImpl.h"
6#include <algorithm>
Julia Lavrovadb9f6692019-08-01 16:02:17 -04007#include "src/utils/SkUTF.h"
8
9namespace {
10
11SkUnichar utf8_next(const char** ptr, const char* end) {
12 SkUnichar val = SkUTF::NextUTF8(ptr, end);
13 return val < 0 ? 0xFFFD : val;
14}
15}
Julia Lavrovaa3552c52019-05-30 16:12:56 -040016
17namespace skia {
18namespace textlayout {
19
Julia Lavrova5207f352019-06-21 12:22:32 -040020Run::Run(ParagraphImpl* master,
Julia Lavrovaa3552c52019-05-30 16:12:56 -040021 const SkShaper::RunHandler::RunInfo& info,
Julia Lavrova916a9042019-08-08 16:51:27 -040022 size_t firstChar,
Julia Lavrovaa3552c52019-05-30 16:12:56 -040023 SkScalar lineHeight,
24 size_t index,
Julia Lavrova5207f352019-06-21 12:22:32 -040025 SkScalar offsetX)
26 : fMaster(master)
Julia Lavrova916a9042019-08-08 16:51:27 -040027 , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
28 , fClusterRange(EMPTY_CLUSTERS)
29 , fFirstChar(firstChar) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040030 fFont = info.fFont;
31 fHeightMultiplier = lineHeight;
32 fBidiLevel = info.fBidiLevel;
33 fAdvance = info.fAdvance;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040034 fIndex = index;
35 fUtf8Range = info.utf8Range;
36 fOffset = SkVector::Make(offsetX, 0);
37 fGlyphs.push_back_n(info.glyphCount);
38 fPositions.push_back_n(info.glyphCount + 1);
Julia Lavrova5207f352019-06-21 12:22:32 -040039 fOffsets.push_back_n(info.glyphCount + 1, 0.0);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040040 fClusterIndexes.push_back_n(info.glyphCount + 1);
41 info.fFont.getMetrics(&fFontMetrics);
42 fSpaced = false;
43 // To make edge cases easier:
44 fPositions[info.glyphCount] = fOffset + fAdvance;
45 fClusterIndexes[info.glyphCount] = info.utf8Range.end();
Julia Lavrova526df262019-08-21 17:49:44 -040046 fEllipsis = false;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040047}
48
49SkShaper::RunHandler::Buffer Run::newRunBuffer() {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040050 return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
51}
52
53SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040054 SkASSERT(start <= end);
55 // clip |= end == size(); // Clip at the end of the run?
56 SkScalar offset = 0;
57 if (fSpaced && end > start) {
58 offset = fOffsets[clip ? end - 1 : end] - fOffsets[start];
59 }
Julia Lavrova5207f352019-06-21 12:22:32 -040060 auto correction = end > start ? fMaster->posShift(fIndex, end - 1) - fMaster->posShift(fIndex, start) : 0;
61 return fPositions[end].fX - fPositions[start].fX + offset + correction;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040062}
63
64void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040065 SkASSERT(pos + size <= this->size());
66 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
67 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
68
69 if (fSpaced || offset.fX != 0 || offset.fY != 0) {
70 for (size_t i = 0; i < size; ++i) {
71 auto point = fPositions[i + pos];
72 if (fSpaced) {
73 point.fX += fOffsets[i + pos];
74 }
Julia Lavrova5207f352019-06-21 12:22:32 -040075 point.fX += fMaster->posShift(fIndex, i + pos);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040076 blobBuffer.points()[i] = point + offset;
77 }
78 } else {
79 // Good for the first line
80 sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
81 }
82}
83
Julia Lavrovaf3ed2732019-09-05 14:35:17 -040084std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool onlyInnerClusters) const {
Julia Lavrova5207f352019-06-21 12:22:32 -040085
86 if (text.width() == 0) {
Julia Lavrovaf3ed2732019-09-05 14:35:17 -040087 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
88 auto& cluster = fMaster->cluster(i);
89 if (cluster.textRange().end >= text.end && cluster.textRange().start <= text.start) {
90 return std::make_tuple(true, i, i);
91 }
92 }
93 return std::make_tuple(false, 0, 0);
94 }
95 Cluster* start = nullptr;
96 Cluster* end = nullptr;
97 if (onlyInnerClusters) {
98 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
99 auto& cluster = fMaster->cluster(i);
100 if (cluster.textRange().start >= text.start && start == nullptr) {
101 start = &cluster;
102 }
103 if (cluster.textRange().end <= text.end) {
104 end = &cluster;
105 } else {
106 break;
107 }
108 }
109 } else {
110 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
111 auto& cluster = fMaster->cluster(i);
112 if (cluster.textRange().end > text.start && start == nullptr) {
113 start = &cluster;
114 }
115 if (cluster.textRange().start < text.end) {
116 end = &cluster;
117 } else {
118 break;
119 }
120 }
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400121 }
122
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400123 if (start == nullptr || end == nullptr) {
124 return std::make_tuple(false, 0, 0);
125 }
126
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400127 if (!leftToRight()) {
128 std::swap(start, end);
129 }
130
Julia Lavrova5207f352019-06-21 12:22:32 -0400131 size_t startIndex = start - fMaster->clusters().begin();
132 size_t endIndex = end - fMaster->clusters().begin();
133 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400134}
135
136void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400137 // Can't figure out how to do it with one code for both cases without 100 ifs
138 // Can't go through clusters because there are no cluster table yet
139 if (leftToRight()) {
140 size_t start = 0;
141 size_t cluster = this->clusterIndex(start);
142 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
143 auto nextCluster = this->clusterIndex(glyph);
144 if (nextCluster == cluster) {
145 continue;
146 }
147
148 visitor(start,
149 glyph,
Julia Lavrova916a9042019-08-08 16:51:27 -0400150 fFirstChar + cluster,
151 fFirstChar + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400152 this->calculateWidth(start, glyph, glyph == size()),
153 this->calculateHeight());
154
155 start = glyph;
156 cluster = nextCluster;
157 }
158 } else {
159 size_t glyph = this->size();
160 size_t cluster = this->fUtf8Range.begin();
161 for (int32_t start = this->size() - 1; start >= 0; --start) {
162 size_t nextCluster =
163 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
164 if (nextCluster == cluster) {
165 continue;
166 }
167
168 visitor(start,
169 glyph,
Julia Lavrova916a9042019-08-08 16:51:27 -0400170 fFirstChar + cluster,
171 fFirstChar + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400172 this->calculateWidth(start, glyph, glyph == 0),
173 this->calculateHeight());
174
175 glyph = start;
176 cluster = nextCluster;
177 }
178 }
179}
180
181SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400182 if (cluster->endPos() == cluster->startPos()) {
183 return 0;
184 }
185
186 fOffsets[cluster->endPos() - 1] += space;
187 // Increment the run width
188 fSpaced = true;
189 fAdvance.fX += space;
190 // Increment the cluster width
191 cluster->space(space, space);
192
193 return space;
194}
195
196SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400197 // Offset all the glyphs in the cluster
198 SkScalar shift = 0;
199 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
200 fOffsets[i] += shift;
201 shift += space;
202 }
203 if (this->size() == cluster->endPos()) {
204 // To make calculations easier
205 fOffsets[cluster->endPos()] += shift;
206 }
207 // Increment the run width
208 fSpaced = true;
209 fAdvance.fX += shift;
210 // Increment the cluster width
211 cluster->space(shift, space);
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400212 cluster->setHalfLetterSpacing(space / 2);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400213
214 return shift;
215}
216
217void Run::shift(const Cluster* cluster, SkScalar offset) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400218 if (offset == 0) {
219 return;
220 }
221
222 fSpaced = true;
223 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
224 fOffsets[i] += offset;
225 }
226 if (this->size() == cluster->endPos()) {
227 // To make calculations easier
228 fOffsets[cluster->endPos()] += offset;
229 }
230}
231
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400232void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
Julia Lavrova916a9042019-08-08 16:51:27 -0400233
234 // Difference between the placeholder baseline and the line bottom
235 SkScalar baselineAdjustment = 0;
236 switch (fPlaceholder->fBaseline) {
237 case TextBaseline::kAlphabetic:
238 break;
239
240 case TextBaseline::kIdeographic:
241 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
242 break;
243 }
244
245 auto height = fPlaceholder->fHeight;
246 auto offset = fPlaceholder->fBaselineOffset;
247
248 fFontMetrics.fLeading = 0;
249 switch (fPlaceholder->fAlignment) {
250 case PlaceholderAlignment::kBaseline:
251 fFontMetrics.fAscent = baselineAdjustment - offset;
252 fFontMetrics.fDescent = baselineAdjustment + height - offset;
253 break;
254
255 case PlaceholderAlignment::kAboveBaseline:
256 fFontMetrics.fAscent = baselineAdjustment - height;
257 fFontMetrics.fDescent = baselineAdjustment;
258 break;
259
260 case PlaceholderAlignment::kBelowBaseline:
261 fFontMetrics.fAscent = baselineAdjustment;
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400262 fFontMetrics.fDescent = baselineAdjustment + height;
Julia Lavrova916a9042019-08-08 16:51:27 -0400263 break;
264
265 case PlaceholderAlignment::kTop:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400266 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
Julia Lavrova916a9042019-08-08 16:51:27 -0400267 break;
268
269 case PlaceholderAlignment::kBottom:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400270 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
Julia Lavrova916a9042019-08-08 16:51:27 -0400271 break;
272
273 case PlaceholderAlignment::kMiddle:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400274 auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
275 fFontMetrics.fDescent = height/2.0 - mid;
276 fFontMetrics.fAscent = - height/2.0 - mid;
Julia Lavrova916a9042019-08-08 16:51:27 -0400277 break;
278 }
279
280 // Make sure the placeholder can fit the line
281 endlineMetrics->add(this);
282}
283
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400284void Cluster::setIsWhiteSpaces() {
Julia Lavrovadb9f6692019-08-01 16:02:17 -0400285
286 fWhiteSpaces = false;
287
288 auto span = fMaster->text(fTextRange);
289 const char* ch = span.begin();
290 while (ch < span.end()) {
291 auto unichar = utf8_next(&ch, span.end());
292 if (!u_isWhitespace(unichar)) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400293 return;
294 }
295 }
296 fWhiteSpaces = true;
297}
298
Julia Lavrova5207f352019-06-21 12:22:32 -0400299SkScalar Cluster::sizeToChar(TextIndex ch) const {
300 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400301 return 0;
302 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400303 auto shift = ch - fTextRange.start;
304 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400305
306 return SkDoubleToScalar(fWidth * ratio);
307}
308
Julia Lavrova5207f352019-06-21 12:22:32 -0400309SkScalar Cluster::sizeFromChar(TextIndex ch) const {
310 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400311 return 0;
312 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400313 auto shift = fTextRange.end - ch - 1;
314 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400315
316 return SkDoubleToScalar(fWidth * ratio);
317}
318
319size_t Cluster::roundPos(SkScalar s) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400320 auto ratio = (s * 1.0) / fWidth;
321 return sk_double_floor2int(ratio * size());
322}
323
324SkScalar Cluster::trimmedWidth(size_t pos) const {
325 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
Julia Lavrova5207f352019-06-21 12:22:32 -0400326 // We don't have to take in account cluster shift since it's the same for 0 and for pos
327 auto& run = fMaster->run(fRunIndex);
328 return SkTMin(run.positionX(pos) - run.positionX(fStart), fWidth - fSpacing);
329}
330
331SkScalar Run::positionX(size_t pos) const {
332 return fPositions[pos].fX + fOffsets[pos] + fMaster->posShift(fIndex, pos);
333}
334
335Run* Cluster::run() const {
336 if (fRunIndex >= fMaster->runs().size()) {
337 return nullptr;
338 }
339 return &fMaster->run(fRunIndex);
340}
341
342SkFont Cluster::font() const {
343 return fMaster->run(fRunIndex).font();
344}
345
346Cluster::Cluster(ParagraphImpl* master,
347 RunIndex runIndex,
348 size_t start,
349 size_t end,
350 SkSpan<const char> text,
351 SkScalar width,
352 SkScalar height)
353 : fMaster(master)
354 , fRunIndex(runIndex)
355 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
Julia Lavrovac2228562019-08-08 16:51:27 -0400356 , fGraphemeRange(EMPTY_RANGE)
Julia Lavrova5207f352019-06-21 12:22:32 -0400357 , fStart(start)
358 , fEnd(end)
359 , fWidth(width)
360 , fSpacing(0)
361 , fHeight(height)
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400362 , fHalfLetterSpacing(0.0)
Julia Lavrova5207f352019-06-21 12:22:32 -0400363 , fWhiteSpaces(false)
364 , fBreakType(None) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400365}
366
367} // namespace textlayout
368} // namespace skia