• 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 
18 namespace skia {
19 namespace textlayout {
20 
Run(ParagraphImpl * master,const SkShaper::RunHandler::RunInfo & info,size_t firstChar,SkScalar lineHeight,size_t index,SkScalar offsetX)21 Run::Run(ParagraphImpl* master,
22          const SkShaper::RunHandler::RunInfo& info,
23          size_t firstChar,
24          SkScalar lineHeight,
25          size_t index,
26          SkScalar offsetX)
27         : fMaster(master)
28         , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
29         , fClusterRange(EMPTY_CLUSTERS)
30         , fClusterStart(firstChar) {
31     fFont = info.fFont;
32     fHeightMultiplier = lineHeight;
33     fBidiLevel = info.fBidiLevel;
34     fAdvance = info.fAdvance;
35     fIndex = index;
36     fUtf8Range = info.utf8Range;
37     fOffset = SkVector::Make(offsetX, 0);
38     fGlyphs.push_back_n(info.glyphCount);
39     fBounds.push_back_n(info.glyphCount);
40     fPositions.push_back_n(info.glyphCount + 1);
41     fOffsets.push_back_n(info.glyphCount + 1);
42     fClusterIndexes.push_back_n(info.glyphCount + 1);
43     fShifts.push_back_n(info.glyphCount + 1, 0.0);
44     info.fFont.getMetrics(&fFontMetrics);
45     fSpaced = false;
46     // To make edge cases easier:
47     fPositions[info.glyphCount] = fOffset + fAdvance;
48     fOffsets[info.glyphCount] = { 0, 0};
49     fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
50     fEllipsis = false;
51     fPlaceholderIndex = std::numeric_limits<size_t>::max();
52 }
53 
newRunBuffer()54 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
55     return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
56 }
57 
commit()58 void Run::commit() {
59     fFont.getBounds(fGlyphs.data(), fGlyphs.size(), fBounds.data(), nullptr);
60 }
calculateWidth(size_t start,size_t end,bool clip) const61 SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
62     SkASSERT(start <= end);
63     // clip |= end == size();  // Clip at the end of the run?
64     SkScalar shift = 0;
65     if (fSpaced && end > start) {
66         shift = fShifts[clip ? end - 1 : end] - fShifts[start];
67     }
68     auto correction = 0.0f;
69     if (end > start) {
70         correction = fMaster->posShift(fIndex, clip ? end - 1 : end) -
71                      fMaster->posShift(fIndex, start);
72     }
73     return posX(end) - posX(start) + shift + correction;
74 }
75 
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size,SkVector runOffset) const76 void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector runOffset) const {
77     SkASSERT(pos + size <= this->size());
78     const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
79     sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
80     for (size_t i = 0; i < size; ++i) {
81         auto point = fPositions[i + pos];
82         auto offset = fOffsets[i + pos];
83         point.offset(offset.fX, offset.fY);
84         if (fSpaced) {
85             point.fX += fShifts[i + pos];
86         }
87         point.fX += fMaster->posShift(fIndex, i + pos);
88         blobBuffer.points()[i] = point + runOffset;
89     }
90 }
91 
findLimitingClusters(TextRange text,bool extendToClusters) const92 std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool extendToClusters) const {
93 
94     if (text.width() == 0) {
95         for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
96             auto& cluster = fMaster->cluster(i);
97             if (cluster.textRange().end >= text.end && cluster.textRange().start <= text.start) {
98                 return std::make_tuple(true, i, i);
99             }
100         }
101         return std::make_tuple(false, 0, 0);
102     }
103     Cluster* start = nullptr;
104     Cluster* end = nullptr;
105     if (extendToClusters) {
106         for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
107             auto& cluster = fMaster->cluster(i);
108             auto clusterRange = cluster.textRange();
109             if (clusterRange.end <= text.start) {
110                 continue;
111             } else if (clusterRange.start >= text.end) {
112                 break;
113             }
114 
115             TextRange s = TextRange(std::max(clusterRange.start, text.start),
116                                     std::min(clusterRange.end, text.end));
117             if (s.width() > 0) {
118                 if (start == nullptr) {
119                     start = &cluster;
120                 }
121                 end = &cluster;
122             }
123         }
124     } else {
125         // TODO: Do we need to use this branch?..
126         for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
127             auto& cluster = fMaster->cluster(i);
128             if (cluster.textRange().end > text.start && start == nullptr) {
129                 start = &cluster;
130             }
131             if (cluster.textRange().start < text.end) {
132                 end = &cluster;
133             } else {
134                 break;
135             }
136         }
137     }
138 
139     if (start == nullptr || end == nullptr) {
140         return std::make_tuple(false, 0, 0);
141     }
142 
143     if (!leftToRight()) {
144         std::swap(start, end);
145     }
146 
147     size_t startIndex = start - fMaster->clusters().begin();
148     size_t endIndex   = end   - fMaster->clusters().begin();
149     return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
150 }
151 
iterateThroughClustersInTextOrder(const ClusterVisitor & visitor)152 void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
153     // Can't figure out how to do it with one code for both cases without 100 ifs
154     // Can't go through clusters because there are no cluster table yet
155     if (leftToRight()) {
156         size_t start = 0;
157         size_t cluster = this->clusterIndex(start);
158         for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
159             auto nextCluster = this->clusterIndex(glyph);
160             if (nextCluster == cluster) {
161                 continue;
162             }
163 
164             visitor(start,
165                     glyph,
166                     fClusterStart + cluster,
167                     fClusterStart + nextCluster,
168                     this->calculateWidth(start, glyph, glyph == size()),
169                     this->calculateHeight());
170 
171             start = glyph;
172             cluster = nextCluster;
173         }
174     } else {
175         size_t glyph = this->size();
176         size_t cluster = this->fUtf8Range.begin();
177         for (int32_t start = this->size() - 1; start >= 0; --start) {
178             size_t nextCluster =
179                     start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
180             if (nextCluster == cluster) {
181                 continue;
182             }
183 
184             visitor(start,
185                     glyph,
186                     fClusterStart + cluster,
187                     fClusterStart + nextCluster,
188                     this->calculateWidth(start, glyph, glyph == 0),
189                     this->calculateHeight());
190 
191             glyph = start;
192             cluster = nextCluster;
193         }
194     }
195 }
196 
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)197 SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
198     if (cluster->endPos() == cluster->startPos()) {
199         return 0;
200     }
201 
202     fShifts[cluster->endPos() - 1] += space;
203     // Increment the run width
204     fSpaced = true;
205     fAdvance.fX += space;
206     // Increment the cluster width
207     cluster->space(space, space);
208 
209     return space;
210 }
211 
addSpacesEvenly(SkScalar space,Cluster * cluster)212 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
213     // Offset all the glyphs in the cluster
214     SkScalar shift = 0;
215     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
216         fShifts[i] += shift;
217         shift += space;
218     }
219     if (this->size() == cluster->endPos()) {
220         // To make calculations easier
221         fShifts[cluster->endPos()] += shift;
222     }
223     // Increment the run width
224     fSpaced = true;
225     fAdvance.fX += shift;
226     // Increment the cluster width
227     cluster->space(shift, space);
228     cluster->setHalfLetterSpacing(space / 2);
229 
230     return shift;
231 }
232 
shift(const Cluster * cluster,SkScalar offset)233 void Run::shift(const Cluster* cluster, SkScalar offset) {
234     if (offset == 0) {
235         return;
236     }
237 
238     fSpaced = true;
239     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
240         fShifts[i] += offset;
241     }
242     if (this->size() == cluster->endPos()) {
243         // To make calculations easier
244         fShifts[cluster->endPos()] += offset;
245     }
246 }
247 
updateMetrics(InternalLineMetrics * endlineMetrics)248 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
249 
250     SkASSERT(isPlaceholder());
251     auto placeholderStyle = this->placeholderStyle();
252     // Difference between the placeholder baseline and the line bottom
253     SkScalar baselineAdjustment = 0;
254     switch (placeholderStyle->fBaseline) {
255         case TextBaseline::kAlphabetic:
256             break;
257 
258         case TextBaseline::kIdeographic:
259             baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
260             break;
261     }
262 
263     auto height = placeholderStyle->fHeight;
264     auto offset = placeholderStyle->fBaselineOffset;
265 
266     fFontMetrics.fLeading = 0;
267     switch (placeholderStyle->fAlignment) {
268         case PlaceholderAlignment::kBaseline:
269             fFontMetrics.fAscent = baselineAdjustment - offset;
270             fFontMetrics.fDescent = baselineAdjustment + height - offset;
271             break;
272 
273         case PlaceholderAlignment::kAboveBaseline:
274             fFontMetrics.fAscent = baselineAdjustment - height;
275             fFontMetrics.fDescent = baselineAdjustment;
276             break;
277 
278         case PlaceholderAlignment::kBelowBaseline:
279             fFontMetrics.fAscent = baselineAdjustment;
280             fFontMetrics.fDescent = baselineAdjustment + height;
281             break;
282 
283         case PlaceholderAlignment::kTop:
284             fFontMetrics.fDescent = height + fFontMetrics.fAscent;
285             break;
286 
287         case PlaceholderAlignment::kBottom:
288             fFontMetrics.fAscent = fFontMetrics.fDescent - height;
289             break;
290 
291         case PlaceholderAlignment::kMiddle:
292             auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
293             fFontMetrics.fDescent = height/2.0 - mid;
294             fFontMetrics.fAscent =  - height/2.0 - mid;
295             break;
296     }
297 
298     // Make sure the placeholder can fit the line
299     endlineMetrics->add(this);
300 }
301 
setIsWhiteSpaces()302 void Cluster::setIsWhiteSpaces() {
303 
304     fWhiteSpaces = false;
305 
306     auto span = fMaster->text(fTextRange);
307     const char* ch = span.begin();
308     while (ch < span.end()) {
309         auto unichar = utf8_next(&ch, span.end());
310         if (!u_isWhitespace(unichar)) {
311             return;
312         }
313     }
314     fWhiteSpaces = true;
315 }
316 
sizeToChar(TextIndex ch) const317 SkScalar Cluster::sizeToChar(TextIndex ch) const {
318     if (ch < fTextRange.start || ch >= fTextRange.end) {
319         return 0;
320     }
321     auto shift = ch - fTextRange.start;
322     auto ratio = shift * 1.0 / fTextRange.width();
323 
324     return SkDoubleToScalar(fWidth * ratio);
325 }
326 
sizeFromChar(TextIndex ch) const327 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
328     if (ch < fTextRange.start || ch >= fTextRange.end) {
329         return 0;
330     }
331     auto shift = fTextRange.end - ch - 1;
332     auto ratio = shift * 1.0 / fTextRange.width();
333 
334     return SkDoubleToScalar(fWidth * ratio);
335 }
336 
roundPos(SkScalar s) const337 size_t Cluster::roundPos(SkScalar s) const {
338     auto ratio = (s * 1.0) / fWidth;
339     return sk_double_floor2int(ratio * size());
340 }
341 
trimmedWidth(size_t pos) const342 SkScalar Cluster::trimmedWidth(size_t pos) const {
343     // Find the width until the pos and return the min between trimmedWidth and the width(pos)
344     // We don't have to take in account cluster shift since it's the same for 0 and for pos
345     auto& run = fMaster->run(fRunIndex);
346     return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
347 }
348 
positionX(size_t pos) const349 SkScalar Run::positionX(size_t pos) const {
350     return posX(pos) + fShifts[pos] + fMaster->posShift(fIndex, pos);
351 }
352 
placeholderStyle() const353 PlaceholderStyle* Run::placeholderStyle() const {
354     if (isPlaceholder()) {
355         return &fMaster->placeholders()[fPlaceholderIndex].fStyle;
356     } else {
357         return nullptr;
358     }
359 }
360 
run() const361 Run* Cluster::run() const {
362     if (fRunIndex >= fMaster->runs().size()) {
363         return nullptr;
364     }
365     return &fMaster->run(fRunIndex);
366 }
367 
font() const368 SkFont Cluster::font() const {
369     return fMaster->run(fRunIndex).font();
370 }
371 
Cluster(ParagraphImpl * master,RunIndex runIndex,size_t start,size_t end,SkSpan<const char> text,SkScalar width,SkScalar height)372 Cluster::Cluster(ParagraphImpl* master,
373         RunIndex runIndex,
374         size_t start,
375         size_t end,
376         SkSpan<const char> text,
377         SkScalar width,
378         SkScalar height)
379         : fMaster(master)
380         , fRunIndex(runIndex)
381         , fTextRange(text.begin() - fMaster->text().begin(), text.end() - fMaster->text().begin())
382         , fGraphemeRange(EMPTY_RANGE)
383         , fStart(start)
384         , fEnd(end)
385         , fWidth(width)
386         , fSpacing(0)
387         , fHeight(height)
388         , fHalfLetterSpacing(0.0)
389         , fWhiteSpaces(false)
390         , fBreakType(None) {
391 }
392 
393 }  // namespace textlayout
394 }  // namespace skia
395