2 * Copyright 2018 The Android Open Source Project
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/core/SkGlyphRun.h"
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/core/SkDevice.h"
15 #include "src/core/SkFontPriv.h"
16 #include "src/core/SkScalerCache.h"
17 #include "src/core/SkStrikeCache.h"
18 #include "src/core/SkStrikeSpec.h"
19 #include "src/core/SkTextBlobPriv.h"
20 #include "src/core/SkUtils.h"
22 // -- SkGlyphRun -----------------------------------------------------------------------------------
23 SkGlyphRun::SkGlyphRun(const SkFont& font,
24 SkSpan<const SkPoint> positions,
25 SkSpan<const SkGlyphID> glyphIDs,
26 SkSpan<const char> text,
27 SkSpan<const uint32_t> clusters,
28 SkSpan<const SkVector> scaledRotations)
29 : fSource{SkMakeZip(glyphIDs, positions)}
32 , fScaledRotations{scaledRotations}
35 SkGlyphRun::SkGlyphRun(const SkGlyphRun& that, const SkFont& font)
36 : fSource{that.fSource}
38 , fClusters{that.fClusters}
41 SkRect SkGlyphRun::sourceBounds(const SkPaint& paint) const {
42 SkASSERT(this->runSize() > 0);
43 const SkRect fontBounds = SkFontPriv::GetFontBounds(fFont);
45 if (fontBounds.isEmpty()) {
46 // Empty font bounds are likely a font bug. TightBounds has a better chance of
47 // producing useful results in this case.
48 auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(fFont, &paint);
49 SkBulkGlyphMetrics metrics{strikeSpec};
50 SkSpan<const SkGlyph*> glyphs = metrics.glyphs(this->glyphsIDs());
51 if (fScaledRotations.empty()) {
52 // No RSXForm data - glyphs x/y aligned.
53 auto scaleAndTranslateRect =
54 [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) {
55 return SkRect::MakeLTRB(in.left() * scale + pos.x(),
56 in.top() * scale + pos.y(),
57 in.right() * scale + pos.x(),
58 in.bottom() * scale + pos.y());
61 SkRect bounds = SkRect::MakeEmpty();
62 for (auto [pos, glyph] : SkMakeZip(this->positions(), glyphs)) {
63 if (SkRect r = glyph->rect(); !r.isEmpty()) {
64 bounds.join(scaleAndTranslateRect(r, pos));
69 // RSXForm - glyphs can be any scale or rotation.
70 SkRect bounds = SkRect::MakeEmpty();
71 for (auto [pos, scaleRotate, glyph] :
72 SkMakeZip(this->positions(), fScaledRotations, glyphs)) {
73 if (!glyph->rect().isEmpty()) {
74 SkMatrix xform = SkMatrix().setRSXform(
75 SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()});
76 xform.preScale(strikeToSourceScale, strikeToSourceScale);
77 bounds.join(xform.mapRect(glyph->rect()));
84 // Use conservative bounds. All glyph have a box of fontBounds size.
85 if (fScaledRotations.empty()) {
87 bounds.setBounds(this->positions().data(), SkCount(this->positions()));
88 bounds.fLeft += fontBounds.left();
89 bounds.fTop += fontBounds.top();
90 bounds.fRight += fontBounds.right();
91 bounds.fBottom += fontBounds.bottom();
94 // RSXForm case glyphs can be any scale or rotation.
97 for (auto [pos, scaleRotate] : SkMakeZip(this->positions(), fScaledRotations)) {
98 const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()};
99 bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds));
105 // -- SkGlyphRunList -------------------------------------------------------------------------------
106 SkGlyphRunList::SkGlyphRunList(
107 const SkTextBlob* blob,
110 SkSpan<const SkGlyphRun> glyphRunList,
111 SkGlyphRunBuilder* builder)
112 : fGlyphRuns{glyphRunList}
113 , fOriginalTextBlob{blob}
114 , fSourceBounds{bounds}
116 , fBuilder{builder} {}
118 SkGlyphRunList::SkGlyphRunList(const SkGlyphRun& glyphRun,
119 const SkRect& bounds,
121 SkGlyphRunBuilder* builder)
122 : fGlyphRuns{SkSpan<const SkGlyphRun>{&glyphRun, 1}}
123 , fOriginalTextBlob{nullptr}
124 , fSourceBounds{bounds}
126 , fBuilder{builder} {}
128 uint64_t SkGlyphRunList::uniqueID() const {
129 return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID()
130 : SK_InvalidUniqueID;
133 bool SkGlyphRunList::anyRunsLCD() const {
134 for (const auto& r : fGlyphRuns) {
135 if (r.font().getEdging() == SkFont::Edging::kSubpixelAntiAlias) {
142 void SkGlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID) const {
143 SkASSERT(fOriginalTextBlob != nullptr);
144 fOriginalTextBlob->notifyAddedToCache(cacheID);
147 sk_sp<SkTextBlob> SkGlyphRunList::makeBlob() const {
148 SkTextBlobBuilder builder;
149 for (auto& run : *this) {
150 SkTextBlobBuilder::RunBuffer buffer;
151 if (run.scaledRotations().empty()) {
152 if (run.text().empty()) {
153 buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr);
155 buffer = builder.allocRunTextPos(run.font(), run.runSize(), run.text().size(), nullptr);
156 auto text = run.text();
157 memcpy(buffer.utf8text, text.data(), text.size_bytes());
158 auto clusters = run.clusters();
159 memcpy(buffer.clusters, clusters.data(), clusters.size_bytes());
161 auto positions = run.positions();
162 memcpy(buffer.points(), positions.data(), positions.size_bytes());
164 buffer = builder.allocRunRSXform(run.font(), run.runSize());
165 for (auto [xform, pos, sr] : SkMakeZip(buffer.xforms(),
167 run.scaledRotations())) {
168 xform = SkRSXform::Make(sr.x(), sr.y(), pos.x(), pos.y());
171 auto glyphIDs = run.glyphsIDs();
172 memcpy(buffer.glyphs, glyphIDs.data(), glyphIDs.size_bytes());
174 return builder.make();
177 // -- SkGlyphRunBuilder ----------------------------------------------------------------------------
178 SkGlyphRunList SkGlyphRunBuilder::makeGlyphRunList(
179 const SkGlyphRun& run, SkRect bounds, SkPoint origin) {
180 return SkGlyphRunList{run, bounds, origin, this};
183 static SkSpan<const SkPoint> draw_text_positions(
184 const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* buffer) {
185 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font);
186 SkBulkGlyphMetrics storage{strikeSpec};
187 auto glyphs = storage.glyphs(glyphIDs);
189 SkPoint* positionCursor = buffer;
190 SkPoint endOfLastGlyph = origin;
191 for (auto glyph : glyphs) {
192 *positionCursor++ = endOfLastGlyph;
193 endOfLastGlyph += glyph->advanceVector();
195 return SkMakeSpan(buffer, glyphIDs.size());
198 const SkGlyphRunList& SkGlyphRunBuilder::textToGlyphRunList(
199 const SkFont& font, const SkPaint& paint,
200 const void* bytes, size_t byteLength, SkPoint origin,
201 SkTextEncoding encoding) {
202 auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding);
203 SkRect bounds = SkRect::MakeEmpty();
204 this->prepareBuffers(glyphIDs.size(), 0);
205 if (!glyphIDs.empty()) {
206 SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions);
207 this->makeGlyphRun(font,
210 SkSpan<const char>{},
211 SkSpan<const uint32_t>{},
212 SkSpan<const SkVector>{});
213 bounds = fGlyphRunListStorage.front().sourceBounds(paint);
216 return this->setGlyphRunList(nullptr, bounds.makeOffset(origin), origin);
219 const SkGlyphRunList& SkGlyphRunBuilder::blobToGlyphRunList(
220 const SkTextBlob& blob, SkPoint origin) {
221 // Pre-size all the buffers, so they don't move during processing.
222 this->initialize(blob);
224 SkPoint* positionCursor = fPositions;
225 SkVector* scaledRotationsCursor = fScaledRotations;
226 for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
227 size_t runSize = it.glyphCount();
228 if (runSize == 0 || !SkFontPriv::IsFinite(it.font())) {
229 // If no glyphs or the font is not finite, don't add the run.
233 const SkFont& font = it.font();
234 auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize};
236 SkSpan<const SkPoint> positions;
237 SkSpan<const SkVector> scaledRotations;
238 switch (it.positioning()) {
239 case SkTextBlobRunIterator::kDefault_Positioning: {
240 positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor);
241 positionCursor += positions.size();
244 case SkTextBlobRunIterator::kHorizontal_Positioning: {
245 positions = SkMakeSpan(positionCursor, runSize);
246 for (auto x : SkSpan<const SkScalar>{it.pos(), glyphIDs.size()}) {
247 *positionCursor++ = SkPoint::Make(x, it.offset().y());
251 case SkTextBlobRunIterator::kFull_Positioning: {
252 positions = SkMakeSpan(it.points(), runSize);
255 case SkTextBlobRunIterator::kRSXform_Positioning: {
256 positions = SkMakeSpan(positionCursor, runSize);
257 scaledRotations = SkMakeSpan(scaledRotationsCursor, runSize);
258 for (const SkRSXform& xform : SkMakeSpan(it.xforms(), runSize)) {
259 *positionCursor++ = {xform.fTx, xform.fTy};
260 *scaledRotationsCursor++ = {xform.fSCos, xform.fSSin};
270 SkSpan<const char>(it.text(), it.textSize()),
271 SkSpan<const uint32_t>(it.clusters(), runSize),
275 return this->setGlyphRunList(&blob, blob.bounds().makeOffset(origin), origin);
278 std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>>
279 SkGlyphRunBuilder::convertRSXForm(SkSpan<const SkRSXform> xforms) {
280 const int count = SkCount(xforms);
281 this->prepareBuffers(count, count);
282 auto positions = SkMakeSpan(fPositions.get(), count);
283 auto scaledRotations = SkMakeSpan(fScaledRotations.get(), count);
284 for (auto [pos, sr, xform] : SkMakeZip(positions, scaledRotations, xforms)) {
285 auto [scos, ssin, tx, ty] = xform;
289 return {positions, scaledRotations};
292 void SkGlyphRunBuilder::initialize(const SkTextBlob& blob) {
293 int positionCount = 0;
294 int rsxFormCount = 0;
295 for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) {
296 if (it.positioning() != SkTextBlobRunIterator::kFull_Positioning) {
297 positionCount += it.glyphCount();
299 if (it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) {
300 rsxFormCount += it.glyphCount();
304 prepareBuffers(positionCount, rsxFormCount);
307 void SkGlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) {
308 if (positionCount > fMaxTotalRunSize) {
309 fMaxTotalRunSize = positionCount;
310 fPositions.reset(fMaxTotalRunSize);
313 if (RSXFormCount > fMaxScaledRotations) {
314 fMaxScaledRotations = RSXFormCount;
315 fScaledRotations.reset(RSXFormCount);
318 fGlyphRunListStorage.clear();
321 SkSpan<const SkGlyphID> SkGlyphRunBuilder::textToGlyphIDs(
322 const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) {
323 if (encoding != SkTextEncoding::kGlyphID) {
324 int count = font.countText(bytes, byteLength, encoding);
326 fScratchGlyphIDs.resize(count);
327 font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count);
328 return SkMakeSpan(fScratchGlyphIDs);
330 return SkSpan<const SkGlyphID>();
333 return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2);
337 void SkGlyphRunBuilder::makeGlyphRun(
339 SkSpan<const SkGlyphID> glyphIDs,
340 SkSpan<const SkPoint> positions,
341 SkSpan<const char> text,
342 SkSpan<const uint32_t> clusters,
343 SkSpan<const SkVector> scaledRotations) {
345 // Ignore empty runs.
346 if (!glyphIDs.empty()) {
347 fGlyphRunListStorage.emplace_back(
357 const SkGlyphRunList& SkGlyphRunBuilder::setGlyphRunList(
358 const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) {
359 fGlyphRunList.emplace(blob, bounds, origin, SkMakeSpan(fGlyphRunListStorage), this);
360 return fGlyphRunList.value();