Flutter Engine
The Flutter Engine
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
40 : fSource{that.fSource}
41 , fText{that.fText}
42 , fClusters{that.fClusters}
43 , fFont{font} {}
44
45// -- GlyphRunList ---------------------------------------------------------------------------------
48 SkPoint origin,
49 SkSpan<const GlyphRun> glyphRunList,
51 : fGlyphRuns{glyphRunList}
52 , fOriginalTextBlob{blob}
53 , fSourceBounds{bounds}
54 , fOrigin{origin}
55 , fBuilder{builder} {}
56
58 const SkRect& bounds,
59 SkPoint origin,
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 {
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
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.
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()) {
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.
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);
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]
Definition: FontMgrTest.cpp:46
int count
Definition: FontMgrTest.cpp:50
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkTextEncoding
Definition: SkFontTypes.h:11
@ kGlyphID
uses two byte words to represent glyph indices
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
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
Definition: SkFont.h:35
@ 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
Definition: SkMatrix.cpp:1141
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)
Definition: SkTemplates.h:296
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
Definition: color_source.cc:38
SkBitmap source
Definition: examples.cpp:28
std::u16string text
double x
Optional< SkRect > bounds
Definition: SkRecords.h:189
PODArray< SkRSXform > xforms
Definition: SkRecords.h:332
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
font
Font Metadata and Metrics.
Definition: run.py:1
def run(cmd)
Definition: run.py:14
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)
Definition: SkPoint_impl.h:173
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
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