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