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