• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 #include "include/core/SkFontMetrics.h"
3 #include "include/core/SkTextBlob.h"
4 #include "include/private/SkFloatingPoint.h"
5 #include "include/private/SkMalloc.h"
6 #include "include/private/SkTo.h"
7 #include "modules/skparagraph/include/DartTypes.h"
8 #include "modules/skparagraph/include/TextStyle.h"
9 #include "modules/skparagraph/src/ParagraphImpl.h"
10 #include "modules/skparagraph/src/Run.h"
11 #include "modules/skshaper/include/SkShaper.h"
12 #include "src/utils/SkUTF.h"
13 
14 namespace skia {
15 namespace textlayout {
16 
Run(ParagraphImpl * owner,const SkShaper::RunHandler::RunInfo & info,size_t firstChar,SkScalar heightMultiplier,bool useHalfLeading,SkScalar baselineShift,size_t index,SkScalar offsetX)17 Run::Run(ParagraphImpl* owner,
18          const SkShaper::RunHandler::RunInfo& info,
19          size_t firstChar,
20          SkScalar heightMultiplier,
21          bool useHalfLeading,
22          SkScalar baselineShift,
23          size_t index,
24          SkScalar offsetX)
25     : fOwner(owner)
26     , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
27     , fClusterRange(EMPTY_CLUSTERS)
28     , fFont(info.fFont)
29     , fClusterStart(firstChar)
30     , fGlyphData(std::make_shared<GlyphData>())
31     , fGlyphs(fGlyphData->glyphs)
32     , fPositions(fGlyphData->positions)
33     , fOffsets(fGlyphData->offsets)
34     , fClusterIndexes(fGlyphData->clusterIndexes)
35     , fHeightMultiplier(heightMultiplier)
36     , fUseHalfLeading(useHalfLeading)
37     , fBaselineShift(baselineShift)
38 {
39     fBidiLevel = info.fBidiLevel;
40     fAdvance = info.fAdvance;
41     fIndex = index;
42     fUtf8Range = info.utf8Range;
43     fOffset = SkVector::Make(offsetX, 0);
44 
45     fGlyphs.push_back_n(info.glyphCount);
46     fPositions.push_back_n(info.glyphCount + 1);
47     fOffsets.push_back_n(info.glyphCount + 1);
48     fClusterIndexes.push_back_n(info.glyphCount + 1);
49     info.fFont.getMetrics(&fFontMetrics);
50 
51     this->calculateMetrics();
52 
53     // To make edge cases easier:
54     fPositions[info.glyphCount] = fOffset + fAdvance;
55     fOffsets[info.glyphCount] = {0, 0};
56     fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
57     fEllipsis = false;
58     fPlaceholderIndex = std::numeric_limits<size_t>::max();
59 }
60 
calculateMetrics()61 void Run::calculateMetrics() {
62     fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5;
63     fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5;
64     fCorrectLeading = 0;
65     if (SkScalarNearlyZero(fHeightMultiplier)) {
66         return;
67     }
68     const auto runHeight = fHeightMultiplier * fFont.getSize();
69     const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent;
70     if (fUseHalfLeading) {
71         const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2;
72         fCorrectAscent -= extraLeading;
73         fCorrectDescent += extraLeading;
74     } else {
75         const auto multiplier = runHeight / fontIntrinsicHeight;
76         fCorrectAscent *= multiplier;
77         fCorrectDescent *= multiplier;
78     }
79     // If we shift the baseline we need to make sure the shifted text fits the line
80     fCorrectAscent += fBaselineShift;
81     fCorrectDescent += fBaselineShift;
82 }
83 
newRunBuffer()84 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
85     return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
86 }
87 
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size) const88 void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const {
89     SkASSERT(pos + size <= this->size());
90     const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
91     sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
92 
93     for (size_t i = 0; i < size; ++i) {
94         auto point = fPositions[i + pos];
95         if (!fJustificationShifts.empty()) {
96             point.fX += fJustificationShifts[i + pos].fX;
97         }
98         point += fOffsets[i + pos];
99         blobBuffer.points()[i] = point;
100     }
101 }
102 
103 // Find a cluster range from text range (within one run)
104 // Cluster range is normalized ([start:end) start < end regardless of TextDirection
105 // Boolean value in triple indicates whether the cluster range was found or not
findLimitingClusters(TextRange text) const106 std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
107     if (text.width() == 0) {
108         // Special Flutter case for "\n" and "...\n"
109         if (text.end > this->fTextRange.start) {
110             ClusterIndex index = fOwner->clusterIndex(text.end - 1);
111             return std::make_tuple(true, index, index);
112         } else {
113             return std::make_tuple(false, 0, 0);
114         }
115     }
116 
117     ClusterRange clusterRange;
118     bool found = true;
119     // Deal with the case when either start or end are not align with glyph cluster edge
120     // In such case we shift the text range to the right
121     // (cutting from the left and adding to the right)
122     if (leftToRight()) {
123         // LTR: [start:end)
124         found = clusterRange.start != fClusterRange.end;
125         clusterRange.start = fOwner->clusterIndex(text.start);
126         clusterRange.end = fOwner->clusterIndex(text.end - 1);
127     } else {
128         // RTL: (start:end]
129         clusterRange.start = fOwner->clusterIndex(text.end);
130         clusterRange.end = fOwner->clusterIndex(text.start + 1);
131         found = clusterRange.end != fClusterRange.start;
132     }
133 
134     return std::make_tuple(
135             found,
136             clusterRange.start,
137             clusterRange.end);
138 }
139 
findLimitingGlyphClusters(TextRange text) const140 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGlyphClusters(TextRange text) const {
141     TextIndex start = fOwner->findPreviousGlyphClusterBoundary(text.start);
142     TextIndex end = fOwner->findNextGlyphClusterBoundary(text.end);
143     return std::make_tuple(true, start, end);
144 }
145 
146 // Adjust the text to grapheme edges so the first grapheme start is in the text and the last grapheme start is in the text
147 // It actually means that the first grapheme is entirely in the text and the last grapheme does not have to be
148 // 12345 234 2:2 -> 2,5 4:4
findLimitingGraphemes(TextRange text) const149 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const {
150     TextIndex start = fOwner->findPreviousGraphemeBoundary(text.start);
151     TextIndex end = fOwner->findNextGraphemeBoundary(text.end);
152     return std::make_tuple(true, start, end);
153 }
154 
iterateThroughClusters(const ClusterVisitor & visitor)155 void Run::iterateThroughClusters(const ClusterVisitor& visitor) {
156 
157     for (size_t index = 0; index < fClusterRange.width(); ++index) {
158         auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1;
159         auto cluster = &fOwner->cluster(correctIndex);
160         visitor(cluster);
161     }
162 }
163 
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)164 void Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
165     // Increment the run width
166     fAdvance.fX += space;
167     // Increment the cluster width
168     cluster->space(space);
169 }
170 
addSpacesEvenly(SkScalar space)171 SkScalar Run::addSpacesEvenly(SkScalar space) {
172     SkScalar shift = 0;
173     for (size_t i = 0; i < this->size(); ++i) {
174         fPositions[i].fX += shift;
175         shift += space;
176     }
177     fPositions[this->size()].fX += shift;
178     fAdvance.fX += shift;
179     return shift;
180 }
181 
addSpacesEvenly(SkScalar space,Cluster * cluster)182 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
183     // Offset all the glyphs in the cluster
184     SkScalar shift = 0;
185     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
186         fPositions[i].fX += shift;
187         if (this->index() == 0) {
188             fPositions[i].fX += space / 2; // offset by space / 2
189         }
190         shift += space;
191     }
192     if (this->size() == cluster->endPos()) {
193         // To make calculations easier
194         fPositions[cluster->endPos()].fX += shift;
195     }
196     // Increment the run width
197     fAdvance.fX += shift;
198     // Increment the cluster width
199     cluster->space(shift);
200     cluster->setHalfLetterSpacing(space / 2);
201 
202     return shift;
203 }
204 
shift(const Cluster * cluster,SkScalar offset)205 void Run::shift(const Cluster* cluster, SkScalar offset) {
206     if (offset == 0) {
207         return;
208     }
209 
210     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
211         fPositions[i].fX += offset;
212     }
213     if (this->size() == cluster->endPos()) {
214         // To make calculations easier
215         fPositions[cluster->endPos()].fX += offset;
216     }
217 }
218 
updateMetrics(InternalLineMetrics * endlineMetrics)219 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
220 
221     SkASSERT(isPlaceholder());
222     auto placeholderStyle = this->placeholderStyle();
223     // Difference between the placeholder baseline and the line bottom
224     SkScalar baselineAdjustment = 0;
225     switch (placeholderStyle->fBaseline) {
226         case TextBaseline::kAlphabetic:
227             break;
228 
229         case TextBaseline::kIdeographic:
230             baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
231             break;
232     }
233 
234     auto height = placeholderStyle->fHeight;
235     auto offset = placeholderStyle->fBaselineOffset;
236 
237     fFontMetrics.fLeading = 0;
238     switch (placeholderStyle->fAlignment) {
239         case PlaceholderAlignment::kBaseline:
240             fFontMetrics.fAscent = baselineAdjustment - offset;
241             fFontMetrics.fDescent = baselineAdjustment + height - offset;
242             break;
243 
244         case PlaceholderAlignment::kAboveBaseline:
245             fFontMetrics.fAscent = baselineAdjustment - height;
246             fFontMetrics.fDescent = baselineAdjustment;
247             break;
248 
249         case PlaceholderAlignment::kBelowBaseline:
250             fFontMetrics.fAscent = baselineAdjustment;
251             fFontMetrics.fDescent = baselineAdjustment + height;
252             break;
253 
254         case PlaceholderAlignment::kTop:
255             fFontMetrics.fDescent = height + fFontMetrics.fAscent;
256             break;
257 
258         case PlaceholderAlignment::kBottom:
259             fFontMetrics.fAscent = fFontMetrics.fDescent - height;
260             break;
261 
262         case PlaceholderAlignment::kMiddle:
263             auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
264             fFontMetrics.fDescent = height/2.0 - mid;
265             fFontMetrics.fAscent =  - height/2.0 - mid;
266             break;
267     }
268 
269     this->calculateMetrics();
270 
271     // Make sure the placeholder can fit the line
272     endlineMetrics->add(this);
273 }
274 
sizeToChar(TextIndex ch) const275 SkScalar Cluster::sizeToChar(TextIndex ch) const {
276     if (ch < fTextRange.start || ch >= fTextRange.end) {
277         return 0;
278     }
279     auto shift = ch - fTextRange.start;
280     auto ratio = shift * 1.0 / fTextRange.width();
281 
282     return SkDoubleToScalar(fWidth * ratio);
283 }
284 
sizeFromChar(TextIndex ch) const285 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
286     if (ch < fTextRange.start || ch >= fTextRange.end) {
287         return 0;
288     }
289     auto shift = fTextRange.end - ch - 1;
290     auto ratio = shift * 1.0 / fTextRange.width();
291 
292     return SkDoubleToScalar(fWidth * ratio);
293 }
294 
roundPos(SkScalar s) const295 size_t Cluster::roundPos(SkScalar s) const {
296     auto ratio = (s * 1.0) / fWidth;
297     return sk_double_floor2int(ratio * size());
298 }
299 
trimmedWidth(size_t pos) const300 SkScalar Cluster::trimmedWidth(size_t pos) const {
301     // Find the width until the pos and return the min between trimmedWidth and the width(pos)
302     // We don't have to take in account cluster shift since it's the same for 0 and for pos
303     auto& run = fOwner->run(fRunIndex);
304     return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
305 }
306 
positionX(size_t pos) const307 SkScalar Run::positionX(size_t pos) const {
308     return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
309 }
310 
placeholderStyle() const311 PlaceholderStyle* Run::placeholderStyle() const {
312     if (isPlaceholder()) {
313         return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
314     } else {
315         return nullptr;
316     }
317 }
318 
isResolved() const319 bool Run::isResolved() const {
320     for (auto& glyph :fGlyphs) {
321         if (glyph == 0) {
322             return false;
323         }
324     }
325     return true;
326 }
327 
runOrNull() const328 Run* Cluster::runOrNull() const {
329     if (fRunIndex >= fOwner->runs().size()) {
330         return nullptr;
331     }
332     return &fOwner->run(fRunIndex);
333 }
334 
run() const335 Run& Cluster::run() const {
336     SkASSERT(fRunIndex < fOwner->runs().size());
337     return fOwner->run(fRunIndex);
338 }
339 
font() const340 SkFont Cluster::font() const {
341     SkASSERT(fRunIndex < fOwner->runs().size());
342     return fOwner->run(fRunIndex).font();
343 }
344 
isSoftBreak() const345 bool Cluster::isSoftBreak() const {
346     return fOwner->codeUnitHasProperty(fTextRange.end,
347                                        SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
348 }
349 
isGraphemeBreak() const350 bool Cluster::isGraphemeBreak() const {
351     return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
352 }
353 }  // namespace textlayout
354 }  // namespace skia
355