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