xref: /aosp_15_r20/external/skia/src/text/GlyphRun.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2018 The Android Open Source Project
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/GlyphRun.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFont.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRSXform.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTLogic.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkFontPriv.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkGlyph.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkStrikeSpec.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTextBlobPriv.h"
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker class SkPaint;
23*c8dee2aaSAndroid Build Coastguard Worker 
24*c8dee2aaSAndroid Build Coastguard Worker namespace sktext {
25*c8dee2aaSAndroid Build Coastguard Worker // -- GlyphRun -------------------------------------------------------------------------------------
GlyphRun(const SkFont & font,SkSpan<const SkPoint> positions,SkSpan<const SkGlyphID> glyphIDs,SkSpan<const char> text,SkSpan<const uint32_t> clusters,SkSpan<const SkVector> scaledRotations)26*c8dee2aaSAndroid Build Coastguard Worker GlyphRun::GlyphRun(const SkFont& font,
27*c8dee2aaSAndroid Build Coastguard Worker                    SkSpan<const SkPoint> positions,
28*c8dee2aaSAndroid Build Coastguard Worker                    SkSpan<const SkGlyphID> glyphIDs,
29*c8dee2aaSAndroid Build Coastguard Worker                    SkSpan<const char> text,
30*c8dee2aaSAndroid Build Coastguard Worker                    SkSpan<const uint32_t> clusters,
31*c8dee2aaSAndroid Build Coastguard Worker                    SkSpan<const SkVector> scaledRotations)
32*c8dee2aaSAndroid Build Coastguard Worker         : fSource{SkMakeZip(glyphIDs, positions)}
33*c8dee2aaSAndroid Build Coastguard Worker         , fText{text}
34*c8dee2aaSAndroid Build Coastguard Worker         , fClusters{clusters}
35*c8dee2aaSAndroid Build Coastguard Worker         , fScaledRotations{scaledRotations}
36*c8dee2aaSAndroid Build Coastguard Worker         , fFont{font} {}
37*c8dee2aaSAndroid Build Coastguard Worker 
GlyphRun(const GlyphRun & that,const SkFont & font)38*c8dee2aaSAndroid Build Coastguard Worker GlyphRun::GlyphRun(const GlyphRun& that, const SkFont& font)
39*c8dee2aaSAndroid Build Coastguard Worker     : fSource{that.fSource}
40*c8dee2aaSAndroid Build Coastguard Worker     , fText{that.fText}
41*c8dee2aaSAndroid Build Coastguard Worker     , fClusters{that.fClusters}
42*c8dee2aaSAndroid Build Coastguard Worker     , fFont{font} {}
43*c8dee2aaSAndroid Build Coastguard Worker 
44*c8dee2aaSAndroid Build Coastguard Worker // -- GlyphRunList ---------------------------------------------------------------------------------
GlyphRunList(const SkTextBlob * blob,SkRect bounds,SkPoint origin,SkSpan<const GlyphRun> glyphRunList,GlyphRunBuilder * builder)45*c8dee2aaSAndroid Build Coastguard Worker GlyphRunList::GlyphRunList(const SkTextBlob* blob,
46*c8dee2aaSAndroid Build Coastguard Worker                            SkRect bounds,
47*c8dee2aaSAndroid Build Coastguard Worker                            SkPoint origin,
48*c8dee2aaSAndroid Build Coastguard Worker                            SkSpan<const GlyphRun> glyphRunList,
49*c8dee2aaSAndroid Build Coastguard Worker                            GlyphRunBuilder* builder)
50*c8dee2aaSAndroid Build Coastguard Worker         : fGlyphRuns{glyphRunList}
51*c8dee2aaSAndroid Build Coastguard Worker         , fOriginalTextBlob{blob}
52*c8dee2aaSAndroid Build Coastguard Worker         , fSourceBounds{bounds}
53*c8dee2aaSAndroid Build Coastguard Worker         , fOrigin{origin}
54*c8dee2aaSAndroid Build Coastguard Worker         , fBuilder{builder} {}
55*c8dee2aaSAndroid Build Coastguard Worker 
GlyphRunList(const GlyphRun & glyphRun,const SkRect & bounds,SkPoint origin,GlyphRunBuilder * builder)56*c8dee2aaSAndroid Build Coastguard Worker GlyphRunList::GlyphRunList(const GlyphRun& glyphRun,
57*c8dee2aaSAndroid Build Coastguard Worker                            const SkRect& bounds,
58*c8dee2aaSAndroid Build Coastguard Worker                            SkPoint origin,
59*c8dee2aaSAndroid Build Coastguard Worker                            GlyphRunBuilder* builder)
60*c8dee2aaSAndroid Build Coastguard Worker         : fGlyphRuns{SkSpan<const GlyphRun>{&glyphRun, 1}}
61*c8dee2aaSAndroid Build Coastguard Worker         , fOriginalTextBlob{nullptr}
62*c8dee2aaSAndroid Build Coastguard Worker         , fSourceBounds{bounds}
63*c8dee2aaSAndroid Build Coastguard Worker         , fOrigin{origin}
64*c8dee2aaSAndroid Build Coastguard Worker         , fBuilder{builder} {}
65*c8dee2aaSAndroid Build Coastguard Worker 
uniqueID() const66*c8dee2aaSAndroid Build Coastguard Worker uint64_t GlyphRunList::uniqueID() const {
67*c8dee2aaSAndroid Build Coastguard Worker     return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID()
68*c8dee2aaSAndroid Build Coastguard Worker                                         : SK_InvalidUniqueID;
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker 
anyRunsLCD() const71*c8dee2aaSAndroid Build Coastguard Worker bool GlyphRunList::anyRunsLCD() const {
72*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& r : fGlyphRuns) {
73*c8dee2aaSAndroid Build Coastguard Worker         if (r.font().getEdging() == SkFont::Edging::kSubpixelAntiAlias) {
74*c8dee2aaSAndroid Build Coastguard Worker             return true;
75*c8dee2aaSAndroid Build Coastguard Worker         }
76*c8dee2aaSAndroid Build Coastguard Worker     }
77*c8dee2aaSAndroid Build Coastguard Worker     return false;
78*c8dee2aaSAndroid Build Coastguard Worker }
79*c8dee2aaSAndroid Build Coastguard Worker 
temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID,SkTextBlob::PurgeDelegate pd) const80*c8dee2aaSAndroid Build Coastguard Worker void GlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID,
81*c8dee2aaSAndroid Build Coastguard Worker                                                         SkTextBlob::PurgeDelegate pd) const {
82*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fOriginalTextBlob != nullptr);
83*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(pd != nullptr);
84*c8dee2aaSAndroid Build Coastguard Worker     fOriginalTextBlob->notifyAddedToCache(cacheID, pd);
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker 
makeBlob() const87*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkTextBlob> GlyphRunList::makeBlob() const {
88*c8dee2aaSAndroid Build Coastguard Worker     SkTextBlobBuilder builder;
89*c8dee2aaSAndroid Build Coastguard Worker     for (auto& run : *this) {
90*c8dee2aaSAndroid Build Coastguard Worker         SkTextBlobBuilder::RunBuffer buffer;
91*c8dee2aaSAndroid Build Coastguard Worker         if (run.scaledRotations().empty()) {
92*c8dee2aaSAndroid Build Coastguard Worker             if (run.text().empty()) {
93*c8dee2aaSAndroid Build Coastguard Worker                 buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr);
94*c8dee2aaSAndroid Build Coastguard Worker             } else {
95*c8dee2aaSAndroid Build Coastguard Worker                 buffer = builder.allocRunTextPos(run.font(), run.runSize(), run.text().size(), nullptr);
96*c8dee2aaSAndroid Build Coastguard Worker                 auto text = run.text();
97*c8dee2aaSAndroid Build Coastguard Worker                 memcpy(buffer.utf8text, text.data(), text.size_bytes());
98*c8dee2aaSAndroid Build Coastguard Worker                 auto clusters = run.clusters();
99*c8dee2aaSAndroid Build Coastguard Worker                 memcpy(buffer.clusters, clusters.data(), clusters.size_bytes());
100*c8dee2aaSAndroid Build Coastguard Worker             }
101*c8dee2aaSAndroid Build Coastguard Worker             auto positions = run.positions();
102*c8dee2aaSAndroid Build Coastguard Worker             memcpy(buffer.points(), positions.data(), positions.size_bytes());
103*c8dee2aaSAndroid Build Coastguard Worker         } else {
104*c8dee2aaSAndroid Build Coastguard Worker             buffer = builder.allocRunRSXform(run.font(), run.runSize());
105*c8dee2aaSAndroid Build Coastguard Worker             for (auto [xform, pos, sr] : SkMakeZip(buffer.xforms(),
106*c8dee2aaSAndroid Build Coastguard Worker                                                    run.positions(),
107*c8dee2aaSAndroid Build Coastguard Worker                                                    run.scaledRotations())) {
108*c8dee2aaSAndroid Build Coastguard Worker                 xform = SkRSXform::Make(sr.x(), sr.y(), pos.x(), pos.y());
109*c8dee2aaSAndroid Build Coastguard Worker             }
110*c8dee2aaSAndroid Build Coastguard Worker         }
111*c8dee2aaSAndroid Build Coastguard Worker         auto glyphIDs = run.glyphsIDs();
112*c8dee2aaSAndroid Build Coastguard Worker         memcpy(buffer.glyphs, glyphIDs.data(), glyphIDs.size_bytes());
113*c8dee2aaSAndroid Build Coastguard Worker     }
114*c8dee2aaSAndroid Build Coastguard Worker     return builder.make();
115*c8dee2aaSAndroid Build Coastguard Worker }
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker // -- GlyphRunBuilder ------------------------------------------------------------------------------
glyphrun_source_bounds(const SkFont & font,const SkPaint & paint,SkZip<const SkGlyphID,const SkPoint> source,SkSpan<const SkVector> scaledRotations)118*c8dee2aaSAndroid Build Coastguard Worker static SkRect glyphrun_source_bounds(
119*c8dee2aaSAndroid Build Coastguard Worker         const SkFont& font,
120*c8dee2aaSAndroid Build Coastguard Worker         const SkPaint& paint,
121*c8dee2aaSAndroid Build Coastguard Worker         SkZip<const SkGlyphID, const SkPoint> source,
122*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkVector> scaledRotations) {
123*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!source.empty());
124*c8dee2aaSAndroid Build Coastguard Worker     const SkRect fontBounds = SkFontPriv::GetFontBounds(font);
125*c8dee2aaSAndroid Build Coastguard Worker 
126*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const SkGlyphID> glyphIDs = source.get<0>();
127*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const SkPoint> positions = source.get<1>();
128*c8dee2aaSAndroid Build Coastguard Worker 
129*c8dee2aaSAndroid Build Coastguard Worker     if (fontBounds.isEmpty()) {
130*c8dee2aaSAndroid Build Coastguard Worker         // Empty font bounds are likely a font bug.  TightBounds has a better chance of
131*c8dee2aaSAndroid Build Coastguard Worker         // producing useful results in this case.
132*c8dee2aaSAndroid Build Coastguard Worker         auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(font, &paint);
133*c8dee2aaSAndroid Build Coastguard Worker         SkBulkGlyphMetrics metrics{strikeSpec};
134*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs);
135*c8dee2aaSAndroid Build Coastguard Worker         if (scaledRotations.empty()) {
136*c8dee2aaSAndroid Build Coastguard Worker             // No RSXForm data - glyphs x/y aligned.
137*c8dee2aaSAndroid Build Coastguard Worker             auto scaleAndTranslateRect =
138*c8dee2aaSAndroid Build Coastguard Worker                     [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) {
139*c8dee2aaSAndroid Build Coastguard Worker                         return SkRect::MakeLTRB(in.left()   * scale + pos.x(),
140*c8dee2aaSAndroid Build Coastguard Worker                                                 in.top()    * scale + pos.y(),
141*c8dee2aaSAndroid Build Coastguard Worker                                                 in.right()  * scale + pos.x(),
142*c8dee2aaSAndroid Build Coastguard Worker                                                 in.bottom() * scale + pos.y());
143*c8dee2aaSAndroid Build Coastguard Worker                     };
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker             SkRect bounds = SkRect::MakeEmpty();
146*c8dee2aaSAndroid Build Coastguard Worker             for (auto [pos, glyph] : SkMakeZip(positions, glyphs)) {
147*c8dee2aaSAndroid Build Coastguard Worker                 if (SkRect r = glyph->rect(); !r.isEmpty()) {
148*c8dee2aaSAndroid Build Coastguard Worker                     bounds.join(scaleAndTranslateRect(r, pos));
149*c8dee2aaSAndroid Build Coastguard Worker                 }
150*c8dee2aaSAndroid Build Coastguard Worker             }
151*c8dee2aaSAndroid Build Coastguard Worker             return bounds;
152*c8dee2aaSAndroid Build Coastguard Worker         } else {
153*c8dee2aaSAndroid Build Coastguard Worker             // RSXForm - glyphs can be any scale or rotation.
154*c8dee2aaSAndroid Build Coastguard Worker             SkRect bounds = SkRect::MakeEmpty();
155*c8dee2aaSAndroid Build Coastguard Worker             for (auto [pos, scaleRotate, glyph] : SkMakeZip(positions, scaledRotations, glyphs)) {
156*c8dee2aaSAndroid Build Coastguard Worker                 if (!glyph->rect().isEmpty()) {
157*c8dee2aaSAndroid Build Coastguard Worker                     SkMatrix xform = SkMatrix().setRSXform(
158*c8dee2aaSAndroid Build Coastguard Worker                             SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()});
159*c8dee2aaSAndroid Build Coastguard Worker                     xform.preScale(strikeToSourceScale, strikeToSourceScale);
160*c8dee2aaSAndroid Build Coastguard Worker                     bounds.join(xform.mapRect(glyph->rect()));
161*c8dee2aaSAndroid Build Coastguard Worker                 }
162*c8dee2aaSAndroid Build Coastguard Worker             }
163*c8dee2aaSAndroid Build Coastguard Worker             return bounds;
164*c8dee2aaSAndroid Build Coastguard Worker         }
165*c8dee2aaSAndroid Build Coastguard Worker     }
166*c8dee2aaSAndroid Build Coastguard Worker 
167*c8dee2aaSAndroid Build Coastguard Worker     // Use conservative bounds. All glyph have a box of fontBounds size.
168*c8dee2aaSAndroid Build Coastguard Worker     if (scaledRotations.empty()) {
169*c8dee2aaSAndroid Build Coastguard Worker         SkRect bounds;
170*c8dee2aaSAndroid Build Coastguard Worker         bounds.setBounds(positions.data(), SkCount(positions));
171*c8dee2aaSAndroid Build Coastguard Worker         bounds.fLeft   += fontBounds.left();
172*c8dee2aaSAndroid Build Coastguard Worker         bounds.fTop    += fontBounds.top();
173*c8dee2aaSAndroid Build Coastguard Worker         bounds.fRight  += fontBounds.right();
174*c8dee2aaSAndroid Build Coastguard Worker         bounds.fBottom += fontBounds.bottom();
175*c8dee2aaSAndroid Build Coastguard Worker         return bounds;
176*c8dee2aaSAndroid Build Coastguard Worker     } else {
177*c8dee2aaSAndroid Build Coastguard Worker         // RSXForm case glyphs can be any scale or rotation.
178*c8dee2aaSAndroid Build Coastguard Worker         SkRect bounds;
179*c8dee2aaSAndroid Build Coastguard Worker         bounds.setEmpty();
180*c8dee2aaSAndroid Build Coastguard Worker         for (auto [pos, scaleRotate] : SkMakeZip(positions, scaledRotations)) {
181*c8dee2aaSAndroid Build Coastguard Worker             const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()};
182*c8dee2aaSAndroid Build Coastguard Worker             bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds));
183*c8dee2aaSAndroid Build Coastguard Worker         }
184*c8dee2aaSAndroid Build Coastguard Worker         return bounds;
185*c8dee2aaSAndroid Build Coastguard Worker     }
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker 
makeGlyphRunList(const GlyphRun & run,const SkPaint & paint,SkPoint origin)188*c8dee2aaSAndroid Build Coastguard Worker GlyphRunList GlyphRunBuilder::makeGlyphRunList(
189*c8dee2aaSAndroid Build Coastguard Worker         const GlyphRun& run, const SkPaint& paint, SkPoint origin) {
190*c8dee2aaSAndroid Build Coastguard Worker     const SkRect bounds =
191*c8dee2aaSAndroid Build Coastguard Worker             glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations());
192*c8dee2aaSAndroid Build Coastguard Worker     return GlyphRunList{run, bounds, origin, this};
193*c8dee2aaSAndroid Build Coastguard Worker }
194*c8dee2aaSAndroid Build Coastguard Worker 
draw_text_positions(const SkFont & font,SkSpan<const SkGlyphID> glyphIDs,SkPoint origin,SkPoint * buffer)195*c8dee2aaSAndroid Build Coastguard Worker static SkSpan<const SkPoint> draw_text_positions(
196*c8dee2aaSAndroid Build Coastguard Worker         const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* buffer) {
197*c8dee2aaSAndroid Build Coastguard Worker     SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font);
198*c8dee2aaSAndroid Build Coastguard Worker     SkBulkGlyphMetrics storage{strikeSpec};
199*c8dee2aaSAndroid Build Coastguard Worker     auto glyphs = storage.glyphs(glyphIDs);
200*c8dee2aaSAndroid Build Coastguard Worker 
201*c8dee2aaSAndroid Build Coastguard Worker     SkPoint* positionCursor = buffer;
202*c8dee2aaSAndroid Build Coastguard Worker     SkPoint endOfLastGlyph = origin;
203*c8dee2aaSAndroid Build Coastguard Worker     for (auto glyph : glyphs) {
204*c8dee2aaSAndroid Build Coastguard Worker         *positionCursor++ = endOfLastGlyph;
205*c8dee2aaSAndroid Build Coastguard Worker         endOfLastGlyph += glyph->advanceVector();
206*c8dee2aaSAndroid Build Coastguard Worker     }
207*c8dee2aaSAndroid Build Coastguard Worker     return SkSpan(buffer, glyphIDs.size());
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker 
textToGlyphRunList(const SkFont & font,const SkPaint & paint,const void * bytes,size_t byteLength,SkPoint origin,SkTextEncoding encoding)210*c8dee2aaSAndroid Build Coastguard Worker const GlyphRunList& GlyphRunBuilder::textToGlyphRunList(
211*c8dee2aaSAndroid Build Coastguard Worker         const SkFont& font, const SkPaint& paint,
212*c8dee2aaSAndroid Build Coastguard Worker         const void* bytes, size_t byteLength, SkPoint origin,
213*c8dee2aaSAndroid Build Coastguard Worker         SkTextEncoding encoding) {
214*c8dee2aaSAndroid Build Coastguard Worker     auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding);
215*c8dee2aaSAndroid Build Coastguard Worker     SkRect bounds = SkRect::MakeEmpty();
216*c8dee2aaSAndroid Build Coastguard Worker     this->prepareBuffers(glyphIDs.size(), 0);
217*c8dee2aaSAndroid Build Coastguard Worker     if (!glyphIDs.empty()) {
218*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions);
219*c8dee2aaSAndroid Build Coastguard Worker         this->makeGlyphRun(font,
220*c8dee2aaSAndroid Build Coastguard Worker                            glyphIDs,
221*c8dee2aaSAndroid Build Coastguard Worker                            positions,
222*c8dee2aaSAndroid Build Coastguard Worker                            SkSpan<const char>{},
223*c8dee2aaSAndroid Build Coastguard Worker                            SkSpan<const uint32_t>{},
224*c8dee2aaSAndroid Build Coastguard Worker                            SkSpan<const SkVector>{});
225*c8dee2aaSAndroid Build Coastguard Worker         auto run = fGlyphRunListStorage.front();
226*c8dee2aaSAndroid Build Coastguard Worker         bounds = glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations());
227*c8dee2aaSAndroid Build Coastguard Worker     }
228*c8dee2aaSAndroid Build Coastguard Worker 
229*c8dee2aaSAndroid Build Coastguard Worker     return this->setGlyphRunList(nullptr, bounds, origin);
230*c8dee2aaSAndroid Build Coastguard Worker }
231*c8dee2aaSAndroid Build Coastguard Worker 
blobToGlyphRunList(const SkTextBlob & blob,SkPoint origin)232*c8dee2aaSAndroid Build Coastguard Worker const GlyphRunList& sktext::GlyphRunBuilder::blobToGlyphRunList(
233*c8dee2aaSAndroid Build Coastguard Worker         const SkTextBlob& blob, SkPoint origin) {
234*c8dee2aaSAndroid Build Coastguard Worker     // Pre-size all the buffers, so they don't move during processing.
235*c8dee2aaSAndroid Build Coastguard Worker     this->initialize(blob);
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker     SkPoint* positionCursor = fPositions;
238*c8dee2aaSAndroid Build Coastguard Worker     SkVector* scaledRotationsCursor = fScaledRotations;
239*c8dee2aaSAndroid Build Coastguard Worker     for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
240*c8dee2aaSAndroid Build Coastguard Worker         size_t runSize = it.glyphCount();
241*c8dee2aaSAndroid Build Coastguard Worker         if (runSize == 0 || !SkFontPriv::IsFinite(it.font())) {
242*c8dee2aaSAndroid Build Coastguard Worker             // If no glyphs or the font is not finite, don't add the run.
243*c8dee2aaSAndroid Build Coastguard Worker             continue;
244*c8dee2aaSAndroid Build Coastguard Worker         }
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker         const SkFont& font = it.font();
247*c8dee2aaSAndroid Build Coastguard Worker         auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize};
248*c8dee2aaSAndroid Build Coastguard Worker 
249*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkPoint> positions;
250*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkVector> scaledRotations;
251*c8dee2aaSAndroid Build Coastguard Worker         switch (it.positioning()) {
252*c8dee2aaSAndroid Build Coastguard Worker             case SkTextBlobRunIterator::kDefault_Positioning: {
253*c8dee2aaSAndroid Build Coastguard Worker                 positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor);
254*c8dee2aaSAndroid Build Coastguard Worker                 positionCursor += positions.size();
255*c8dee2aaSAndroid Build Coastguard Worker                 break;
256*c8dee2aaSAndroid Build Coastguard Worker             }
257*c8dee2aaSAndroid Build Coastguard Worker             case SkTextBlobRunIterator::kHorizontal_Positioning: {
258*c8dee2aaSAndroid Build Coastguard Worker                 positions = SkSpan(positionCursor, runSize);
259*c8dee2aaSAndroid Build Coastguard Worker                 for (auto x : SkSpan<const SkScalar>{it.pos(), glyphIDs.size()}) {
260*c8dee2aaSAndroid Build Coastguard Worker                     *positionCursor++ = SkPoint::Make(x, it.offset().y());
261*c8dee2aaSAndroid Build Coastguard Worker                 }
262*c8dee2aaSAndroid Build Coastguard Worker                 break;
263*c8dee2aaSAndroid Build Coastguard Worker             }
264*c8dee2aaSAndroid Build Coastguard Worker             case SkTextBlobRunIterator::kFull_Positioning: {
265*c8dee2aaSAndroid Build Coastguard Worker                 positions = SkSpan(it.points(), runSize);
266*c8dee2aaSAndroid Build Coastguard Worker                 break;
267*c8dee2aaSAndroid Build Coastguard Worker             }
268*c8dee2aaSAndroid Build Coastguard Worker             case SkTextBlobRunIterator::kRSXform_Positioning: {
269*c8dee2aaSAndroid Build Coastguard Worker                 positions = SkSpan(positionCursor, runSize);
270*c8dee2aaSAndroid Build Coastguard Worker                 scaledRotations = SkSpan(scaledRotationsCursor, runSize);
271*c8dee2aaSAndroid Build Coastguard Worker                 for (const SkRSXform& xform : SkSpan(it.xforms(), runSize)) {
272*c8dee2aaSAndroid Build Coastguard Worker                     *positionCursor++ = {xform.fTx, xform.fTy};
273*c8dee2aaSAndroid Build Coastguard Worker                     *scaledRotationsCursor++ = {xform.fSCos, xform.fSSin};
274*c8dee2aaSAndroid Build Coastguard Worker                 }
275*c8dee2aaSAndroid Build Coastguard Worker                 break;
276*c8dee2aaSAndroid Build Coastguard Worker             }
277*c8dee2aaSAndroid Build Coastguard Worker         }
278*c8dee2aaSAndroid Build Coastguard Worker 
279*c8dee2aaSAndroid Build Coastguard Worker         const uint32_t* clusters = it.clusters();
280*c8dee2aaSAndroid Build Coastguard Worker         this->makeGlyphRun(
281*c8dee2aaSAndroid Build Coastguard Worker                 font,
282*c8dee2aaSAndroid Build Coastguard Worker                 glyphIDs,
283*c8dee2aaSAndroid Build Coastguard Worker                 positions,
284*c8dee2aaSAndroid Build Coastguard Worker                 SkSpan<const char>(it.text(), it.textSize()),
285*c8dee2aaSAndroid Build Coastguard Worker                 SkSpan<const uint32_t>(clusters, clusters ? runSize : 0),
286*c8dee2aaSAndroid Build Coastguard Worker                 scaledRotations);
287*c8dee2aaSAndroid Build Coastguard Worker     }
288*c8dee2aaSAndroid Build Coastguard Worker 
289*c8dee2aaSAndroid Build Coastguard Worker     return this->setGlyphRunList(&blob, blob.bounds(), origin);
290*c8dee2aaSAndroid Build Coastguard Worker }
291*c8dee2aaSAndroid Build Coastguard Worker 
292*c8dee2aaSAndroid Build Coastguard Worker std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>>
convertRSXForm(SkSpan<const SkRSXform> xforms)293*c8dee2aaSAndroid Build Coastguard Worker GlyphRunBuilder::convertRSXForm(SkSpan<const SkRSXform> xforms) {
294*c8dee2aaSAndroid Build Coastguard Worker     const int count = SkCount(xforms);
295*c8dee2aaSAndroid Build Coastguard Worker     this->prepareBuffers(count, count);
296*c8dee2aaSAndroid Build Coastguard Worker     auto positions = SkSpan(fPositions.get(), count);
297*c8dee2aaSAndroid Build Coastguard Worker     auto scaledRotations = SkSpan(fScaledRotations.get(), count);
298*c8dee2aaSAndroid Build Coastguard Worker     for (auto [pos, sr, xform] : SkMakeZip(positions, scaledRotations, xforms)) {
299*c8dee2aaSAndroid Build Coastguard Worker         auto [scos, ssin, tx, ty] = xform;
300*c8dee2aaSAndroid Build Coastguard Worker         pos = {tx, ty};
301*c8dee2aaSAndroid Build Coastguard Worker         sr = {scos, ssin};
302*c8dee2aaSAndroid Build Coastguard Worker     }
303*c8dee2aaSAndroid Build Coastguard Worker     return {positions, scaledRotations};
304*c8dee2aaSAndroid Build Coastguard Worker }
305*c8dee2aaSAndroid Build Coastguard Worker 
initialize(const SkTextBlob & blob)306*c8dee2aaSAndroid Build Coastguard Worker void GlyphRunBuilder::initialize(const SkTextBlob& blob) {
307*c8dee2aaSAndroid Build Coastguard Worker     int positionCount = 0;
308*c8dee2aaSAndroid Build Coastguard Worker     int rsxFormCount = 0;
309*c8dee2aaSAndroid Build Coastguard Worker     for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
310*c8dee2aaSAndroid Build Coastguard Worker         if (it.positioning() != SkTextBlobRunIterator::kFull_Positioning) {
311*c8dee2aaSAndroid Build Coastguard Worker             positionCount += it.glyphCount();
312*c8dee2aaSAndroid Build Coastguard Worker         }
313*c8dee2aaSAndroid Build Coastguard Worker         if (it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) {
314*c8dee2aaSAndroid Build Coastguard Worker             rsxFormCount += it.glyphCount();
315*c8dee2aaSAndroid Build Coastguard Worker         }
316*c8dee2aaSAndroid Build Coastguard Worker     }
317*c8dee2aaSAndroid Build Coastguard Worker 
318*c8dee2aaSAndroid Build Coastguard Worker     prepareBuffers(positionCount, rsxFormCount);
319*c8dee2aaSAndroid Build Coastguard Worker }
320*c8dee2aaSAndroid Build Coastguard Worker 
prepareBuffers(int positionCount,int RSXFormCount)321*c8dee2aaSAndroid Build Coastguard Worker void GlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) {
322*c8dee2aaSAndroid Build Coastguard Worker     if (positionCount > fMaxTotalRunSize) {
323*c8dee2aaSAndroid Build Coastguard Worker         fMaxTotalRunSize = positionCount;
324*c8dee2aaSAndroid Build Coastguard Worker         fPositions.reset(fMaxTotalRunSize);
325*c8dee2aaSAndroid Build Coastguard Worker     }
326*c8dee2aaSAndroid Build Coastguard Worker 
327*c8dee2aaSAndroid Build Coastguard Worker     if (RSXFormCount > fMaxScaledRotations) {
328*c8dee2aaSAndroid Build Coastguard Worker         fMaxScaledRotations = RSXFormCount;
329*c8dee2aaSAndroid Build Coastguard Worker         fScaledRotations.reset(RSXFormCount);
330*c8dee2aaSAndroid Build Coastguard Worker     }
331*c8dee2aaSAndroid Build Coastguard Worker 
332*c8dee2aaSAndroid Build Coastguard Worker     fGlyphRunListStorage.clear();
333*c8dee2aaSAndroid Build Coastguard Worker }
334*c8dee2aaSAndroid Build Coastguard Worker 
textToGlyphIDs(const SkFont & font,const void * bytes,size_t byteLength,SkTextEncoding encoding)335*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const SkGlyphID> GlyphRunBuilder::textToGlyphIDs(
336*c8dee2aaSAndroid Build Coastguard Worker         const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) {
337*c8dee2aaSAndroid Build Coastguard Worker     if (encoding != SkTextEncoding::kGlyphID) {
338*c8dee2aaSAndroid Build Coastguard Worker         int count = font.countText(bytes, byteLength, encoding);
339*c8dee2aaSAndroid Build Coastguard Worker         if (count > 0) {
340*c8dee2aaSAndroid Build Coastguard Worker             fScratchGlyphIDs.resize(count);
341*c8dee2aaSAndroid Build Coastguard Worker             font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count);
342*c8dee2aaSAndroid Build Coastguard Worker             return SkSpan(fScratchGlyphIDs);
343*c8dee2aaSAndroid Build Coastguard Worker         } else {
344*c8dee2aaSAndroid Build Coastguard Worker             return SkSpan<const SkGlyphID>();
345*c8dee2aaSAndroid Build Coastguard Worker         }
346*c8dee2aaSAndroid Build Coastguard Worker     } else {
347*c8dee2aaSAndroid Build Coastguard Worker         return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2);
348*c8dee2aaSAndroid Build Coastguard Worker     }
349*c8dee2aaSAndroid Build Coastguard Worker }
350*c8dee2aaSAndroid Build Coastguard Worker 
makeGlyphRun(const SkFont & font,SkSpan<const SkGlyphID> glyphIDs,SkSpan<const SkPoint> positions,SkSpan<const char> text,SkSpan<const uint32_t> clusters,SkSpan<const SkVector> scaledRotations)351*c8dee2aaSAndroid Build Coastguard Worker void GlyphRunBuilder::makeGlyphRun(
352*c8dee2aaSAndroid Build Coastguard Worker         const SkFont& font,
353*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkGlyphID> glyphIDs,
354*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkPoint> positions,
355*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const char> text,
356*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const uint32_t> clusters,
357*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const SkVector> scaledRotations) {
358*c8dee2aaSAndroid Build Coastguard Worker 
359*c8dee2aaSAndroid Build Coastguard Worker     // Ignore empty runs.
360*c8dee2aaSAndroid Build Coastguard Worker     if (!glyphIDs.empty()) {
361*c8dee2aaSAndroid Build Coastguard Worker         fGlyphRunListStorage.emplace_back(
362*c8dee2aaSAndroid Build Coastguard Worker                 font,
363*c8dee2aaSAndroid Build Coastguard Worker                 positions,
364*c8dee2aaSAndroid Build Coastguard Worker                 glyphIDs,
365*c8dee2aaSAndroid Build Coastguard Worker                 text,
366*c8dee2aaSAndroid Build Coastguard Worker                 clusters,
367*c8dee2aaSAndroid Build Coastguard Worker                 scaledRotations);
368*c8dee2aaSAndroid Build Coastguard Worker     }
369*c8dee2aaSAndroid Build Coastguard Worker }
370*c8dee2aaSAndroid Build Coastguard Worker 
setGlyphRunList(const SkTextBlob * blob,const SkRect & bounds,SkPoint origin)371*c8dee2aaSAndroid Build Coastguard Worker const GlyphRunList& sktext::GlyphRunBuilder::setGlyphRunList(
372*c8dee2aaSAndroid Build Coastguard Worker         const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) {
373*c8dee2aaSAndroid Build Coastguard Worker     fGlyphRunList.emplace(blob, bounds, origin, SkSpan(fGlyphRunListStorage), this);
374*c8dee2aaSAndroid Build Coastguard Worker     return fGlyphRunList.value();
375*c8dee2aaSAndroid Build Coastguard Worker }
376*c8dee2aaSAndroid Build Coastguard Worker }  // namespace sktext
377