Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
GlyphRun.cpp
Go to the documentation of this file.
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"
15#include "src/core/SkFontPriv.h"
16#include "src/core/SkGlyph.h"
19
20#include <cstring>
21#include <initializer_list>
22
23class SkPaint;
24
25namespace sktext {
26// -- GlyphRun -------------------------------------------------------------------------------------
28 SkSpan<const SkPoint> positions,
32 SkSpan<const SkVector> scaledRotations)
33 : fSource{SkMakeZip(glyphIDs, positions)}
34 , fText{text}
35 , fClusters{clusters}
36 , fScaledRotations{scaledRotations}
37 , fFont{font} {}
38
39GlyphRun::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 ---------------------------------------------------------------------------------
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
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
67uint64_t GlyphRunList::uniqueID() const {
68 return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID()
70}
71
72bool 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
81void 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
88sk_sp<SkTextBlob> GlyphRunList::makeBlob() const {
89 SkTextBlobBuilder builder;
90 for (auto& run : *this) {
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 ------------------------------------------------------------------------------
120 const SkFont& font,
121 const SkPaint& paint,
123 SkSpan<const SkVector> scaledRotations) {
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
189GlyphRunList 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
197 const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* buffer) {
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
211const 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,
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
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()) {
254 positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor);
255 positionCursor += positions.size();
256 break;
257 }
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 }
266 positions = SkSpan(it.points(), runSize);
267 break;
268 }
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
293std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>>
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
307void 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
322void 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
336SkSpan<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 {
346 }
347 } else {
348 return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2);
349 }
350}
351
352void GlyphRunBuilder::makeGlyphRun(
353 const SkFont& font,
355 SkSpan<const SkPoint> positions,
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
372const 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
uint16_t glyphs[5]
int count
SkPoint pos
#define SkASSERT(cond)
Definition SkAssert.h:116
SkTextEncoding
Definition SkFontTypes.h:11
@ kGlyphID
uses two byte words to represent glyph indices
constexpr int SkCount(const Container &c)
Definition SkTLogic.h:54
uint16_t SkGlyphID
Definition SkTypes.h:179
static constexpr uint32_t SK_InvalidUniqueID
Definition SkTypes.h:196
constexpr auto SkMakeZip(Ts &&... ts)
Definition SkZip.h:212
bool empty() const
Definition SkBitmap.h:210
static bool IsFinite(const SkFont &font)
Definition SkFontPriv.h:78
static SkRect GetFontBounds(const SkFont &)
Definition SkFont.cpp:354
@ kSubpixelAntiAlias
glyph positioned in pixel using transparency
SkMatrix & setRSXform(const SkRSXform &rsxForm)
Definition SkMatrix.cpp:420
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:315
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
constexpr T * data() const
Definition SkSpan_impl.h:94
constexpr bool empty() const
Definition SkSpan_impl.h:96
constexpr size_t size() const
Definition SkSpan_impl.h:95
static SkStrikeSpec MakeWithNoDevice(const SkFont &font, const SkPaint *paint=nullptr)
static std::tuple< SkStrikeSpec, SkScalar > MakeCanonicalized(const SkFont &font, const SkPaint *paint=nullptr)
const SkRect & bounds() const
Definition SkTextBlob.h:53
Definition SkZip.h:25
T * reset(size_t count=0)
std::tuple< SkSpan< const SkPoint >, SkSpan< const SkVector > > convertRSXForm(SkSpan< const SkRSXform > xforms)
Definition GlyphRun.cpp:294
const GlyphRunList & blobToGlyphRunList(const SkTextBlob &blob, SkPoint origin)
Definition GlyphRun.cpp:233
GlyphRunList(const SkTextBlob *blob, SkRect bounds, SkPoint origin, SkSpan< const GlyphRun > glyphRunList, GlyphRunBuilder *builder)
Definition GlyphRun.cpp:46
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)
Definition GlyphRun.cpp:27
const Paint & paint
SkBitmap source
Definition examples.cpp:28
static const uint8_t buffer[]
std::u16string text
double x
Optional< SkRect > bounds
Definition SkRecords.h:189
font
Font Metadata and Metrics.
Definition run.py:1
static SkRect glyphrun_source_bounds(const SkFont &font, const SkPaint &paint, SkZip< const SkGlyphID, const SkPoint > source, SkSpan< const SkVector > scaledRotations)
Definition GlyphRun.cpp:119
static SkSpan< const SkPoint > draw_text_positions(const SkFont &font, SkSpan< const SkGlyphID > glyphIDs, SkPoint origin, SkPoint *buffer)
Definition GlyphRun.cpp:196
const Scalar scale
static constexpr SkPoint Make(float x, float y)
constexpr float y() const
constexpr float x() const
static SkRSXform Make(SkScalar scos, SkScalar ssin, SkScalar tx, SkScalar ty)
Definition SkRSXform.h:24
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
constexpr float left() const
Definition SkRect.h:734
constexpr float top() const
Definition SkRect.h:741
constexpr float right() const
Definition SkRect.h:748
bool isEmpty() const
Definition SkRect.h:693
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
constexpr float bottom() const
Definition SkRect.h:755