blob: 9bcdf90c683050d69a225e274712e29fe9df92ca [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)
Julia Lavrova2e30fde2019-10-09 09:43:02 -040029 , fClusterStart(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);
Julia Lavrova2e30fde2019-10-09 09:43:02 -040038 fBounds.push_back_n(info.glyphCount);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040039 fPositions.push_back_n(info.glyphCount + 1);
Julia Lavrova2e30fde2019-10-09 09:43:02 -040040 fOffsets.push_back_n(info.glyphCount + 1);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040041 fClusterIndexes.push_back_n(info.glyphCount + 1);
Julia Lavrova2e30fde2019-10-09 09:43:02 -040042 fShifts.push_back_n(info.glyphCount + 1, 0.0);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040043 info.fFont.getMetrics(&fFontMetrics);
44 fSpaced = false;
45 // To make edge cases easier:
46 fPositions[info.glyphCount] = fOffset + fAdvance;
Julia Lavrova2e30fde2019-10-09 09:43:02 -040047 fOffsets[info.glyphCount] = { 0, 0};
48 fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
Julia Lavrova526df262019-08-21 17:49:44 -040049 fEllipsis = false;
Julia Lavrovac0360582020-02-05 10:17:53 -050050 fPlaceholderIndex = std::numeric_limits<size_t>::max();
Julia Lavrovaa3552c52019-05-30 16:12:56 -040051}
52
53SkShaper::RunHandler::Buffer Run::newRunBuffer() {
Julia Lavrova2e30fde2019-10-09 09:43:02 -040054 return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
Julia Lavrovaa3552c52019-05-30 16:12:56 -040055}
56
Julia Lavrova2e30fde2019-10-09 09:43:02 -040057void Run::commit() {
58 fFont.getBounds(fGlyphs.data(), fGlyphs.size(), fBounds.data(), nullptr);
59}
Julia Lavrovaa3552c52019-05-30 16:12:56 -040060SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040061 SkASSERT(start <= end);
62 // clip |= end == size(); // Clip at the end of the run?
Julia Lavrova2e30fde2019-10-09 09:43:02 -040063 SkScalar shift = 0;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040064 if (fSpaced && end > start) {
Julia Lavrova2e30fde2019-10-09 09:43:02 -040065 shift = fShifts[clip ? end - 1 : end] - fShifts[start];
Julia Lavrovaa3552c52019-05-30 16:12:56 -040066 }
Julia Lavrova2e30fde2019-10-09 09:43:02 -040067 auto correction = 0.0f;
68 if (end > start) {
69 correction = fMaster->posShift(fIndex, clip ? end - 1 : end) -
70 fMaster->posShift(fIndex, start);
71 }
72 return posX(end) - posX(start) + shift + correction;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040073}
74
Julia Lavrova2e30fde2019-10-09 09:43:02 -040075void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector runOffset) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040076 SkASSERT(pos + size <= this->size());
77 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
78 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
Julia Lavrova2e30fde2019-10-09 09:43:02 -040079 for (size_t i = 0; i < size; ++i) {
80 auto point = fPositions[i + pos];
81 auto offset = fOffsets[i + pos];
82 point.offset(offset.fX, offset.fY);
83 if (fSpaced) {
84 point.fX += fShifts[i + pos];
Julia Lavrovaa3552c52019-05-30 16:12:56 -040085 }
Julia Lavrova2e30fde2019-10-09 09:43:02 -040086 point.fX += fMaster->posShift(fIndex, i + pos);
87 blobBuffer.points()[i] = point + runOffset;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040088 }
89}
90
Julia Lavrovaf3ed2732019-09-05 14:35:17 -040091std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool onlyInnerClusters) const {
Julia Lavrova5207f352019-06-21 12:22:32 -040092
93 if (text.width() == 0) {
Julia Lavrovaf3ed2732019-09-05 14:35:17 -040094 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
95 auto& cluster = fMaster->cluster(i);
96 if (cluster.textRange().end >= text.end && cluster.textRange().start <= text.start) {
97 return std::make_tuple(true, i, i);
98 }
99 }
100 return std::make_tuple(false, 0, 0);
101 }
102 Cluster* start = nullptr;
103 Cluster* end = nullptr;
104 if (onlyInnerClusters) {
105 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
106 auto& cluster = fMaster->cluster(i);
107 if (cluster.textRange().start >= text.start && start == nullptr) {
108 start = &cluster;
109 }
110 if (cluster.textRange().end <= text.end) {
111 end = &cluster;
112 } else {
113 break;
114 }
115 }
116 } else {
117 for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
118 auto& cluster = fMaster->cluster(i);
119 if (cluster.textRange().end > text.start && start == nullptr) {
120 start = &cluster;
121 }
122 if (cluster.textRange().start < text.end) {
123 end = &cluster;
124 } else {
125 break;
126 }
127 }
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400128 }
129
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400130 if (start == nullptr || end == nullptr) {
131 return std::make_tuple(false, 0, 0);
132 }
133
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400134 if (!leftToRight()) {
135 std::swap(start, end);
136 }
137
Julia Lavrova5207f352019-06-21 12:22:32 -0400138 size_t startIndex = start - fMaster->clusters().begin();
139 size_t endIndex = end - fMaster->clusters().begin();
140 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400141}
142
143void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400144 // Can't figure out how to do it with one code for both cases without 100 ifs
145 // Can't go through clusters because there are no cluster table yet
146 if (leftToRight()) {
147 size_t start = 0;
148 size_t cluster = this->clusterIndex(start);
149 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
150 auto nextCluster = this->clusterIndex(glyph);
151 if (nextCluster == cluster) {
152 continue;
153 }
154
155 visitor(start,
156 glyph,
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400157 fClusterStart + cluster,
158 fClusterStart + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400159 this->calculateWidth(start, glyph, glyph == size()),
160 this->calculateHeight());
161
162 start = glyph;
163 cluster = nextCluster;
164 }
165 } else {
166 size_t glyph = this->size();
167 size_t cluster = this->fUtf8Range.begin();
168 for (int32_t start = this->size() - 1; start >= 0; --start) {
169 size_t nextCluster =
170 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
171 if (nextCluster == cluster) {
172 continue;
173 }
174
175 visitor(start,
176 glyph,
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400177 fClusterStart + cluster,
178 fClusterStart + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400179 this->calculateWidth(start, glyph, glyph == 0),
180 this->calculateHeight());
181
182 glyph = start;
183 cluster = nextCluster;
184 }
185 }
186}
187
188SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400189 if (cluster->endPos() == cluster->startPos()) {
190 return 0;
191 }
192
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400193 fShifts[cluster->endPos() - 1] += space;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400194 // Increment the run width
195 fSpaced = true;
196 fAdvance.fX += space;
197 // Increment the cluster width
198 cluster->space(space, space);
199
200 return space;
201}
202
203SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400204 // Offset all the glyphs in the cluster
205 SkScalar shift = 0;
206 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400207 fShifts[i] += shift;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400208 shift += space;
209 }
210 if (this->size() == cluster->endPos()) {
211 // To make calculations easier
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400212 fShifts[cluster->endPos()] += shift;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400213 }
214 // Increment the run width
215 fSpaced = true;
216 fAdvance.fX += shift;
217 // Increment the cluster width
218 cluster->space(shift, space);
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400219 cluster->setHalfLetterSpacing(space / 2);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400220
221 return shift;
222}
223
224void Run::shift(const Cluster* cluster, SkScalar offset) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400225 if (offset == 0) {
226 return;
227 }
228
229 fSpaced = true;
230 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400231 fShifts[i] += offset;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400232 }
233 if (this->size() == cluster->endPos()) {
234 // To make calculations easier
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400235 fShifts[cluster->endPos()] += offset;
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400236 }
237}
238
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400239void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
Julia Lavrova916a9042019-08-08 16:51:27 -0400240
Julia Lavrovac0360582020-02-05 10:17:53 -0500241 SkASSERT(isPlaceholder());
242 auto placeholderStyle = this->placeholderStyle();
Julia Lavrova916a9042019-08-08 16:51:27 -0400243 // Difference between the placeholder baseline and the line bottom
244 SkScalar baselineAdjustment = 0;
Julia Lavrovac0360582020-02-05 10:17:53 -0500245 switch (placeholderStyle->fBaseline) {
Julia Lavrova916a9042019-08-08 16:51:27 -0400246 case TextBaseline::kAlphabetic:
247 break;
248
249 case TextBaseline::kIdeographic:
250 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
251 break;
252 }
253
Julia Lavrovac0360582020-02-05 10:17:53 -0500254 auto height = placeholderStyle->fHeight;
255 auto offset = placeholderStyle->fBaselineOffset;
Julia Lavrova916a9042019-08-08 16:51:27 -0400256
257 fFontMetrics.fLeading = 0;
Julia Lavrovac0360582020-02-05 10:17:53 -0500258 switch (placeholderStyle->fAlignment) {
Julia Lavrova916a9042019-08-08 16:51:27 -0400259 case PlaceholderAlignment::kBaseline:
260 fFontMetrics.fAscent = baselineAdjustment - offset;
261 fFontMetrics.fDescent = baselineAdjustment + height - offset;
262 break;
263
264 case PlaceholderAlignment::kAboveBaseline:
265 fFontMetrics.fAscent = baselineAdjustment - height;
266 fFontMetrics.fDescent = baselineAdjustment;
267 break;
268
269 case PlaceholderAlignment::kBelowBaseline:
270 fFontMetrics.fAscent = baselineAdjustment;
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400271 fFontMetrics.fDescent = baselineAdjustment + height;
Julia Lavrova916a9042019-08-08 16:51:27 -0400272 break;
273
274 case PlaceholderAlignment::kTop:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400275 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
Julia Lavrova916a9042019-08-08 16:51:27 -0400276 break;
277
278 case PlaceholderAlignment::kBottom:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400279 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
Julia Lavrova916a9042019-08-08 16:51:27 -0400280 break;
281
282 case PlaceholderAlignment::kMiddle:
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400283 auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
284 fFontMetrics.fDescent = height/2.0 - mid;
285 fFontMetrics.fAscent = - height/2.0 - mid;
Julia Lavrova916a9042019-08-08 16:51:27 -0400286 break;
287 }
288
289 // Make sure the placeholder can fit the line
290 endlineMetrics->add(this);
291}
292
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400293void Cluster::setIsWhiteSpaces() {
Julia Lavrovadb9f6692019-08-01 16:02:17 -0400294
295 fWhiteSpaces = false;
296
297 auto span = fMaster->text(fTextRange);
298 const char* ch = span.begin();
299 while (ch < span.end()) {
300 auto unichar = utf8_next(&ch, span.end());
301 if (!u_isWhitespace(unichar)) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400302 return;
303 }
304 }
305 fWhiteSpaces = true;
306}
307
Julia Lavrova5207f352019-06-21 12:22:32 -0400308SkScalar Cluster::sizeToChar(TextIndex ch) const {
309 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400310 return 0;
311 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400312 auto shift = ch - fTextRange.start;
313 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400314
315 return SkDoubleToScalar(fWidth * ratio);
316}
317
Julia Lavrova5207f352019-06-21 12:22:32 -0400318SkScalar Cluster::sizeFromChar(TextIndex ch) const {
319 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400320 return 0;
321 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400322 auto shift = fTextRange.end - ch - 1;
323 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400324
325 return SkDoubleToScalar(fWidth * ratio);
326}
327
328size_t Cluster::roundPos(SkScalar s) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400329 auto ratio = (s * 1.0) / fWidth;
330 return sk_double_floor2int(ratio * size());
331}
332
333SkScalar Cluster::trimmedWidth(size_t pos) const {
334 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
Julia Lavrova5207f352019-06-21 12:22:32 -0400335 // We don't have to take in account cluster shift since it's the same for 0 and for pos
336 auto& run = fMaster->run(fRunIndex);
Julia Lavrova212bf072020-02-18 12:05:55 -0500337 return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
Julia Lavrova5207f352019-06-21 12:22:32 -0400338}
339
340SkScalar Run::positionX(size_t pos) const {
Julia Lavrova2e30fde2019-10-09 09:43:02 -0400341 return posX(pos) + fShifts[pos] + fMaster->posShift(fIndex, pos);
Julia Lavrova5207f352019-06-21 12:22:32 -0400342}
343
Julia Lavrovac0360582020-02-05 10:17:53 -0500344PlaceholderStyle* Run::placeholderStyle() const {
345 if (isPlaceholder()) {
346 return &fMaster->placeholders()[fPlaceholderIndex].fStyle;
347 } else {
348 return nullptr;
349 }
350}
351
Julia Lavrova5207f352019-06-21 12:22:32 -0400352Run* Cluster::run() const {
353 if (fRunIndex >= fMaster->runs().size()) {
354 return nullptr;
355 }
356 return &fMaster->run(fRunIndex);
357}
358
359SkFont Cluster::font() const {
360 return fMaster->run(fRunIndex).font();
361}
362
363Cluster::Cluster(ParagraphImpl* master,
364 RunIndex runIndex,
365 size_t start,
366 size_t end,
367 SkSpan<const char> text,
368 SkScalar width,
369 SkScalar height)
370 : fMaster(master)
371 , fRunIndex(runIndex)
372 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
Julia Lavrovac2228562019-08-08 16:51:27 -0400373 , fGraphemeRange(EMPTY_RANGE)
Julia Lavrova5207f352019-06-21 12:22:32 -0400374 , fStart(start)
375 , fEnd(end)
376 , fWidth(width)
377 , fSpacing(0)
378 , fHeight(height)
Julia Lavrovaf3ed2732019-09-05 14:35:17 -0400379 , fHalfLetterSpacing(0.0)
Julia Lavrova5207f352019-06-21 12:22:32 -0400380 , fWhiteSpaces(false)
381 , fBreakType(None) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400382}
383
384} // namespace textlayout
385} // namespace skia