1 // Copyright 2019 Google LLC.
2 #include "modules/skparagraph/src/Run.h"
3 #include <unicode/brkiter.h>
4 #include "include/core/SkFontMetrics.h"
5 #include "modules/skparagraph/src/ParagraphImpl.h"
6 #include <algorithm>
7 #include "src/utils/SkUTF.h"
8
9 namespace {
10
utf8_next(const char ** ptr,const char * end)11 SkUnichar utf8_next(const char** ptr, const char* end) {
12 SkUnichar val = SkUTF::NextUTF8(ptr, end);
13 return val < 0 ? 0xFFFD : val;
14 }
15 }
16
17 namespace skia {
18 namespace textlayout {
19
Run(ParagraphImpl * master,const SkShaper::RunHandler::RunInfo & info,SkScalar lineHeight,size_t index,SkScalar offsetX)20 Run::Run(ParagraphImpl* master,
21 const SkShaper::RunHandler::RunInfo& info,
22 SkScalar lineHeight,
23 size_t index,
24 SkScalar offsetX)
25 : fMaster(master)
26 , fTextRange(info.utf8Range.begin(), info.utf8Range.end())
27 , fClusterRange(EMPTY_CLUSTERS) {
28 fFont = info.fFont;
29 fHeightMultiplier = lineHeight;
30 fBidiLevel = info.fBidiLevel;
31 fAdvance = info.fAdvance;
32 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);
37 fOffsets.push_back_n(info.glyphCount + 1, 0.0);
38 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
newRunBuffer()46 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
47 return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
48 }
49
calculateWidth(size_t start,size_t end,bool clip) const50 SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
51 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 }
57 auto correction = end > start ? fMaster->posShift(fIndex, end - 1) - fMaster->posShift(fIndex, start) : 0;
58 return fPositions[end].fX - fPositions[start].fX + offset + correction;
59 }
60
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size,SkVector offset) const61 void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
62 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 }
72 point.fX += fMaster->posShift(fIndex, i + pos);
73 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
findLimitingClusters(TextRange text)81 std::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());
95 }
96
97 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);
105 if (!leftToRight()) {
106 std::swap(start, end);
107 }
108
109 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);
112 }
113
iterateThroughClustersInTextOrder(const ClusterVisitor & visitor)114 void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
115 // 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
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)159 SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
160 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
addSpacesEvenly(SkScalar space,Cluster * cluster)174 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
175 // 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
shift(const Cluster * cluster,SkScalar offset)194 void Run::shift(const Cluster* cluster, SkScalar offset) {
195 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
setIsWhiteSpaces()209 void Cluster::setIsWhiteSpaces() {
210
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)) {
218 return;
219 }
220 }
221 fWhiteSpaces = true;
222 }
223
sizeToChar(TextIndex ch) const224 SkScalar Cluster::sizeToChar(TextIndex ch) const {
225 if (ch < fTextRange.start || ch >= fTextRange.end) {
226 return 0;
227 }
228 auto shift = ch - fTextRange.start;
229 auto ratio = shift * 1.0 / fTextRange.width();
230
231 return SkDoubleToScalar(fWidth * ratio);
232 }
233
sizeFromChar(TextIndex ch) const234 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
235 if (ch < fTextRange.start || ch >= fTextRange.end) {
236 return 0;
237 }
238 auto shift = fTextRange.end - ch - 1;
239 auto ratio = shift * 1.0 / fTextRange.width();
240
241 return SkDoubleToScalar(fWidth * ratio);
242 }
243
roundPos(SkScalar s) const244 size_t Cluster::roundPos(SkScalar s) const {
245 auto ratio = (s * 1.0) / fWidth;
246 return sk_double_floor2int(ratio * size());
247 }
248
trimmedWidth(size_t pos) const249 SkScalar Cluster::trimmedWidth(size_t pos) const {
250 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
251 // 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
positionX(size_t pos) const256 SkScalar Run::positionX(size_t pos) const {
257 return fPositions[pos].fX + fOffsets[pos] + fMaster->posShift(fIndex, pos);
258 }
259
run() const260 Run* Cluster::run() const {
261 if (fRunIndex >= fMaster->runs().size()) {
262 return nullptr;
263 }
264 return &fMaster->run(fRunIndex);
265 }
266
font() const267 SkFont Cluster::font() const {
268 return fMaster->run(fRunIndex).font();
269 }
270
Cluster(ParagraphImpl * master,RunIndex runIndex,size_t start,size_t end,SkSpan<const char> text,SkScalar width,SkScalar height)271 Cluster::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())
281 , fGraphemeRange(EMPTY_RANGE)
282 , fStart(start)
283 , fEnd(end)
284 , fWidth(width)
285 , fSpacing(0)
286 , fHeight(height)
287 , fWhiteSpaces(false)
288 , fBreakType(None) {
289 }
290
291 } // namespace textlayout
292 } // namespace skia
293