blob: 34ec8ddc0a1ff16048b15bc56c31fc6723772f04 [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 Lavrova526df262019-08-21 17:49:44 -040084std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
Julia Lavrova5207f352019-06-21 12:22:32 -040085 auto less = [](const Cluster& c1, const Cluster& c2) {
86 return c1.textRange().end <= c2.textRange().start;
87 };
88
89 if (text.width() == 0) {
90
91 auto found = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
92 fMaster->clusters().begin() + fClusterRange.end,
93 Cluster(text),
94 less);
95 return std::make_tuple(found != nullptr,
96 found - fMaster->clusters().begin(),
97 found - fMaster->clusters().begin());
Julia Lavrovaa3552c52019-05-30 16:12:56 -040098 }
99
Julia Lavrova5207f352019-06-21 12:22:32 -0400100 auto start = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
101 fMaster->clusters().begin() + fClusterRange.end,
102 Cluster(TextRange(text.start, text.start + 1)),
103 less);
104 auto end = std::lower_bound(start,
105 fMaster->clusters().begin() + fClusterRange.end,
106 Cluster(TextRange(text.end - 1, text.end)),
107 less);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400108 if (!leftToRight()) {
109 std::swap(start, end);
110 }
111
Julia Lavrova5207f352019-06-21 12:22:32 -0400112 size_t startIndex = start - fMaster->clusters().begin();
113 size_t endIndex = end - fMaster->clusters().begin();
114 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400115}
116
117void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400118 // Can't figure out how to do it with one code for both cases without 100 ifs
119 // Can't go through clusters because there are no cluster table yet
120 if (leftToRight()) {
121 size_t start = 0;
122 size_t cluster = this->clusterIndex(start);
123 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
124 auto nextCluster = this->clusterIndex(glyph);
125 if (nextCluster == cluster) {
126 continue;
127 }
128
129 visitor(start,
130 glyph,
Julia Lavrova916a9042019-08-08 16:51:27 -0400131 fFirstChar + cluster,
132 fFirstChar + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400133 this->calculateWidth(start, glyph, glyph == size()),
134 this->calculateHeight());
135
136 start = glyph;
137 cluster = nextCluster;
138 }
139 } else {
140 size_t glyph = this->size();
141 size_t cluster = this->fUtf8Range.begin();
142 for (int32_t start = this->size() - 1; start >= 0; --start) {
143 size_t nextCluster =
144 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
145 if (nextCluster == cluster) {
146 continue;
147 }
148
149 visitor(start,
150 glyph,
Julia Lavrova916a9042019-08-08 16:51:27 -0400151 fFirstChar + cluster,
152 fFirstChar + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400153 this->calculateWidth(start, glyph, glyph == 0),
154 this->calculateHeight());
155
156 glyph = start;
157 cluster = nextCluster;
158 }
159 }
160}
161
162SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400163 if (cluster->endPos() == cluster->startPos()) {
164 return 0;
165 }
166
167 fOffsets[cluster->endPos() - 1] += space;
168 // Increment the run width
169 fSpaced = true;
170 fAdvance.fX += space;
171 // Increment the cluster width
172 cluster->space(space, space);
173
174 return space;
175}
176
177SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400178 // Offset all the glyphs in the cluster
179 SkScalar shift = 0;
180 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
181 fOffsets[i] += shift;
182 shift += space;
183 }
184 if (this->size() == cluster->endPos()) {
185 // To make calculations easier
186 fOffsets[cluster->endPos()] += shift;
187 }
188 // Increment the run width
189 fSpaced = true;
190 fAdvance.fX += shift;
191 // Increment the cluster width
192 cluster->space(shift, space);
193
194 return shift;
195}
196
197void Run::shift(const Cluster* cluster, SkScalar offset) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400198 if (offset == 0) {
199 return;
200 }
201
202 fSpaced = true;
203 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
204 fOffsets[i] += offset;
205 }
206 if (this->size() == cluster->endPos()) {
207 // To make calculations easier
208 fOffsets[cluster->endPos()] += offset;
209 }
210}
211
Julia Lavrova916a9042019-08-08 16:51:27 -0400212void Run::updateMetrics(LineMetrics* endlineMetrics) {
213
214 // Difference between the placeholder baseline and the line bottom
215 SkScalar baselineAdjustment = 0;
216 switch (fPlaceholder->fBaseline) {
217 case TextBaseline::kAlphabetic:
218 break;
219
220 case TextBaseline::kIdeographic:
221 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
222 break;
223 }
224
225 auto height = fPlaceholder->fHeight;
226 auto offset = fPlaceholder->fBaselineOffset;
227
228 fFontMetrics.fLeading = 0;
229 switch (fPlaceholder->fAlignment) {
230 case PlaceholderAlignment::kBaseline:
231 fFontMetrics.fAscent = baselineAdjustment - offset;
232 fFontMetrics.fDescent = baselineAdjustment + height - offset;
233 break;
234
235 case PlaceholderAlignment::kAboveBaseline:
236 fFontMetrics.fAscent = baselineAdjustment - height;
237 fFontMetrics.fDescent = baselineAdjustment;
238 break;
239
240 case PlaceholderAlignment::kBelowBaseline:
241 fFontMetrics.fAscent = baselineAdjustment;
242 fFontMetrics.fDescent = baselineAdjustment +height;
243 break;
244
245 case PlaceholderAlignment::kTop:
246 fFontMetrics.fAscent = - endlineMetrics->alphabeticBaseline();
247 fFontMetrics.fDescent = height - endlineMetrics->alphabeticBaseline();
248 break;
249
250 case PlaceholderAlignment::kBottom:
251 fFontMetrics.fDescent = endlineMetrics->deltaBaselines();
252 fFontMetrics.fAscent = - height + endlineMetrics->deltaBaselines();
253 break;
254
255 case PlaceholderAlignment::kMiddle:
256 double mid = (endlineMetrics->ascent() + endlineMetrics->descent())/ 2;
257 fFontMetrics.fDescent = mid + height/2;
258 fFontMetrics.fAscent = mid - height/2;
259 break;
260 }
261
262 // Make sure the placeholder can fit the line
263 endlineMetrics->add(this);
264}
265
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400266void Cluster::setIsWhiteSpaces() {
Julia Lavrovadb9f6692019-08-01 16:02:17 -0400267
268 fWhiteSpaces = false;
269
270 auto span = fMaster->text(fTextRange);
271 const char* ch = span.begin();
272 while (ch < span.end()) {
273 auto unichar = utf8_next(&ch, span.end());
274 if (!u_isWhitespace(unichar)) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400275 return;
276 }
277 }
278 fWhiteSpaces = true;
279}
280
Julia Lavrova5207f352019-06-21 12:22:32 -0400281SkScalar Cluster::sizeToChar(TextIndex ch) const {
282 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400283 return 0;
284 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400285 auto shift = ch - fTextRange.start;
286 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400287
288 return SkDoubleToScalar(fWidth * ratio);
289}
290
Julia Lavrova5207f352019-06-21 12:22:32 -0400291SkScalar Cluster::sizeFromChar(TextIndex ch) const {
292 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400293 return 0;
294 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400295 auto shift = fTextRange.end - ch - 1;
296 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400297
298 return SkDoubleToScalar(fWidth * ratio);
299}
300
301size_t Cluster::roundPos(SkScalar s) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400302 auto ratio = (s * 1.0) / fWidth;
303 return sk_double_floor2int(ratio * size());
304}
305
306SkScalar Cluster::trimmedWidth(size_t pos) const {
307 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
Julia Lavrova5207f352019-06-21 12:22:32 -0400308 // We don't have to take in account cluster shift since it's the same for 0 and for pos
309 auto& run = fMaster->run(fRunIndex);
310 return SkTMin(run.positionX(pos) - run.positionX(fStart), fWidth - fSpacing);
311}
312
313SkScalar Run::positionX(size_t pos) const {
314 return fPositions[pos].fX + fOffsets[pos] + fMaster->posShift(fIndex, pos);
315}
316
317Run* Cluster::run() const {
318 if (fRunIndex >= fMaster->runs().size()) {
319 return nullptr;
320 }
321 return &fMaster->run(fRunIndex);
322}
323
324SkFont Cluster::font() const {
325 return fMaster->run(fRunIndex).font();
326}
327
328Cluster::Cluster(ParagraphImpl* master,
329 RunIndex runIndex,
330 size_t start,
331 size_t end,
332 SkSpan<const char> text,
333 SkScalar width,
334 SkScalar height)
335 : fMaster(master)
336 , fRunIndex(runIndex)
337 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
Julia Lavrovac2228562019-08-08 16:51:27 -0400338 , fGraphemeRange(EMPTY_RANGE)
Julia Lavrova5207f352019-06-21 12:22:32 -0400339 , fStart(start)
340 , fEnd(end)
341 , fWidth(width)
342 , fSpacing(0)
343 , fHeight(height)
344 , fWhiteSpaces(false)
345 , fBreakType(None) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400346}
347
348} // namespace textlayout
349} // namespace skia