blob: ad93fb11a97a4623e506c3e73059c99852f393fc [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,
22 SkScalar lineHeight,
23 size_t index,
Julia Lavrova5207f352019-06-21 12:22:32 -040024 SkScalar offsetX)
25 : fMaster(master)
26 , fTextRange(info.utf8Range.begin(), info.utf8Range.end())
27 , fClusterRange(EMPTY_CLUSTERS) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040028 fFont = info.fFont;
29 fHeightMultiplier = lineHeight;
30 fBidiLevel = info.fBidiLevel;
31 fAdvance = info.fAdvance;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040032 fIndex = index;
33 fUtf8Range = info.utf8Range;
34 fOffset = SkVector::Make(offsetX, 0);
35 fGlyphs.push_back_n(info.glyphCount);
36 fPositions.push_back_n(info.glyphCount + 1);
Julia Lavrova5207f352019-06-21 12:22:32 -040037 fOffsets.push_back_n(info.glyphCount + 1, 0.0);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040038 fClusterIndexes.push_back_n(info.glyphCount + 1);
39 info.fFont.getMetrics(&fFontMetrics);
40 fSpaced = false;
41 // To make edge cases easier:
42 fPositions[info.glyphCount] = fOffset + fAdvance;
43 fClusterIndexes[info.glyphCount] = info.utf8Range.end();
44}
45
46SkShaper::RunHandler::Buffer Run::newRunBuffer() {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040047 return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
48}
49
50SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040051 SkASSERT(start <= end);
52 // clip |= end == size(); // Clip at the end of the run?
53 SkScalar offset = 0;
54 if (fSpaced && end > start) {
55 offset = fOffsets[clip ? end - 1 : end] - fOffsets[start];
56 }
Julia Lavrova5207f352019-06-21 12:22:32 -040057 auto correction = end > start ? fMaster->posShift(fIndex, end - 1) - fMaster->posShift(fIndex, start) : 0;
58 return fPositions[end].fX - fPositions[start].fX + offset + correction;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040059}
60
61void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040062 SkASSERT(pos + size <= this->size());
63 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
64 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
65
66 if (fSpaced || offset.fX != 0 || offset.fY != 0) {
67 for (size_t i = 0; i < size; ++i) {
68 auto point = fPositions[i + pos];
69 if (fSpaced) {
70 point.fX += fOffsets[i + pos];
71 }
Julia Lavrova5207f352019-06-21 12:22:32 -040072 point.fX += fMaster->posShift(fIndex, i + pos);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040073 blobBuffer.points()[i] = point + offset;
74 }
75 } else {
76 // Good for the first line
77 sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
78 }
79}
80
Julia Lavrova5207f352019-06-21 12:22:32 -040081std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) {
82 auto less = [](const Cluster& c1, const Cluster& c2) {
83 return c1.textRange().end <= c2.textRange().start;
84 };
85
86 if (text.width() == 0) {
87
88 auto found = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
89 fMaster->clusters().begin() + fClusterRange.end,
90 Cluster(text),
91 less);
92 return std::make_tuple(found != nullptr,
93 found - fMaster->clusters().begin(),
94 found - fMaster->clusters().begin());
Julia Lavrovaa3552c52019-05-30 16:12:56 -040095 }
96
Julia Lavrova5207f352019-06-21 12:22:32 -040097 auto start = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
98 fMaster->clusters().begin() + fClusterRange.end,
99 Cluster(TextRange(text.start, text.start + 1)),
100 less);
101 auto end = std::lower_bound(start,
102 fMaster->clusters().begin() + fClusterRange.end,
103 Cluster(TextRange(text.end - 1, text.end)),
104 less);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400105 if (!leftToRight()) {
106 std::swap(start, end);
107 }
108
Julia Lavrova5207f352019-06-21 12:22:32 -0400109 size_t startIndex = start - fMaster->clusters().begin();
110 size_t endIndex = end - fMaster->clusters().begin();
111 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400112}
113
114void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400115 // Can't figure out how to do it with one code for both cases without 100 ifs
116 // Can't go through clusters because there are no cluster table yet
117 if (leftToRight()) {
118 size_t start = 0;
119 size_t cluster = this->clusterIndex(start);
120 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
121 auto nextCluster = this->clusterIndex(glyph);
122 if (nextCluster == cluster) {
123 continue;
124 }
125
126 visitor(start,
127 glyph,
128 cluster,
129 nextCluster,
130 this->calculateWidth(start, glyph, glyph == size()),
131 this->calculateHeight());
132
133 start = glyph;
134 cluster = nextCluster;
135 }
136 } else {
137 size_t glyph = this->size();
138 size_t cluster = this->fUtf8Range.begin();
139 for (int32_t start = this->size() - 1; start >= 0; --start) {
140 size_t nextCluster =
141 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
142 if (nextCluster == cluster) {
143 continue;
144 }
145
146 visitor(start,
147 glyph,
148 cluster,
149 nextCluster,
150 this->calculateWidth(start, glyph, glyph == 0),
151 this->calculateHeight());
152
153 glyph = start;
154 cluster = nextCluster;
155 }
156 }
157}
158
159SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400160 if (cluster->endPos() == cluster->startPos()) {
161 return 0;
162 }
163
164 fOffsets[cluster->endPos() - 1] += space;
165 // Increment the run width
166 fSpaced = true;
167 fAdvance.fX += space;
168 // Increment the cluster width
169 cluster->space(space, space);
170
171 return space;
172}
173
174SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400175 // Offset all the glyphs in the cluster
176 SkScalar shift = 0;
177 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
178 fOffsets[i] += shift;
179 shift += space;
180 }
181 if (this->size() == cluster->endPos()) {
182 // To make calculations easier
183 fOffsets[cluster->endPos()] += shift;
184 }
185 // Increment the run width
186 fSpaced = true;
187 fAdvance.fX += shift;
188 // Increment the cluster width
189 cluster->space(shift, space);
190
191 return shift;
192}
193
194void Run::shift(const Cluster* cluster, SkScalar offset) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400195 if (offset == 0) {
196 return;
197 }
198
199 fSpaced = true;
200 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
201 fOffsets[i] += offset;
202 }
203 if (this->size() == cluster->endPos()) {
204 // To make calculations easier
205 fOffsets[cluster->endPos()] += offset;
206 }
207}
208
209void Cluster::setIsWhiteSpaces() {
Julia Lavrovadb9f6692019-08-01 16:02:17 -0400210
211 fWhiteSpaces = false;
212
213 auto span = fMaster->text(fTextRange);
214 const char* ch = span.begin();
215 while (ch < span.end()) {
216 auto unichar = utf8_next(&ch, span.end());
217 if (!u_isWhitespace(unichar)) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400218 return;
219 }
220 }
221 fWhiteSpaces = true;
222}
223
Julia Lavrova5207f352019-06-21 12:22:32 -0400224SkScalar Cluster::sizeToChar(TextIndex ch) const {
225 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400226 return 0;
227 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400228 auto shift = ch - fTextRange.start;
229 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400230
231 return SkDoubleToScalar(fWidth * ratio);
232}
233
Julia Lavrova5207f352019-06-21 12:22:32 -0400234SkScalar Cluster::sizeFromChar(TextIndex ch) const {
235 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400236 return 0;
237 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400238 auto shift = fTextRange.end - ch - 1;
239 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400240
241 return SkDoubleToScalar(fWidth * ratio);
242}
243
244size_t Cluster::roundPos(SkScalar s) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400245 auto ratio = (s * 1.0) / fWidth;
246 return sk_double_floor2int(ratio * size());
247}
248
249SkScalar Cluster::trimmedWidth(size_t pos) const {
250 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
Julia Lavrova5207f352019-06-21 12:22:32 -0400251 // We don't have to take in account cluster shift since it's the same for 0 and for pos
252 auto& run = fMaster->run(fRunIndex);
253 return SkTMin(run.positionX(pos) - run.positionX(fStart), fWidth - fSpacing);
254}
255
256SkScalar Run::positionX(size_t pos) const {
257 return fPositions[pos].fX + fOffsets[pos] + fMaster->posShift(fIndex, pos);
258}
259
260Run* Cluster::run() const {
261 if (fRunIndex >= fMaster->runs().size()) {
262 return nullptr;
263 }
264 return &fMaster->run(fRunIndex);
265}
266
267SkFont Cluster::font() const {
268 return fMaster->run(fRunIndex).font();
269}
270
271Cluster::Cluster(ParagraphImpl* master,
272 RunIndex runIndex,
273 size_t start,
274 size_t end,
275 SkSpan<const char> text,
276 SkScalar width,
277 SkScalar height)
278 : fMaster(master)
279 , fRunIndex(runIndex)
280 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
Julia Lavrovac2228562019-08-08 16:51:27 -0400281 , fGraphemeRange(EMPTY_RANGE)
Julia Lavrova5207f352019-06-21 12:22:32 -0400282 , fStart(start)
283 , fEnd(end)
284 , fWidth(width)
285 , fSpacing(0)
286 , fHeight(height)
287 , fWhiteSpaces(false)
288 , fBreakType(None) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400289}
290
291} // namespace textlayout
292} // namespace skia