blob: e679be8648c0ff19afdaf781bfd3680692e5bf23 [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}
Julia Lavrova62076972020-02-19 15:12:48 -050015
Julia Lavrovadb9f6692019-08-01 16:02:17 -040016}
Julia Lavrovaa3552c52019-05-30 16:12:56 -040017
18namespace skia {
19namespace textlayout {
20
Julia Lavrova5207f352019-06-21 12:22:32 -040021Run::Run(ParagraphImpl* master,
Julia Lavrovaa3552c52019-05-30 16:12:56 -040022 const SkShaper::RunHandler::RunInfo& info,
Julia Lavrova916a9042019-08-08 16:51:27 -040023 size_t firstChar,
Julia Lavrovaa3552c52019-05-30 16:12:56 -040024 SkScalar lineHeight,
25 size_t index,
Julia Lavrova5207f352019-06-21 12:22:32 -040026 SkScalar offsetX)
27 : fMaster(master)
Julia Lavrova916a9042019-08-08 16:51:27 -040028 , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
29 , fClusterRange(EMPTY_CLUSTERS)
Julia Lavrova2e30fde2019-10-09 09:43:02 -040030 , fClusterStart(firstChar) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040031 fFont = info.fFont;
32 fHeightMultiplier = lineHeight;
33 fBidiLevel = info.fBidiLevel;
34 fAdvance = info.fAdvance;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040035 fIndex = index;
36 fUtf8Range = info.utf8Range;
37 fOffset = SkVector::Make(offsetX, 0);
38 fGlyphs.push_back_n(info.glyphCount);
Julia Lavrova2e30fde2019-10-09 09:43:02 -040039 fBounds.push_back_n(info.glyphCount);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040040 fPositions.push_back_n(info.glyphCount + 1);
Julia Lavrova2e30fde2019-10-09 09:43:02 -040041 fOffsets.push_back_n(info.glyphCount + 1);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040042 fClusterIndexes.push_back_n(info.glyphCount + 1);
Julia Lavrova2e30fde2019-10-09 09:43:02 -040043 fShifts.push_back_n(info.glyphCount + 1, 0.0);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040044 info.fFont.getMetrics(&fFontMetrics);
45 fSpaced = false;
46 // To make edge cases easier:
47 fPositions[info.glyphCount] = fOffset + fAdvance;
Julia Lavrova2e30fde2019-10-09 09:43:02 -040048 fOffsets[info.glyphCount] = { 0, 0};
49 fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
Julia Lavrova526df262019-08-21 17:49:44 -040050 fEllipsis = false;
Julia Lavrovac0360582020-02-05 10:17:53 -050051 fPlaceholderIndex = std::numeric_limits<size_t>::max();
Julia Lavrovaa3552c52019-05-30 16:12:56 -040052}
53
54SkShaper::RunHandler::Buffer Run::newRunBuffer() {
Julia Lavrova2e30fde2019-10-09 09:43:02 -040055 return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
Julia Lavrovaa3552c52019-05-30 16:12:56 -040056}
57
Julia Lavrova2e30fde2019-10-09 09:43:02 -040058void Run::commit() {
59 fFont.getBounds(fGlyphs.data(), fGlyphs.size(), fBounds.data(), nullptr);
60}
Julia Lavrovaa3552c52019-05-30 16:12:56 -040061SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040062 SkASSERT(start <= end);
63 // clip |= end == size(); // Clip at the end of the run?
Julia Lavrova2e30fde2019-10-09 09:43:02 -040064 SkScalar shift = 0;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040065 if (fSpaced && end > start) {
Julia Lavrova2e30fde2019-10-09 09:43:02 -040066 shift = fShifts[clip ? end - 1 : end] - fShifts[start];
Julia Lavrovaa3552c52019-05-30 16:12:56 -040067 }
Julia Lavrova2e30fde2019-10-09 09:43:02 -040068 auto correction = 0.0f;
Julia Lavrovaa0708e82020-02-28 12:14:58 -050069 if (end > start && !fJustificationShifts.empty()) {
Julia Lavrova68d14332020-05-11 13:47:08 -040070 // This is not a typo: we are using Point as a pair of SkScalars
Julia Lavrovaa0708e82020-02-28 12:14:58 -050071 correction = fJustificationShifts[end - 1].fX -
72 fJustificationShifts[start].fY;
Julia Lavrova2e30fde2019-10-09 09:43:02 -040073 }
74 return posX(end) - posX(start) + shift + correction;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040075}
76
Julia Lavrova2e30fde2019-10-09 09:43:02 -040077void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector runOffset) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040078 SkASSERT(pos + size <= this->size());
79 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
80 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
Julia Lavrova2e30fde2019-10-09 09:43:02 -040081 for (size_t i = 0; i < size; ++i) {
82 auto point = fPositions[i + pos];
83 auto offset = fOffsets[i + pos];
84 point.offset(offset.fX, offset.fY);
85 if (fSpaced) {
86 point.fX += fShifts[i + pos];
Julia Lavrovaa3552c52019-05-30 16:12:56 -040087 }
Julia Lavrovaa0708e82020-02-28 12:14:58 -050088 if (!fJustificationShifts.empty()) {
89 point.fX += fJustificationShifts[i + pos].fX;
90 }
Julia Lavrova2e30fde2019-10-09 09:43:02 -040091 blobBuffer.points()[i] = point + runOffset;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040092 }
93}
94
Julia Lavrova62076972020-02-19 15:12:48 -050095std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool extendToClusters) const {
Julia Lavrova5207f352019-06-21 12:22:32 -040096
97 if (text.width() == 0) {
Julia Lavrovaf3ed2732019-09-05 14:35:17 -040098 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
99 auto& cluster = fMaster->cluster(i);
100 if (cluster.textRange().end >= text.end && cluster.textRange().start <= text.start) {
101 return std::make_tuple(true, i, i);
102 }
103 }
104 return std::make_tuple(false, 0, 0);
105 }
106 Cluster* start = nullptr;
107 Cluster* end = nullptr;
Julia Lavrova62076972020-02-19 15:12:48 -0500108 if (extendToClusters) {
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400109 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
110 auto& cluster = fMaster->cluster(i);
Julia Lavrova62076972020-02-19 15:12:48 -0500111 auto clusterRange = cluster.textRange();
112 if (clusterRange.end <= text.start) {
113 continue;
114 } else if (clusterRange.start >= text.end) {
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400115 break;
116 }
Julia Lavrova62076972020-02-19 15:12:48 -0500117
118 TextRange s = TextRange(std::max(clusterRange.start, text.start),
119 std::min(clusterRange.end, text.end));
120 if (s.width() > 0) {
121 if (start == nullptr) {
122 start = &cluster;
123 }
124 end = &cluster;
125 }
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400126 }
127 } else {
Julia Lavrovacd2d4e42020-03-27 15:40:37 -0400128 // We need this branch when we draw styles for the part of a cluster
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400129 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
130 auto& cluster = fMaster->cluster(i);
131 if (cluster.textRange().end > text.start && start == nullptr) {
132 start = &cluster;
133 }
134 if (cluster.textRange().start < text.end) {
135 end = &cluster;
136 } else {
137 break;
138 }
139 }
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400140 }
141
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400142 if (start == nullptr || end == nullptr) {
143 return std::make_tuple(false, 0, 0);
144 }
145
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400146 if (!leftToRight()) {
147 std::swap(start, end);
148 }
149
Julia Lavrova5207f352019-06-21 12:22:32 -0400150 size_t startIndex = start - fMaster->clusters().begin();
151 size_t endIndex = end - fMaster->clusters().begin();
152 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400153}
154
Julia Lavrova8335ab62020-04-27 15:49:53 -0400155void Run::iterateThroughClustersInTextOrder(const ClusterTextVisitor& visitor) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400156 // Can't figure out how to do it with one code for both cases without 100 ifs
157 // Can't go through clusters because there are no cluster table yet
158 if (leftToRight()) {
159 size_t start = 0;
160 size_t cluster = this->clusterIndex(start);
161 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
162 auto nextCluster = this->clusterIndex(glyph);
Julia Lavrova99ede422020-03-17 13:20:58 -0400163 if (nextCluster <= cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400164 continue;
165 }
166
167 visitor(start,
168 glyph,
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400169 fClusterStart + cluster,
170 fClusterStart + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400171 this->calculateWidth(start, glyph, glyph == size()),
Julia Lavrovadd1de252020-05-08 11:53:19 -0400172 this->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS));
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400173
174 start = glyph;
175 cluster = nextCluster;
176 }
177 } else {
178 size_t glyph = this->size();
179 size_t cluster = this->fUtf8Range.begin();
180 for (int32_t start = this->size() - 1; start >= 0; --start) {
181 size_t nextCluster =
182 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
Julia Lavrova99ede422020-03-17 13:20:58 -0400183 if (nextCluster <= cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400184 continue;
185 }
186
187 visitor(start,
188 glyph,
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400189 fClusterStart + cluster,
190 fClusterStart + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400191 this->calculateWidth(start, glyph, glyph == 0),
Julia Lavrovadd1de252020-05-08 11:53:19 -0400192 this->calculateHeight(LineMetricStyle::CSS, LineMetricStyle::CSS));
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400193
194 glyph = start;
195 cluster = nextCluster;
196 }
197 }
198}
199
Julia Lavrova8335ab62020-04-27 15:49:53 -0400200void Run::iterateThroughClusters(const ClusterVisitor& visitor) {
201
202 for (size_t index = 0; index < fClusterRange.width(); ++index) {
203 auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1;
204 auto cluster = &fMaster->cluster(correctIndex);
205 visitor(cluster);
206 }
207}
208
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400209SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400210 if (cluster->endPos() == cluster->startPos()) {
211 return 0;
212 }
213
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400214 fShifts[cluster->endPos() - 1] += space;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400215 // Increment the run width
216 fSpaced = true;
217 fAdvance.fX += space;
218 // Increment the cluster width
219 cluster->space(space, space);
220
221 return space;
222}
223
224SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400225 // Offset all the glyphs in the cluster
226 SkScalar shift = 0;
227 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400228 fShifts[i] += shift;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400229 shift += space;
230 }
231 if (this->size() == cluster->endPos()) {
232 // To make calculations easier
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400233 fShifts[cluster->endPos()] += shift;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400234 }
235 // Increment the run width
236 fSpaced = true;
237 fAdvance.fX += shift;
238 // Increment the cluster width
239 cluster->space(shift, space);
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400240 cluster->setHalfLetterSpacing(space / 2);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400241
242 return shift;
243}
244
245void Run::shift(const Cluster* cluster, SkScalar offset) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400246 if (offset == 0) {
247 return;
248 }
249
250 fSpaced = true;
251 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400252 fShifts[i] += offset;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400253 }
254 if (this->size() == cluster->endPos()) {
255 // To make calculations easier
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400256 fShifts[cluster->endPos()] += offset;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400257 }
258}
259
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400260void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
Julia Lavrova916a9042019-08-08 16:51:27 -0400261
Julia Lavrovac0360582020-02-05 10:17:53 -0500262 SkASSERT(isPlaceholder());
263 auto placeholderStyle = this->placeholderStyle();
Julia Lavrova916a9042019-08-08 16:51:27 -0400264 // Difference between the placeholder baseline and the line bottom
265 SkScalar baselineAdjustment = 0;
Julia Lavrovac0360582020-02-05 10:17:53 -0500266 switch (placeholderStyle->fBaseline) {
Julia Lavrova916a9042019-08-08 16:51:27 -0400267 case TextBaseline::kAlphabetic:
268 break;
269
270 case TextBaseline::kIdeographic:
271 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
272 break;
273 }
274
Julia Lavrovac0360582020-02-05 10:17:53 -0500275 auto height = placeholderStyle->fHeight;
276 auto offset = placeholderStyle->fBaselineOffset;
Julia Lavrova916a9042019-08-08 16:51:27 -0400277
278 fFontMetrics.fLeading = 0;
Julia Lavrovac0360582020-02-05 10:17:53 -0500279 switch (placeholderStyle->fAlignment) {
Julia Lavrova916a9042019-08-08 16:51:27 -0400280 case PlaceholderAlignment::kBaseline:
281 fFontMetrics.fAscent = baselineAdjustment - offset;
282 fFontMetrics.fDescent = baselineAdjustment + height - offset;
283 break;
284
285 case PlaceholderAlignment::kAboveBaseline:
286 fFontMetrics.fAscent = baselineAdjustment - height;
287 fFontMetrics.fDescent = baselineAdjustment;
288 break;
289
290 case PlaceholderAlignment::kBelowBaseline:
291 fFontMetrics.fAscent = baselineAdjustment;
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400292 fFontMetrics.fDescent = baselineAdjustment + height;
Julia Lavrova916a9042019-08-08 16:51:27 -0400293 break;
294
295 case PlaceholderAlignment::kTop:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400296 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
Julia Lavrova916a9042019-08-08 16:51:27 -0400297 break;
298
299 case PlaceholderAlignment::kBottom:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400300 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
Julia Lavrova916a9042019-08-08 16:51:27 -0400301 break;
302
303 case PlaceholderAlignment::kMiddle:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400304 auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
305 fFontMetrics.fDescent = height/2.0 - mid;
306 fFontMetrics.fAscent = - height/2.0 - mid;
Julia Lavrova916a9042019-08-08 16:51:27 -0400307 break;
308 }
309
310 // Make sure the placeholder can fit the line
311 endlineMetrics->add(this);
312}
313
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400314void Cluster::setIsWhiteSpaces() {
Julia Lavrovadb9f6692019-08-01 16:02:17 -0400315
316 fWhiteSpaces = false;
317
318 auto span = fMaster->text(fTextRange);
319 const char* ch = span.begin();
320 while (ch < span.end()) {
321 auto unichar = utf8_next(&ch, span.end());
322 if (!u_isWhitespace(unichar)) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400323 return;
324 }
325 }
326 fWhiteSpaces = true;
327}
328
Julia Lavrova5207f352019-06-21 12:22:32 -0400329SkScalar Cluster::sizeToChar(TextIndex ch) const {
330 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400331 return 0;
332 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400333 auto shift = ch - fTextRange.start;
334 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400335
336 return SkDoubleToScalar(fWidth * ratio);
337}
338
Julia Lavrova5207f352019-06-21 12:22:32 -0400339SkScalar Cluster::sizeFromChar(TextIndex ch) const {
340 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400341 return 0;
342 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400343 auto shift = fTextRange.end - ch - 1;
344 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400345
346 return SkDoubleToScalar(fWidth * ratio);
347}
348
349size_t Cluster::roundPos(SkScalar s) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400350 auto ratio = (s * 1.0) / fWidth;
351 return sk_double_floor2int(ratio * size());
352}
353
354SkScalar Cluster::trimmedWidth(size_t pos) const {
355 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
Julia Lavrova5207f352019-06-21 12:22:32 -0400356 // We don't have to take in account cluster shift since it's the same for 0 and for pos
357 auto& run = fMaster->run(fRunIndex);
Julia Lavrova212bf072020-02-18 12:05:55 -0500358 return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
Julia Lavrova5207f352019-06-21 12:22:32 -0400359}
360
361SkScalar Run::positionX(size_t pos) const {
Julia Lavrovaa0708e82020-02-28 12:14:58 -0500362 return posX(pos) + fShifts[pos] +
363 (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
Julia Lavrova5207f352019-06-21 12:22:32 -0400364}
365
Julia Lavrovac0360582020-02-05 10:17:53 -0500366PlaceholderStyle* Run::placeholderStyle() const {
367 if (isPlaceholder()) {
368 return &fMaster->placeholders()[fPlaceholderIndex].fStyle;
369 } else {
370 return nullptr;
371 }
372}
373
Julia Lavrova5207f352019-06-21 12:22:32 -0400374Run* Cluster::run() const {
375 if (fRunIndex >= fMaster->runs().size()) {
376 return nullptr;
377 }
378 return &fMaster->run(fRunIndex);
379}
380
381SkFont Cluster::font() const {
382 return fMaster->run(fRunIndex).font();
383}
384
385Cluster::Cluster(ParagraphImpl* master,
386 RunIndex runIndex,
387 size_t start,
388 size_t end,
389 SkSpan<const char> text,
390 SkScalar width,
391 SkScalar height)
392 : fMaster(master)
393 , fRunIndex(runIndex)
394 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
Julia Lavrovac2228562019-08-08 16:51:27 -0400395 , fGraphemeRange(EMPTY_RANGE)
Julia Lavrova5207f352019-06-21 12:22:32 -0400396 , fStart(start)
397 , fEnd(end)
398 , fWidth(width)
399 , fSpacing(0)
400 , fHeight(height)
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400401 , fHalfLetterSpacing(0.0)
Julia Lavrova5207f352019-06-21 12:22:32 -0400402 , fWhiteSpaces(false)
403 , fBreakType(None) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400404}
405
406} // namespace textlayout
407} // namespace skia