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