blob: 9517451011fe83ea3876c8d66748ddc0035dde06 [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 Lavrovaa3552c52019-05-30 16:12:56 -04007
8namespace skia {
9namespace textlayout {
10
Julia Lavrova5207f352019-06-21 12:22:32 -040011Run::Run(ParagraphImpl* master,
Julia Lavrovaa3552c52019-05-30 16:12:56 -040012 const SkShaper::RunHandler::RunInfo& info,
13 SkScalar lineHeight,
14 size_t index,
Julia Lavrova5207f352019-06-21 12:22:32 -040015 SkScalar offsetX)
16 : fMaster(master)
17 , fTextRange(info.utf8Range.begin(), info.utf8Range.end())
18 , fClusterRange(EMPTY_CLUSTERS) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040019 fFont = info.fFont;
20 fHeightMultiplier = lineHeight;
21 fBidiLevel = info.fBidiLevel;
22 fAdvance = info.fAdvance;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040023 fIndex = index;
24 fUtf8Range = info.utf8Range;
25 fOffset = SkVector::Make(offsetX, 0);
26 fGlyphs.push_back_n(info.glyphCount);
27 fPositions.push_back_n(info.glyphCount + 1);
Julia Lavrova5207f352019-06-21 12:22:32 -040028 fOffsets.push_back_n(info.glyphCount + 1, 0.0);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040029 fClusterIndexes.push_back_n(info.glyphCount + 1);
30 info.fFont.getMetrics(&fFontMetrics);
31 fSpaced = false;
32 // To make edge cases easier:
33 fPositions[info.glyphCount] = fOffset + fAdvance;
34 fClusterIndexes[info.glyphCount] = info.utf8Range.end();
35}
36
37SkShaper::RunHandler::Buffer Run::newRunBuffer() {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040038 return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
39}
40
41SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040042 SkASSERT(start <= end);
43 // clip |= end == size(); // Clip at the end of the run?
44 SkScalar offset = 0;
45 if (fSpaced && end > start) {
46 offset = fOffsets[clip ? end - 1 : end] - fOffsets[start];
47 }
Julia Lavrova5207f352019-06-21 12:22:32 -040048 auto correction = end > start ? fMaster->posShift(fIndex, end - 1) - fMaster->posShift(fIndex, start) : 0;
49 return fPositions[end].fX - fPositions[start].fX + offset + correction;
Julia Lavrovaa3552c52019-05-30 16:12:56 -040050}
51
52void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -040053 SkASSERT(pos + size <= this->size());
54 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
55 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
56
57 if (fSpaced || offset.fX != 0 || offset.fY != 0) {
58 for (size_t i = 0; i < size; ++i) {
59 auto point = fPositions[i + pos];
60 if (fSpaced) {
61 point.fX += fOffsets[i + pos];
62 }
Julia Lavrova5207f352019-06-21 12:22:32 -040063 point.fX += fMaster->posShift(fIndex, i + pos);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040064 blobBuffer.points()[i] = point + offset;
65 }
66 } else {
67 // Good for the first line
68 sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
69 }
70}
71
Julia Lavrova5207f352019-06-21 12:22:32 -040072std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) {
73 auto less = [](const Cluster& c1, const Cluster& c2) {
74 return c1.textRange().end <= c2.textRange().start;
75 };
76
77 if (text.width() == 0) {
78
79 auto found = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
80 fMaster->clusters().begin() + fClusterRange.end,
81 Cluster(text),
82 less);
83 return std::make_tuple(found != nullptr,
84 found - fMaster->clusters().begin(),
85 found - fMaster->clusters().begin());
Julia Lavrovaa3552c52019-05-30 16:12:56 -040086 }
87
Julia Lavrova5207f352019-06-21 12:22:32 -040088 auto start = std::lower_bound(fMaster->clusters().begin() + fClusterRange.start,
89 fMaster->clusters().begin() + fClusterRange.end,
90 Cluster(TextRange(text.start, text.start + 1)),
91 less);
92 auto end = std::lower_bound(start,
93 fMaster->clusters().begin() + fClusterRange.end,
94 Cluster(TextRange(text.end - 1, text.end)),
95 less);
Julia Lavrovaa3552c52019-05-30 16:12:56 -040096 if (!leftToRight()) {
97 std::swap(start, end);
98 }
99
Julia Lavrova5207f352019-06-21 12:22:32 -0400100 size_t startIndex = start - fMaster->clusters().begin();
101 size_t endIndex = end - fMaster->clusters().begin();
102 return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400103}
104
105void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400106 // Can't figure out how to do it with one code for both cases without 100 ifs
107 // Can't go through clusters because there are no cluster table yet
108 if (leftToRight()) {
109 size_t start = 0;
110 size_t cluster = this->clusterIndex(start);
111 for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
112 auto nextCluster = this->clusterIndex(glyph);
113 if (nextCluster == cluster) {
114 continue;
115 }
116
117 visitor(start,
118 glyph,
119 cluster,
120 nextCluster,
121 this->calculateWidth(start, glyph, glyph == size()),
122 this->calculateHeight());
123
124 start = glyph;
125 cluster = nextCluster;
126 }
127 } else {
128 size_t glyph = this->size();
129 size_t cluster = this->fUtf8Range.begin();
130 for (int32_t start = this->size() - 1; start >= 0; --start) {
131 size_t nextCluster =
132 start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
133 if (nextCluster == cluster) {
134 continue;
135 }
136
137 visitor(start,
138 glyph,
139 cluster,
140 nextCluster,
141 this->calculateWidth(start, glyph, glyph == 0),
142 this->calculateHeight());
143
144 glyph = start;
145 cluster = nextCluster;
146 }
147 }
148}
149
150SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400151 if (cluster->endPos() == cluster->startPos()) {
152 return 0;
153 }
154
155 fOffsets[cluster->endPos() - 1] += space;
156 // Increment the run width
157 fSpaced = true;
158 fAdvance.fX += space;
159 // Increment the cluster width
160 cluster->space(space, space);
161
162 return space;
163}
164
165SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400166 // Offset all the glyphs in the cluster
167 SkScalar shift = 0;
168 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
169 fOffsets[i] += shift;
170 shift += space;
171 }
172 if (this->size() == cluster->endPos()) {
173 // To make calculations easier
174 fOffsets[cluster->endPos()] += shift;
175 }
176 // Increment the run width
177 fSpaced = true;
178 fAdvance.fX += shift;
179 // Increment the cluster width
180 cluster->space(shift, space);
181
182 return shift;
183}
184
185void Run::shift(const Cluster* cluster, SkScalar offset) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400186 if (offset == 0) {
187 return;
188 }
189
190 fSpaced = true;
191 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
192 fOffsets[i] += offset;
193 }
194 if (this->size() == cluster->endPos()) {
195 // To make calculations easier
196 fOffsets[cluster->endPos()] += offset;
197 }
198}
199
200void Cluster::setIsWhiteSpaces() {
Julia Lavrova5207f352019-06-21 12:22:32 -0400201 auto text = fMaster->text();
202 auto pos = fTextRange.end;
203 while (--pos >= fTextRange.start) {
204 auto ch = text[pos];
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400205 if (!u_isspace(ch) && u_charType(ch) != U_CONTROL_CHAR &&
206 u_charType(ch) != U_NON_SPACING_MARK) {
207 return;
208 }
209 }
210 fWhiteSpaces = true;
211}
212
Julia Lavrova5207f352019-06-21 12:22:32 -0400213SkScalar Cluster::sizeToChar(TextIndex ch) const {
214 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400215 return 0;
216 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400217 auto shift = ch - fTextRange.start;
218 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400219
220 return SkDoubleToScalar(fWidth * ratio);
221}
222
Julia Lavrova5207f352019-06-21 12:22:32 -0400223SkScalar Cluster::sizeFromChar(TextIndex ch) const {
224 if (ch < fTextRange.start || ch >= fTextRange.end) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400225 return 0;
226 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400227 auto shift = fTextRange.end - ch - 1;
228 auto ratio = shift * 1.0 / fTextRange.width();
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400229
230 return SkDoubleToScalar(fWidth * ratio);
231}
232
233size_t Cluster::roundPos(SkScalar s) const {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400234 auto ratio = (s * 1.0) / fWidth;
235 return sk_double_floor2int(ratio * size());
236}
237
238SkScalar Cluster::trimmedWidth(size_t pos) const {
239 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
Julia Lavrova5207f352019-06-21 12:22:32 -0400240 // We don't have to take in account cluster shift since it's the same for 0 and for pos
241 auto& run = fMaster->run(fRunIndex);
242 return SkTMin(run.positionX(pos) - run.positionX(fStart), fWidth - fSpacing);
243}
244
245SkScalar Run::positionX(size_t pos) const {
246 return fPositions[pos].fX + fOffsets[pos] + fMaster->posShift(fIndex, pos);
247}
248
249Run* Cluster::run() const {
250 if (fRunIndex >= fMaster->runs().size()) {
251 return nullptr;
252 }
253 return &fMaster->run(fRunIndex);
254}
255
256SkFont Cluster::font() const {
257 return fMaster->run(fRunIndex).font();
258}
259
260Cluster::Cluster(ParagraphImpl* master,
261 RunIndex runIndex,
262 size_t start,
263 size_t end,
264 SkSpan<const char> text,
265 SkScalar width,
266 SkScalar height)
267 : fMaster(master)
268 , fRunIndex(runIndex)
269 , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
270 , fStart(start)
271 , fEnd(end)
272 , fWidth(width)
273 , fSpacing(0)
274 , fHeight(height)
275 , fWhiteSpaces(false)
276 , fBreakType(None) {
Julia Lavrovaa3552c52019-05-30 16:12:56 -0400277}
278
279} // namespace textlayout
280} // namespace skia