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