xref: /aosp_15_r20/external/skia/modules/skparagraph/src/Run.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 // Copyright 2019 Google LLC.
2 #include "include/core/SkFontMetrics.h"
3 #include "include/core/SkTextBlob.h"
4 #include "include/private/base/SkFloatingPoint.h"
5 #include "include/private/base/SkMalloc.h"
6 #include "include/private/base/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/base/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         shift += space;
188     }
189     if (this->size() == cluster->endPos()) {
190         // To make calculations easier
191         fPositions[cluster->endPos()].fX += shift;
192     }
193     // Increment the run width
194     fAdvance.fX += shift;
195     // Increment the cluster width
196     cluster->space(shift);
197     cluster->setHalfLetterSpacing(space / 2);
198 
199     return shift;
200 }
201 
shift(const Cluster * cluster,SkScalar offset)202 void Run::shift(const Cluster* cluster, SkScalar offset) {
203     if (offset == 0) {
204         return;
205     }
206     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
207         fPositions[i].fX += offset;
208     }
209     if (this->size() == cluster->endPos()) {
210         // To make calculations easier
211         fPositions[cluster->endPos()].fX += offset;
212     }
213 }
214 
extend(const Cluster * cluster,SkScalar offset)215 void Run::extend(const Cluster* cluster, SkScalar offset) {
216     // Extend the cluster at the end
217     fPositions[cluster->endPos()].fX += offset;
218 }
219 
updateMetrics(InternalLineMetrics * endlineMetrics)220 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
221 
222     SkASSERT(isPlaceholder());
223     auto placeholderStyle = this->placeholderStyle();
224     // Difference between the placeholder baseline and the line bottom
225     SkScalar baselineAdjustment = 0;
226     switch (placeholderStyle->fBaseline) {
227         case TextBaseline::kAlphabetic:
228             break;
229 
230         case TextBaseline::kIdeographic:
231             baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
232             break;
233     }
234 
235     auto height = placeholderStyle->fHeight;
236     auto offset = placeholderStyle->fBaselineOffset;
237 
238     fFontMetrics.fLeading = 0;
239     switch (placeholderStyle->fAlignment) {
240         case PlaceholderAlignment::kBaseline:
241             fFontMetrics.fAscent = baselineAdjustment - offset;
242             fFontMetrics.fDescent = baselineAdjustment + height - offset;
243             break;
244 
245         case PlaceholderAlignment::kAboveBaseline:
246             fFontMetrics.fAscent = baselineAdjustment - height;
247             fFontMetrics.fDescent = baselineAdjustment;
248             break;
249 
250         case PlaceholderAlignment::kBelowBaseline:
251             fFontMetrics.fAscent = baselineAdjustment;
252             fFontMetrics.fDescent = baselineAdjustment + height;
253             break;
254 
255         case PlaceholderAlignment::kTop:
256             fFontMetrics.fDescent = height + fFontMetrics.fAscent;
257             break;
258 
259         case PlaceholderAlignment::kBottom:
260             fFontMetrics.fAscent = fFontMetrics.fDescent - height;
261             break;
262 
263         case PlaceholderAlignment::kMiddle:
264             auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
265             fFontMetrics.fDescent = height/2.0 - mid;
266             fFontMetrics.fAscent =  - height/2.0 - mid;
267             break;
268     }
269 
270     this->calculateMetrics();
271 
272     // Make sure the placeholder can fit the line
273     endlineMetrics->add(this);
274 }
275 
sizeToChar(TextIndex ch) const276 SkScalar Cluster::sizeToChar(TextIndex ch) const {
277     if (ch < fTextRange.start || ch >= fTextRange.end) {
278         return 0;
279     }
280     auto shift = ch - fTextRange.start;
281     auto ratio = shift * 1.0 / fTextRange.width();
282 
283     return SkDoubleToScalar(fWidth * ratio);
284 }
285 
sizeFromChar(TextIndex ch) const286 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
287     if (ch < fTextRange.start || ch >= fTextRange.end) {
288         return 0;
289     }
290     auto shift = fTextRange.end - ch - 1;
291     auto ratio = shift * 1.0 / fTextRange.width();
292 
293     return SkDoubleToScalar(fWidth * ratio);
294 }
295 
roundPos(SkScalar s) const296 size_t Cluster::roundPos(SkScalar s) const {
297     auto ratio = (s * 1.0) / fWidth;
298     return sk_double_floor2int(ratio * size());
299 }
300 
trimmedWidth(size_t pos) const301 SkScalar Cluster::trimmedWidth(size_t pos) const {
302     // Find the width until the pos and return the min between trimmedWidth and the width(pos)
303     // We don't have to take in account cluster shift since it's the same for 0 and for pos
304     auto& run = fOwner->run(fRunIndex);
305     return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
306 }
307 
positionX(size_t pos) const308 SkScalar Run::positionX(size_t pos) const {
309     return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
310 }
311 
placeholderStyle() const312 PlaceholderStyle* Run::placeholderStyle() const {
313     if (isPlaceholder()) {
314         return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
315     } else {
316         return nullptr;
317     }
318 }
319 
isResolved() const320 bool Run::isResolved() const {
321     for (auto& glyph :fGlyphs) {
322         if (glyph == 0) {
323             return false;
324         }
325     }
326     return true;
327 }
328 
runOrNull() const329 Run* Cluster::runOrNull() const {
330     if (fRunIndex >= fOwner->runs().size()) {
331         return nullptr;
332     }
333     return &fOwner->run(fRunIndex);
334 }
335 
run() const336 Run& Cluster::run() const {
337     SkASSERT(fRunIndex < fOwner->runs().size());
338     return fOwner->run(fRunIndex);
339 }
340 
font() const341 SkFont Cluster::font() const {
342     SkASSERT(fRunIndex < fOwner->runs().size());
343     return fOwner->run(fRunIndex).font();
344 }
345 
isSoftBreak() const346 bool Cluster::isSoftBreak() const {
347     return fOwner->codeUnitHasProperty(fTextRange.end,
348                                        SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
349 }
350 
isGraphemeBreak() const351 bool Cluster::isGraphemeBreak() const {
352     return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
353 }
354 }  // namespace textlayout
355 }  // namespace skia
356