• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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