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