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