/src/skia/src/text/GlyphRun.cpp
Line | Count | Source (jump to first uncovered line) |
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 ------------------------------------------------------------------------------------- |
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 | 54.1k | , fFont{font} {} |
38 | | |
39 | | GlyphRun::GlyphRun(const GlyphRun& that, const SkFont& font) |
40 | | : fSource{that.fSource} |
41 | | , fText{that.fText} |
42 | | , fClusters{that.fClusters} |
43 | 0 | , fFont{font} {} |
44 | | |
45 | | // -- GlyphRunList --------------------------------------------------------------------------------- |
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 | 15.2k | , fBuilder{builder} {} |
56 | | |
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 | 23.5k | , fBuilder{builder} {} |
66 | | |
67 | 1.18k | uint64_t GlyphRunList::uniqueID() const { |
68 | 1.18k | return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID() |
69 | 1.18k | : SK_InvalidUniqueID; |
70 | 1.18k | } |
71 | | |
72 | 1.18k | bool GlyphRunList::anyRunsLCD() const { |
73 | 7.11k | for (const auto& r : fGlyphRuns) { |
74 | 7.11k | if (r.font().getEdging() == SkFont::Edging::kSubpixelAntiAlias) { |
75 | 17 | return true; |
76 | 17 | } |
77 | 7.11k | } |
78 | 1.16k | return false; |
79 | 1.18k | } |
80 | | |
81 | | void GlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID, |
82 | 816 | SkTextBlob::PurgeDelegate pd) const { |
83 | 816 | SkASSERT(fOriginalTextBlob != nullptr); |
84 | 816 | SkASSERT(pd != nullptr); |
85 | 816 | fOriginalTextBlob->notifyAddedToCache(cacheID, pd); |
86 | 816 | } |
87 | | |
88 | 1.86k | sk_sp<SkTextBlob> GlyphRunList::makeBlob() const { |
89 | 1.86k | SkTextBlobBuilder builder; |
90 | 1.86k | for (auto& run : *this) { |
91 | 1.86k | SkTextBlobBuilder::RunBuffer buffer; |
92 | 1.86k | if (run.scaledRotations().empty()) { |
93 | 1.86k | if (run.text().empty()) { |
94 | 1.86k | buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr); |
95 | 1.86k | } else { |
96 | 0 | buffer = builder.allocRunTextPos(run.font(), run.runSize(), run.text().size(), nullptr); |
97 | 0 | auto text = run.text(); |
98 | 0 | memcpy(buffer.utf8text, text.data(), text.size_bytes()); |
99 | 0 | auto clusters = run.clusters(); |
100 | 0 | memcpy(buffer.clusters, clusters.data(), clusters.size_bytes()); |
101 | 0 | } |
102 | 1.86k | auto positions = run.positions(); |
103 | 1.86k | memcpy(buffer.points(), positions.data(), positions.size_bytes()); |
104 | 1.86k | } else { |
105 | 0 | buffer = builder.allocRunRSXform(run.font(), run.runSize()); |
106 | 0 | for (auto [xform, pos, sr] : SkMakeZip(buffer.xforms(), |
107 | 0 | run.positions(), |
108 | 0 | run.scaledRotations())) { |
109 | 0 | xform = SkRSXform::Make(sr.x(), sr.y(), pos.x(), pos.y()); |
110 | 0 | } |
111 | 0 | } |
112 | 1.86k | auto glyphIDs = run.glyphsIDs(); |
113 | 1.86k | memcpy(buffer.glyphs, glyphIDs.data(), glyphIDs.size_bytes()); |
114 | 1.86k | } |
115 | 1.86k | return builder.make(); |
116 | 1.86k | } |
117 | | |
118 | | // -- GlyphRunBuilder ------------------------------------------------------------------------------ |
119 | | static SkRect glyphrun_source_bounds( |
120 | | const SkFont& font, |
121 | | const SkPaint& paint, |
122 | | SkZip<const SkGlyphID, const SkPoint> source, |
123 | 30.5k | SkSpan<const SkVector> scaledRotations) { |
124 | 30.5k | SkASSERT(!source.empty()); |
125 | 30.5k | const SkRect fontBounds = SkFontPriv::GetFontBounds(font); |
126 | | |
127 | 30.5k | SkSpan<const SkGlyphID> glyphIDs = source.get<0>(); |
128 | 30.5k | SkSpan<const SkPoint> positions = source.get<1>(); |
129 | | |
130 | 30.5k | 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 | 14.5k | auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(font, &paint); |
134 | 14.5k | SkBulkGlyphMetrics metrics{strikeSpec}; |
135 | 14.5k | SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs); |
136 | 14.5k | if (scaledRotations.empty()) { |
137 | | // No RSXForm data - glyphs x/y aligned. |
138 | 14.5k | auto scaleAndTranslateRect = |
139 | 14.5k | [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) { |
140 | 13.0k | return SkRect::MakeLTRB(in.left() * scale + pos.x(), |
141 | 13.0k | in.top() * scale + pos.y(), |
142 | 13.0k | in.right() * scale + pos.x(), |
143 | 13.0k | in.bottom() * scale + pos.y()); |
144 | 13.0k | }; GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>)::$_0::operator()(SkRect const&, SkPoint const&) const Line | Count | Source | 139 | 13.0k | [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) { | 140 | 13.0k | return SkRect::MakeLTRB(in.left() * scale + pos.x(), | 141 | 13.0k | in.top() * scale + pos.y(), | 142 | 13.0k | in.right() * scale + pos.x(), | 143 | 13.0k | in.bottom() * scale + pos.y()); | 144 | 13.0k | }; |
Unexecuted instantiation: GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>)::$_1::operator()(SkRect const&, SkPoint const&) const |
145 | | |
146 | 14.5k | SkRect bounds = SkRect::MakeEmpty(); |
147 | 63.3k | for (auto [pos, glyph] : SkMakeZip(positions, glyphs)) { |
148 | 63.3k | if (SkRect r = glyph->rect(); !r.isEmpty()) { |
149 | 13.0k | bounds.join(scaleAndTranslateRect(r, pos)); |
150 | 13.0k | } |
151 | 63.3k | } |
152 | 14.5k | return bounds; |
153 | 14.5k | } else { |
154 | | // RSXForm - glyphs can be any scale or rotation. |
155 | 0 | SkRect bounds = SkRect::MakeEmpty(); |
156 | 0 | for (auto [pos, scaleRotate, glyph] : SkMakeZip(positions, scaledRotations, glyphs)) { |
157 | 0 | if (!glyph->rect().isEmpty()) { |
158 | 0 | SkMatrix xform = SkMatrix().setRSXform( |
159 | 0 | SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}); |
160 | 0 | xform.preScale(strikeToSourceScale, strikeToSourceScale); |
161 | 0 | bounds.join(xform.mapRect(glyph->rect())); |
162 | 0 | } |
163 | 0 | } |
164 | 0 | return bounds; |
165 | 0 | } |
166 | 14.5k | } |
167 | | |
168 | | // Use conservative bounds. All glyph have a box of fontBounds size. |
169 | 16.0k | if (scaledRotations.empty()) { |
170 | 16.0k | SkRect bounds; |
171 | 16.0k | bounds.setBounds(positions.data(), SkCount(positions)); |
172 | 16.0k | bounds.fLeft += fontBounds.left(); |
173 | 16.0k | bounds.fTop += fontBounds.top(); |
174 | 16.0k | bounds.fRight += fontBounds.right(); |
175 | 16.0k | bounds.fBottom += fontBounds.bottom(); |
176 | 16.0k | return bounds; |
177 | 16.0k | } else { |
178 | | // RSXForm case glyphs can be any scale or rotation. |
179 | 0 | SkRect bounds; |
180 | 0 | bounds.setEmpty(); |
181 | 0 | for (auto [pos, scaleRotate] : SkMakeZip(positions, scaledRotations)) { |
182 | 0 | const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}; |
183 | 0 | bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds)); |
184 | 0 | } |
185 | 0 | return bounds; |
186 | 0 | } |
187 | 16.0k | } GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>) Line | Count | Source | 123 | 30.5k | SkSpan<const SkVector> scaledRotations) { | 124 | 30.5k | SkASSERT(!source.empty()); | 125 | 30.5k | const SkRect fontBounds = SkFontPriv::GetFontBounds(font); | 126 | | | 127 | 30.5k | SkSpan<const SkGlyphID> glyphIDs = source.get<0>(); | 128 | 30.5k | SkSpan<const SkPoint> positions = source.get<1>(); | 129 | | | 130 | 30.5k | 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 | 14.5k | auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(font, &paint); | 134 | 14.5k | SkBulkGlyphMetrics metrics{strikeSpec}; | 135 | 14.5k | SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs); | 136 | 14.5k | if (scaledRotations.empty()) { | 137 | | // No RSXForm data - glyphs x/y aligned. | 138 | 14.5k | auto scaleAndTranslateRect = | 139 | 14.5k | [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) { | 140 | 14.5k | return SkRect::MakeLTRB(in.left() * scale + pos.x(), | 141 | 14.5k | in.top() * scale + pos.y(), | 142 | 14.5k | in.right() * scale + pos.x(), | 143 | 14.5k | in.bottom() * scale + pos.y()); | 144 | 14.5k | }; | 145 | | | 146 | 14.5k | SkRect bounds = SkRect::MakeEmpty(); | 147 | 63.3k | for (auto [pos, glyph] : SkMakeZip(positions, glyphs)) { | 148 | 63.3k | if (SkRect r = glyph->rect(); !r.isEmpty()) { | 149 | 13.0k | bounds.join(scaleAndTranslateRect(r, pos)); | 150 | 13.0k | } | 151 | 63.3k | } | 152 | 14.5k | return bounds; | 153 | 14.5k | } else { | 154 | | // RSXForm - glyphs can be any scale or rotation. | 155 | 0 | SkRect bounds = SkRect::MakeEmpty(); | 156 | 0 | for (auto [pos, scaleRotate, glyph] : SkMakeZip(positions, scaledRotations, glyphs)) { | 157 | 0 | if (!glyph->rect().isEmpty()) { | 158 | 0 | SkMatrix xform = SkMatrix().setRSXform( | 159 | 0 | SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}); | 160 | 0 | xform.preScale(strikeToSourceScale, strikeToSourceScale); | 161 | 0 | bounds.join(xform.mapRect(glyph->rect())); | 162 | 0 | } | 163 | 0 | } | 164 | 0 | return bounds; | 165 | 0 | } | 166 | 14.5k | } | 167 | | | 168 | | // Use conservative bounds. All glyph have a box of fontBounds size. | 169 | 16.0k | if (scaledRotations.empty()) { | 170 | 16.0k | SkRect bounds; | 171 | 16.0k | bounds.setBounds(positions.data(), SkCount(positions)); | 172 | 16.0k | bounds.fLeft += fontBounds.left(); | 173 | 16.0k | bounds.fTop += fontBounds.top(); | 174 | 16.0k | bounds.fRight += fontBounds.right(); | 175 | 16.0k | bounds.fBottom += fontBounds.bottom(); | 176 | 16.0k | return bounds; | 177 | 16.0k | } else { | 178 | | // RSXForm case glyphs can be any scale or rotation. | 179 | 0 | SkRect bounds; | 180 | 0 | bounds.setEmpty(); | 181 | 0 | for (auto [pos, scaleRotate] : SkMakeZip(positions, scaledRotations)) { | 182 | 0 | const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}; | 183 | 0 | bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds)); | 184 | 0 | } | 185 | 0 | return bounds; | 186 | 0 | } | 187 | 16.0k | } |
Unexecuted instantiation: GlyphRun.cpp:sktext::glyphrun_source_bounds(SkFont const&, SkPaint const&, SkZip<unsigned short const, SkPoint const>, SkSpan<SkPoint const>) |
188 | | |
189 | | GlyphRunList GlyphRunBuilder::makeGlyphRunList( |
190 | 23.5k | const GlyphRun& run, const SkPaint& paint, SkPoint origin) { |
191 | 23.5k | const SkRect bounds = |
192 | 23.5k | glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations()); |
193 | 23.5k | return GlyphRunList{run, bounds, origin, this}; |
194 | 23.5k | } |
195 | | |
196 | | static SkSpan<const SkPoint> draw_text_positions( |
197 | 22.1k | const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* buffer) { |
198 | 22.1k | SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font); |
199 | 22.1k | SkBulkGlyphMetrics storage{strikeSpec}; |
200 | 22.1k | auto glyphs = storage.glyphs(glyphIDs); |
201 | | |
202 | 22.1k | SkPoint* positionCursor = buffer; |
203 | 22.1k | SkPoint endOfLastGlyph = origin; |
204 | 261k | for (auto glyph : glyphs) { |
205 | 261k | *positionCursor++ = endOfLastGlyph; |
206 | 261k | endOfLastGlyph += glyph->advanceVector(); |
207 | 261k | } |
208 | 22.1k | return SkSpan(buffer, glyphIDs.size()); |
209 | 22.1k | } |
210 | | |
211 | | const GlyphRunList& GlyphRunBuilder::textToGlyphRunList( |
212 | | const SkFont& font, const SkPaint& paint, |
213 | | const void* bytes, size_t byteLength, SkPoint origin, |
214 | 7.11k | SkTextEncoding encoding) { |
215 | 7.11k | auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding); |
216 | 7.11k | SkRect bounds = SkRect::MakeEmpty(); |
217 | 7.11k | this->prepareBuffers(glyphIDs.size(), 0); |
218 | 7.11k | if (!glyphIDs.empty()) { |
219 | 7.01k | SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions); |
220 | 7.01k | this->makeGlyphRun(font, |
221 | 7.01k | glyphIDs, |
222 | 7.01k | positions, |
223 | 7.01k | SkSpan<const char>{}, |
224 | 7.01k | SkSpan<const uint32_t>{}, |
225 | 7.01k | SkSpan<const SkVector>{}); |
226 | 7.01k | auto run = fGlyphRunListStorage.front(); |
227 | 7.01k | bounds = glyphrun_source_bounds(run.font(), paint, run.source(), run.scaledRotations()); |
228 | 7.01k | } |
229 | | |
230 | 7.11k | return this->setGlyphRunList(nullptr, bounds, origin); |
231 | 7.11k | } |
232 | | |
233 | | const GlyphRunList& sktext::GlyphRunBuilder::blobToGlyphRunList( |
234 | 8.12k | const SkTextBlob& blob, SkPoint origin) { |
235 | | // Pre-size all the buffers, so they don't move during processing. |
236 | 8.12k | this->initialize(blob); |
237 | | |
238 | 8.12k | SkPoint* positionCursor = fPositions; |
239 | 8.12k | SkVector* scaledRotationsCursor = fScaledRotations; |
240 | 36.3k | for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { |
241 | 28.2k | size_t runSize = it.glyphCount(); |
242 | 28.2k | if (runSize == 0 || !SkFontPriv::IsFinite(it.font())) { |
243 | | // If no glyphs or the font is not finite, don't add the run. |
244 | 189 | continue; |
245 | 189 | } |
246 | | |
247 | 28.0k | const SkFont& font = it.font(); |
248 | 28.0k | auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize}; |
249 | | |
250 | 28.0k | SkSpan<const SkPoint> positions; |
251 | 28.0k | SkSpan<const SkVector> scaledRotations; |
252 | 28.0k | switch (it.positioning()) { |
253 | 15.1k | case SkTextBlobRunIterator::kDefault_Positioning: { |
254 | 15.1k | positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor); |
255 | 15.1k | positionCursor += positions.size(); |
256 | 15.1k | break; |
257 | 0 | } |
258 | 1.57k | case SkTextBlobRunIterator::kHorizontal_Positioning: { |
259 | 1.57k | positions = SkSpan(positionCursor, runSize); |
260 | 33.4k | for (auto x : SkSpan<const SkScalar>{it.pos(), glyphIDs.size()}) { |
261 | 33.4k | *positionCursor++ = SkPoint::Make(x, it.offset().y()); |
262 | 33.4k | } |
263 | 1.57k | break; |
264 | 0 | } |
265 | 10.0k | case SkTextBlobRunIterator::kFull_Positioning: { |
266 | 10.0k | positions = SkSpan(it.points(), runSize); |
267 | 10.0k | break; |
268 | 0 | } |
269 | 1.28k | case SkTextBlobRunIterator::kRSXform_Positioning: { |
270 | 1.28k | positions = SkSpan(positionCursor, runSize); |
271 | 1.28k | scaledRotations = SkSpan(scaledRotationsCursor, runSize); |
272 | 23.1k | for (const SkRSXform& xform : SkSpan(it.xforms(), runSize)) { |
273 | 23.1k | *positionCursor++ = {xform.fTx, xform.fTy}; |
274 | 23.1k | *scaledRotationsCursor++ = {xform.fSCos, xform.fSSin}; |
275 | 23.1k | } |
276 | 1.28k | break; |
277 | 0 | } |
278 | 28.0k | } |
279 | | |
280 | 28.0k | const uint32_t* clusters = it.clusters(); |
281 | 28.0k | this->makeGlyphRun( |
282 | 28.0k | font, |
283 | 28.0k | glyphIDs, |
284 | 28.0k | positions, |
285 | 28.0k | SkSpan<const char>(it.text(), it.textSize()), |
286 | 28.0k | SkSpan<const uint32_t>(clusters, clusters ? runSize : 0), |
287 | 28.0k | scaledRotations); |
288 | 28.0k | } |
289 | | |
290 | 8.12k | return this->setGlyphRunList(&blob, blob.bounds(), origin); |
291 | 8.12k | } |
292 | | |
293 | | std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>> |
294 | 0 | GlyphRunBuilder::convertRSXForm(SkSpan<const SkRSXform> xforms) { |
295 | 0 | const int count = SkCount(xforms); |
296 | 0 | this->prepareBuffers(count, count); |
297 | 0 | auto positions = SkSpan(fPositions.get(), count); |
298 | 0 | auto scaledRotations = SkSpan(fScaledRotations.get(), count); |
299 | 0 | for (auto [pos, sr, xform] : SkMakeZip(positions, scaledRotations, xforms)) { |
300 | 0 | auto [scos, ssin, tx, ty] = xform; |
301 | 0 | pos = {tx, ty}; |
302 | 0 | sr = {scos, ssin}; |
303 | 0 | } |
304 | 0 | return {positions, scaledRotations}; |
305 | 0 | } |
306 | | |
307 | 8.12k | void GlyphRunBuilder::initialize(const SkTextBlob& blob) { |
308 | 8.12k | int positionCount = 0; |
309 | 8.12k | int rsxFormCount = 0; |
310 | 36.3k | for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { |
311 | 28.2k | if (it.positioning() != SkTextBlobRunIterator::kFull_Positioning) { |
312 | 18.0k | positionCount += it.glyphCount(); |
313 | 18.0k | } |
314 | 28.2k | if (it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) { |
315 | 1.28k | rsxFormCount += it.glyphCount(); |
316 | 1.28k | } |
317 | 28.2k | } |
318 | | |
319 | 8.12k | prepareBuffers(positionCount, rsxFormCount); |
320 | 8.12k | } |
321 | | |
322 | 15.2k | void GlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) { |
323 | 15.2k | if (positionCount > fMaxTotalRunSize) { |
324 | 9.69k | fMaxTotalRunSize = positionCount; |
325 | 9.69k | fPositions.reset(fMaxTotalRunSize); |
326 | 9.69k | } |
327 | | |
328 | 15.2k | if (RSXFormCount > fMaxScaledRotations) { |
329 | 422 | fMaxScaledRotations = RSXFormCount; |
330 | 422 | fScaledRotations.reset(RSXFormCount); |
331 | 422 | } |
332 | | |
333 | 15.2k | fGlyphRunListStorage.clear(); |
334 | 15.2k | } |
335 | | |
336 | | SkSpan<const SkGlyphID> GlyphRunBuilder::textToGlyphIDs( |
337 | 7.11k | const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) { |
338 | 7.11k | if (encoding != SkTextEncoding::kGlyphID) { |
339 | 7.11k | int count = font.countText(bytes, byteLength, encoding); |
340 | 7.11k | if (count > 0) { |
341 | 7.01k | fScratchGlyphIDs.resize(count); |
342 | 7.01k | font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count); |
343 | 7.01k | return SkSpan(fScratchGlyphIDs); |
344 | 7.01k | } else { |
345 | 96 | return SkSpan<const SkGlyphID>(); |
346 | 96 | } |
347 | 7.11k | } else { |
348 | 0 | return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2); |
349 | 0 | } |
350 | 7.11k | } |
351 | | |
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 | 35.0k | SkSpan<const SkVector> scaledRotations) { |
359 | | |
360 | | // Ignore empty runs. |
361 | 35.0k | if (!glyphIDs.empty()) { |
362 | 35.0k | fGlyphRunListStorage.emplace_back( |
363 | 35.0k | font, |
364 | 35.0k | positions, |
365 | 35.0k | glyphIDs, |
366 | 35.0k | text, |
367 | 35.0k | clusters, |
368 | 35.0k | scaledRotations); |
369 | 35.0k | } |
370 | 35.0k | } |
371 | | |
372 | | const GlyphRunList& sktext::GlyphRunBuilder::setGlyphRunList( |
373 | 15.2k | const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) { |
374 | 15.2k | fGlyphRunList.emplace(blob, bounds, origin, SkSpan(fGlyphRunListStorage), this); |
375 | 15.2k | return fGlyphRunList.value(); |
376 | 15.2k | } |
377 | | } // namespace sktext |