blob: 2f7edc9f55c649507ff3f93b78ed648729db6aa3 [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();
46}
47
48SkShaper::RunHandler::Buffer Run::newRunBuffer() {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040049 return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
50}
51
52SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040053 SkASSERT(start <= end);
54 // clip |= end == size(); // Clip at the end of the run?
55 SkScalar offset = 0;
56 if (fSpaced && end > start) {
57 offset = fOffsets[clip ? end - 1 : end] - fOffsets[start];
58 }
Julia Lavrova5207f352019-06-21 12:22:32 -040059 auto correction = end > start ? fMaster->posShift(fIndex, end - 1) - fMaster->posShift(fIndex, start) : 0;
60 return fPositions[end].fX - fPositions[start].fX + offset + correction;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040061}
62
63void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040064 SkASSERT(pos + size <= this->size());
65 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
66 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
67
68 if (fSpaced || offset.fX != 0 || offset.fY != 0) {
69 for (size_t i = 0; i < size; ++i) {
70 auto point = fPositions[i + pos];
71 if (fSpaced) {
72 point.fX += fOffsets[i + pos];
73 }
Julia Lavrova5207f352019-06-21 12:22:32 -040074 point.fX += fMaster->posShift(fIndex, i + pos);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040075 blobBuffer.points()[i] = point + offset;
76 }
77 } else {
78 // Good for the first line
79 sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
80 }
81}
82
Julia Lavrova5207f352019-06-21 12:22:32 -040083std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) {
84 auto less = [](const Cluster& c1, const Cluster& c2) {
85 return c1.textRange().end <= c2.textRange().start;
86 };
87
88 if (text.width() == 0) {
89
90 auto found = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
91 fMaster->clusters().begin() + fClusterRange.end,
92 Cluster(text),
93 less);
94 return std::make_tuple(found != nullptr,
95 found - fMaster->clusters().begin(),
96 found - fMaster->clusters().begin());
Julia Lavrovaa3552c52019-05-30 16:12:56 -040097 }
98
Julia Lavrova5207f352019-06-21 12:22:32 -040099 auto start = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
100 fMaster->clusters().begin() + fClusterRange.end,
101 Cluster(TextRange(text.start, text.start + 1)),
102 less);
103 auto end = std::lower_bound(start,
104 fMaster->clusters().begin() + fClusterRange.end,
105 Cluster(TextRange(text.end - 1, text.end)),
106 less);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400107 if (!leftToRight()) {
108 std::swap(start, end);
109 }
110
Julia Lavrova5207f352019-06-21 12:22:32 -0400111 size_t startIndex = start - fMaster->clusters().begin();
112 size_t endIndex = end - fMaster->clusters().begin();
113 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400114}
115
116void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400117 // Can't figure out how to do it with one code for both cases without 100 ifs
118 // Can't go through clusters because there are no cluster table yet
119 if (leftToRight()) {
120 size_t start = 0;
121 size_t cluster = this->clusterIndex(start);
122 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
123 auto nextCluster = this->clusterIndex(glyph);
124 if (nextCluster == cluster) {
125 continue;
126 }
127
128 visitor(start,
129 glyph,
Julia Lavrova916a9042019-08-08 16:51:27 -0400130 fFirstChar + cluster,
131 fFirstChar + nextCluster,
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400132 this->calculateWidth(start, glyph, glyph == size()),
133 this->calculateHeight());
134
135 start = glyph;
136 cluster = nextCluster;
137 }
138 } else {
139 size_t glyph = this->size();
140 size_t cluster = this->fUtf8Range.begin();
141 for (int32_t start = this->size() - 1; start >= 0; --start) {
142 size_t nextCluster =
143 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
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 == 0),
153 this->calculateHeight());
154
155 glyph = start;
156 cluster = nextCluster;
157 }
158 }
159}
160
161SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400162 if (cluster->endPos() == cluster->startPos()) {
163 return 0;
164 }
165
166 fOffsets[cluster->endPos() - 1] += space;
167 // Increment the run width
168 fSpaced = true;
169 fAdvance.fX += space;
170 // Increment the cluster width
171 cluster->space(space, space);
172
173 return space;
174}
175
176SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400177 // Offset all the glyphs in the cluster
178 SkScalar shift = 0;
179 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
180 fOffsets[i] += shift;
181 shift += space;
182 }
183 if (this->size() == cluster->endPos()) {
184 // To make calculations easier
185 fOffsets[cluster->endPos()] += shift;
186 }
187 // Increment the run width
188 fSpaced = true;
189 fAdvance.fX += shift;
190 // Increment the cluster width
191 cluster->space(shift, space);
192
193 return shift;
194}
195
196void Run::shift(const Cluster* cluster, SkScalar offset) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400197 if (offset == 0) {
198 return;
199 }
200
201 fSpaced = true;
202 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
203 fOffsets[i] += offset;
204 }
205 if (this->size() == cluster->endPos()) {
206 // To make calculations easier
207 fOffsets[cluster->endPos()] += offset;
208 }
209}
210
Julia Lavrova916a9042019-08-08 16:51:27 -0400211void Run::updateMetrics(LineMetrics* endlineMetrics) {
212
213 // Difference between the placeholder baseline and the line bottom
214 SkScalar baselineAdjustment = 0;
215 switch (fPlaceholder->fBaseline) {
216 case TextBaseline::kAlphabetic:
217 break;
218
219 case TextBaseline::kIdeographic:
220 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
221 break;
222 }
223
224 auto height = fPlaceholder->fHeight;
225 auto offset = fPlaceholder->fBaselineOffset;
226
227 fFontMetrics.fLeading = 0;
228 switch (fPlaceholder->fAlignment) {
229 case PlaceholderAlignment::kBaseline:
230 fFontMetrics.fAscent = baselineAdjustment - offset;
231 fFontMetrics.fDescent = baselineAdjustment + height - offset;
232 break;
233
234 case PlaceholderAlignment::kAboveBaseline:
235 fFontMetrics.fAscent = baselineAdjustment - height;
236 fFontMetrics.fDescent = baselineAdjustment;
237 break;
238
239 case PlaceholderAlignment::kBelowBaseline:
240 fFontMetrics.fAscent = baselineAdjustment;
241 fFontMetrics.fDescent = baselineAdjustment +height;
242 break;
243
244 case PlaceholderAlignment::kTop:
245 fFontMetrics.fAscent = - endlineMetrics->alphabeticBaseline();
246 fFontMetrics.fDescent = height - endlineMetrics->alphabeticBaseline();
247 break;
248
249 case PlaceholderAlignment::kBottom:
250 fFontMetrics.fDescent = endlineMetrics->deltaBaselines();
251 fFontMetrics.fAscent = - height + endlineMetrics->deltaBaselines();
252 break;
253
254 case PlaceholderAlignment::kMiddle:
255 double mid = (endlineMetrics->ascent() + endlineMetrics->descent())/ 2;
256 fFontMetrics.fDescent = mid + height/2;
257 fFontMetrics.fAscent = mid - height/2;
258 break;
259 }
260
261 // Make sure the placeholder can fit the line
262 endlineMetrics->add(this);
263}
264
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400265void Cluster::setIsWhiteSpaces() {
Julia Lavrovadb9f6692019-08-01 16:02:17 -0400266
267 fWhiteSpaces = false;
268
269 auto span = fMaster->text(fTextRange);
270 const char* ch = span.begin();
271 while (ch < span.end()) {
272 auto unichar = utf8_next(&ch, span.end());
273 if (!u_isWhitespace(unichar)) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400274 return;
275 }
276 }
277 fWhiteSpaces = true;
278}
279
Julia Lavrova5207f352019-06-21 12:22:32 -0400280SkScalar Cluster::sizeToChar(TextIndex ch) const {
281 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400282 return 0;
283 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400284 auto shift = ch - fTextRange.start;
285 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400286
287 return SkDoubleToScalar(fWidth * ratio);
288}
289
Julia Lavrova5207f352019-06-21 12:22:32 -0400290SkScalar Cluster::sizeFromChar(TextIndex ch) const {
291 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400292 return 0;
293 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400294 auto shift = fTextRange.end - ch - 1;
295 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400296
297 return SkDoubleToScalar(fWidth * ratio);
298}
299
300size_t Cluster::roundPos(SkScalar s) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400301 auto ratio = (s * 1.0) / fWidth;
302 return sk_double_floor2int(ratio * size());
303}
304
305SkScalar Cluster::trimmedWidth(size_t pos) const {
306 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
Julia Lavrova5207f352019-06-21 12:22:32 -0400307 // We don't have to take in account cluster shift since it's the same for 0 and for pos
308 auto& run = fMaster->run(fRunIndex);
309 return SkTMin(run.positionX(pos) - run.positionX(fStart), fWidth - fSpacing);
310}
311
312SkScalar Run::positionX(size_t pos) const {
313 return fPositions[pos].fX + fOffsets[pos] + fMaster->posShift(fIndex, pos);
314}
315
316Run* Cluster::run() const {
317 if (fRunIndex >= fMaster->runs().size()) {
318 return nullptr;
319 }
320 return &fMaster->run(fRunIndex);
321}
322
323SkFont Cluster::font() const {
324 return fMaster->run(fRunIndex).font();
325}
326
327Cluster::Cluster(ParagraphImpl* master,
328 RunIndex runIndex,
329 size_t start,
330 size_t end,
331 SkSpan<const char> text,
332 SkScalar width,
333 SkScalar height)
334 : fMaster(master)
335 , fRunIndex(runIndex)
336 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
Julia Lavrovac2228562019-08-08 16:51:27 -0400337 , fGraphemeRange(EMPTY_RANGE)
Julia Lavrova5207f352019-06-21 12:22:32 -0400338 , fStart(start)
339 , fEnd(end)
340 , fWidth(width)
341 , fSpacing(0)
342 , fHeight(height)
343 , fWhiteSpaces(false)
344 , fBreakType(None) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400345}
346
347} // namespace textlayout
348} // namespace skia