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