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