1 /*
2 * Copyright 2006 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/core/SkScalerCache.h"
9
10 #include "include/core/SkDrawable.h"
11 #include "include/core/SkGraphics.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkTypeface.h"
14 #include "src/core/SkEnumerate.h"
15 #include "src/core/SkScalerContext.h"
16
use_or_generate_metrics(const SkFontMetrics * metrics,SkScalerContext * context)17 static SkFontMetrics use_or_generate_metrics(
18 const SkFontMetrics* metrics, SkScalerContext* context) {
19 SkFontMetrics answer;
20 if (metrics) {
21 answer = *metrics;
22 } else {
23 context->getFontMetrics(&answer);
24 }
25 return answer;
26 }
27
SkScalerCache(std::unique_ptr<SkScalerContext> scaler,const SkFontMetrics * fontMetrics)28 SkScalerCache::SkScalerCache(
29 std::unique_ptr<SkScalerContext> scaler,
30 const SkFontMetrics* fontMetrics)
31 : fScalerContext{std::move(scaler)}
32 , fFontMetrics{use_or_generate_metrics(fontMetrics, fScalerContext.get())}
33 , fRoundingSpec{fScalerContext->isSubpixel(),
34 fScalerContext->computeAxisAlignmentForHText()} {
35 SkASSERT(fScalerContext != nullptr);
36 }
37
glyph(SkPackedGlyphID packedGlyphID)38 std::tuple<SkGlyph*, size_t> SkScalerCache::glyph(SkPackedGlyphID packedGlyphID) {
39 auto [digest, size] = this->digest(packedGlyphID);
40 return {fGlyphForIndex[digest.index()], size};
41 }
42
digest(SkPackedGlyphID packedGlyphID)43 std::tuple<SkGlyphDigest, size_t> SkScalerCache::digest(SkPackedGlyphID packedGlyphID) {
44 SkGlyphDigest* digest = fDigestForPackedGlyphID.find(packedGlyphID.value());
45
46 if (digest != nullptr) {
47 return {*digest, 0};
48 }
49
50 SkGlyph* glyph = fAlloc.make<SkGlyph>(fScalerContext->makeGlyph(packedGlyphID, &fAlloc));
51 return {this->addGlyph(glyph), sizeof(SkGlyph)};
52 }
53
addGlyph(SkGlyph * glyph)54 SkGlyphDigest SkScalerCache::addGlyph(SkGlyph* glyph) {
55 size_t index = fGlyphForIndex.size();
56 SkGlyphDigest digest = SkGlyphDigest{index, *glyph};
57 fDigestForPackedGlyphID.set(digest);
58 fGlyphForIndex.push_back(glyph);
59 return digest;
60 }
61
preparePath(SkGlyph * glyph)62 std::tuple<const SkPath*, size_t> SkScalerCache::preparePath(SkGlyph* glyph) {
63 size_t delta = 0;
64 if (glyph->setPath(&fAlloc, fScalerContext.get())) {
65 delta = glyph->path()->approximateBytesUsed();
66 }
67 return {glyph->path(), delta};
68 }
69
mergePath(SkGlyph * glyph,const SkPath * path,bool hairline)70 std::tuple<const SkPath*, size_t> SkScalerCache::mergePath(
71 SkGlyph* glyph, const SkPath* path, bool hairline) {
72 SkAutoMutexExclusive lock{fMu};
73 size_t pathDelta = 0;
74 if (glyph->setPathHasBeenCalled()) {
75 SkDEBUGFAIL("Re-adding path to existing glyph. This should not happen.");
76 }
77 if (glyph->setPath(&fAlloc, path, hairline)) {
78 pathDelta = glyph->path()->approximateBytesUsed();
79 }
80 return {glyph->path(), pathDelta};
81 }
82
prepareDrawable(SkGlyph * glyph)83 std::tuple<SkDrawable*, size_t> SkScalerCache::prepareDrawable(SkGlyph* glyph) {
84 size_t delta = 0;
85 if (glyph->setDrawable(&fAlloc, fScalerContext.get())) {
86 delta = glyph->drawable()->approximateBytesUsed();
87 SkASSERT(delta > 0);
88 }
89 return {glyph->drawable(), delta};
90 }
91
mergeDrawable(SkGlyph * glyph,sk_sp<SkDrawable> drawable)92 std::tuple<SkDrawable*, size_t> SkScalerCache::mergeDrawable(SkGlyph* glyph,
93 sk_sp<SkDrawable> drawable) {
94 SkAutoMutexExclusive lock{fMu};
95 size_t delta = 0;
96 if (glyph->setDrawableHasBeenCalled()) {
97 SkDEBUGFAIL("Re-adding drawable to existing glyph. This should not happen.");
98 }
99 if (glyph->setDrawable(&fAlloc, std::move(drawable))) {
100 delta = glyph->drawable()->approximateBytesUsed();
101 SkASSERT(delta > 0);
102 }
103 return {glyph->drawable(), delta};
104 }
105
countCachedGlyphs() const106 int SkScalerCache::countCachedGlyphs() const {
107 SkAutoMutexExclusive lock(fMu);
108 return fDigestForPackedGlyphID.count();
109 }
110
internalPrepare(SkSpan<const SkGlyphID> glyphIDs,PathDetail pathDetail,const SkGlyph ** results)111 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::internalPrepare(
112 SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
113 const SkGlyph** cursor = results;
114 size_t delta = 0;
115 for (auto glyphID : glyphIDs) {
116 auto [glyph, size] = this->glyph(SkPackedGlyphID{glyphID});
117 delta += size;
118 if (pathDetail == kMetricsAndPath) {
119 auto [_, pathSize] = this->preparePath(glyph);
120 delta += pathSize;
121 }
122 *cursor++ = glyph;
123 }
124
125 return {{results, glyphIDs.size()}, delta};
126 }
127
prepareImage(SkGlyph * glyph)128 std::tuple<const void*, size_t> SkScalerCache::prepareImage(SkGlyph* glyph) {
129 size_t delta = 0;
130 if (glyph->setImage(&fAlloc, fScalerContext.get())) {
131 delta = glyph->imageSize();
132 }
133 return {glyph->image(), delta};
134 }
135
mergeGlyphAndImage(SkPackedGlyphID toID,const SkGlyph & from)136 std::tuple<SkGlyph*, size_t> SkScalerCache::mergeGlyphAndImage(
137 SkPackedGlyphID toID, const SkGlyph& from) {
138 SkAutoMutexExclusive lock{fMu};
139 // TODO(herb): remove finding the glyph when setting the metrics and image are separated
140 SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID.value());
141 if (digest != nullptr) {
142 SkGlyph* to = fGlyphForIndex[digest->index()];
143 size_t delta = 0;
144 if (from.setImageHasBeenCalled()) {
145 if (to->setImageHasBeenCalled()) {
146 // Should never set an image on a glyph which already has an image.
147 SkDEBUGFAIL("Re-adding image to existing glyph. This should not happen.");
148 }
149 // TODO: assert that any metrics on `from` are the same.
150 delta = to->setMetricsAndImage(&fAlloc, from);
151 }
152 return {to, delta};
153 } else {
154 SkGlyph* glyph = fAlloc.make<SkGlyph>(toID);
155 size_t delta = glyph->setMetricsAndImage(&fAlloc, from);
156 (void)this->addGlyph(glyph);
157 return {glyph, sizeof(SkGlyph) + delta};
158 }
159 }
160
metrics(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])161 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::metrics(
162 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
163 SkAutoMutexExclusive lock{fMu};
164 auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsOnly, results);
165 return {glyphs, delta};
166 }
167
preparePaths(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])168 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::preparePaths(
169 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
170 SkAutoMutexExclusive lock{fMu};
171 auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsAndPath, results);
172 return {glyphs, delta};
173 }
174
prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,const SkGlyph * results[])175 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::prepareImages(
176 SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
177 const SkGlyph** cursor = results;
178 SkAutoMutexExclusive lock{fMu};
179 size_t delta = 0;
180 for (auto glyphID : glyphIDs) {
181 auto[glyph, glyphSize] = this->glyph(glyphID);
182 auto[_, imageSize] = this->prepareImage(glyph);
183 delta += glyphSize + imageSize;
184 *cursor++ = glyph;
185 }
186
187 return {{results, glyphIDs.size()}, delta};
188 }
189
190 template <typename Fn>
commonFilterLoop(SkDrawableGlyphBuffer * accepted,Fn && fn)191 size_t SkScalerCache::commonFilterLoop(SkDrawableGlyphBuffer* accepted, Fn&& fn) {
192 size_t total = 0;
193 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
194 if (SkScalarsAreFinite(pos.x(), pos.y())) {
195 auto [digest, size] = this->digest(packedID);
196 total += size;
197 if (!digest.isEmpty()) {
198 fn(i, digest, pos);
199 }
200 }
201 }
202 return total;
203 }
204
prepareForDrawingMasksCPU(SkDrawableGlyphBuffer * accepted)205 size_t SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* accepted) {
206 SkAutoMutexExclusive lock{fMu};
207 size_t imageDelta = 0;
208 size_t delta = this->commonFilterLoop(accepted,
209 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
210 // If the glyph is too large, then no image is created.
211 SkGlyph* glyph = fGlyphForIndex[digest.index()];
212 auto [image, imageSize] = this->prepareImage(glyph);
213 if (image != nullptr) {
214 accepted->accept(glyph, i);
215 imageDelta += imageSize;
216 }
217 });
218
219 return delta + imageDelta;
220 }
221
222 // Note: this does not actually fill out the image. That happens at atlas building time.
prepareForMaskDrawing(SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)223 size_t SkScalerCache::prepareForMaskDrawing(
224 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
225 SkAutoMutexExclusive lock{fMu};
226 size_t delta = this->commonFilterLoop(accepted,
227 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
228 // N.B. this must have the same behavior as RemoteStrike::prepareForMaskDrawing.
229 if (digest.canDrawAsMask()) {
230 accepted->accept(fGlyphForIndex[digest.index()], i);
231 } else {
232 rejected->reject(i, digest.maxDimension());
233 }
234 });
235
236 return delta;
237 }
238
prepareForSDFTDrawing(SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)239 size_t SkScalerCache::prepareForSDFTDrawing(
240 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
241 SkAutoMutexExclusive lock{fMu};
242 size_t delta = this->commonFilterLoop(accepted,
243 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
244 if (digest.canDrawAsSDFT()) {
245 accepted->accept(fGlyphForIndex[digest.index()], i);
246 } else {
247 // Assume whatever follows SDF doesn't care about the maximum rejected size.
248 rejected->reject(i);
249 }
250 });
251
252 return delta;
253 }
254
prepareForPathDrawing(SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)255 size_t SkScalerCache::prepareForPathDrawing(
256 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
257 SkAutoMutexExclusive lock{fMu};
258 size_t pathDelta = 0;
259 size_t delta = this->commonFilterLoop(accepted,
260 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
261 SkGlyph* glyph = fGlyphForIndex[digest.index()];
262 auto [path, pathSize] = this->preparePath(glyph);
263 pathDelta += pathSize;
264 if (path != nullptr) {
265 // Save off the path to draw later.
266 accepted->accept(path, i);
267 } else {
268 // Glyph does not have a path.
269 rejected->reject(i, digest.maxDimension());
270 }
271 });
272
273 return delta + pathDelta;
274 }
275
prepareForDrawableDrawing(SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)276 size_t SkScalerCache::prepareForDrawableDrawing(
277 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
278 SkAutoMutexExclusive lock{fMu};
279 size_t drawableDelta = 0;
280 size_t delta = this->commonFilterLoop(accepted,
281 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
282 SkGlyph* glyph = fGlyphForIndex[digest.index()];
283 auto [drawable, drawableSize] = this->prepareDrawable(glyph);
284 drawableDelta += drawableSize;
285 if (drawable != nullptr) {
286 // Save off the drawable to draw later.
287 accepted->accept(drawable, i);
288 } else {
289 // Glyph does not have a drawable.
290 rejected->reject(i, glyph->maxDimension());
291 }
292 });
293
294 return delta + drawableDelta;
295 }
296
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,SkGlyph * glyph,SkScalar * array,int * count)297 void SkScalerCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
298 SkGlyph* glyph, SkScalar* array, int* count) {
299 SkAutoMutexExclusive lock{fMu};
300 glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
301 }
302
dump() const303 void SkScalerCache::dump() const {
304 SkAutoMutexExclusive lock{fMu};
305 const SkTypeface* face = fScalerContext->getTypeface();
306 const SkScalerContextRec& rec = fScalerContext->getRec();
307 SkMatrix matrix;
308 rec.getSingleMatrix(&matrix);
309 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
310 SkString name;
311 face->getFamilyName(&name);
312
313 SkString msg;
314 SkFontStyle style = face->fontStyle();
315 msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
316 face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
317 rec.dump().c_str(), fDigestForPackedGlyphID.count());
318 SkDebugf("%s\n", msg.c_str());
319 }
320
321