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/SkGraphics.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkTypeface.h"
13 #include "src/core/SkEnumerate.h"
14 #include "src/core/SkScalerContext.h"
15 
use_or_generate_metrics(const SkFontMetrics * metrics,SkScalerContext * context)16 static SkFontMetrics use_or_generate_metrics(
17         const SkFontMetrics* metrics, SkScalerContext* context) {
18     SkFontMetrics answer;
19     if (metrics) {
20         answer = *metrics;
21     } else {
22         context->getFontMetrics(&answer);
23     }
24     return answer;
25 }
26 
SkScalerCache(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,const SkFontMetrics * fontMetrics)27 SkScalerCache::SkScalerCache(
28     const SkDescriptor& desc,
29     std::unique_ptr<SkScalerContext> scaler,
30     const SkFontMetrics* fontMetrics)
31         : fDesc{desc}
32         , fScalerContext{std::move(scaler)}
33         , fFontMetrics{use_or_generate_metrics(fontMetrics, fScalerContext.get())}
34         , fRoundingSpec{fScalerContext->isSubpixel(),
35                         fScalerContext->computeAxisAlignmentForHText()} {
36     SkASSERT(fScalerContext != nullptr);
37 }
38 
glyph(SkPackedGlyphID packedGlyphID)39 std::tuple<SkGlyph*, size_t> SkScalerCache::glyph(SkPackedGlyphID packedGlyphID) {
40     auto [digest, size] = this->digest(packedGlyphID);
41     return {fGlyphForIndex[digest.index()], size};
42 }
43 
digest(SkPackedGlyphID packedGlyphID)44 std::tuple<SkGlyphDigest, size_t> SkScalerCache::digest(SkPackedGlyphID packedGlyphID) {
45     SkGlyphDigest* digest = fDigestForPackedGlyphID.find(packedGlyphID);
46 
47     if (digest != nullptr) {
48         return {*digest, 0};
49     }
50 
51     SkGlyph* glyph = fAlloc.make<SkGlyph>(packedGlyphID);
52     fScalerContext->getMetrics(glyph);
53     return {this->addGlyph(glyph), sizeof(SkGlyph)};
54 }
55 
addGlyph(SkGlyph * glyph)56 SkGlyphDigest SkScalerCache::addGlyph(SkGlyph* glyph) {
57     size_t index = fGlyphForIndex.size();
58     SkGlyphDigest digest = SkGlyphDigest{index, *glyph};
59     fDigestForPackedGlyphID.set(glyph->getPackedID(), digest);
60     fGlyphForIndex.push_back(glyph);
61     return digest;
62 }
63 
preparePath(SkGlyph * glyph)64 std::tuple<const SkPath*, size_t> SkScalerCache::preparePath(SkGlyph* glyph) {
65     size_t delta = 0;
66     if (glyph->setPath(&fAlloc, fScalerContext.get())) {
67         delta = glyph->path()->approximateBytesUsed();
68     }
69     return {glyph->path(), delta};
70 }
71 
mergePath(SkGlyph * glyph,const SkPath * path)72 std::tuple<const SkPath*, size_t> SkScalerCache::mergePath(SkGlyph* glyph, const SkPath* path) {
73     SkAutoMutexExclusive lock{fMu};
74     size_t pathDelta = 0;
75     if (glyph->setPath(&fAlloc, path)) {
76         pathDelta = glyph->path()->approximateBytesUsed();
77     }
78     return {glyph->path(), pathDelta};
79 }
80 
getDescriptor() const81 const SkDescriptor& SkScalerCache::getDescriptor() const {
82     return *fDesc.getDesc();
83 }
84 
countCachedGlyphs() const85 int SkScalerCache::countCachedGlyphs() const {
86     SkAutoMutexExclusive lock(fMu);
87     return fDigestForPackedGlyphID.count();
88 }
89 
internalPrepare(SkSpan<const SkGlyphID> glyphIDs,PathDetail pathDetail,const SkGlyph ** results)90 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::internalPrepare(
91         SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
92     const SkGlyph** cursor = results;
93     size_t delta = 0;
94     for (auto glyphID : glyphIDs) {
95         auto [glyph, size] = this->glyph(SkPackedGlyphID{glyphID});
96         delta += size;
97         if (pathDetail == kMetricsAndPath) {
98             auto [_, pathSize] = this->preparePath(glyph);
99             delta += pathSize;
100         }
101         *cursor++ = glyph;
102     }
103 
104     return {{results, glyphIDs.size()}, delta};
105 }
106 
prepareImage(SkGlyph * glyph)107 std::tuple<const void*, size_t> SkScalerCache::prepareImage(SkGlyph* glyph) {
108     size_t delta = 0;
109     if (glyph->setImage(&fAlloc, fScalerContext.get())) {
110         delta = glyph->imageSize();
111     }
112     return {glyph->image(), delta};
113 }
114 
mergeGlyphAndImage(SkPackedGlyphID toID,const SkGlyph & from)115 std::tuple<SkGlyph*, size_t> SkScalerCache::mergeGlyphAndImage(
116         SkPackedGlyphID toID, const SkGlyph& from) {
117     SkAutoMutexExclusive lock{fMu};
118     // TODO(herb): remove finding the glyph when we are sure there are no glyph collisions.
119     SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID);
120     if (digest != nullptr) {
121         // Since there is no search for replacement glyphs, this glyph should not exist yet.
122         SkDEBUGFAIL("This implies adding to an existing glyph. This should not happen.");
123 
124         // Just return what we have. The invariants have already been cast in stone.
125         return {fGlyphForIndex[digest->index()], 0};
126     } else {
127         SkGlyph* glyph = fAlloc.make<SkGlyph>(toID);
128         size_t delta = glyph->setMetricsAndImage(&fAlloc, from);
129         (void)this->addGlyph(glyph);
130         return {glyph, sizeof(SkGlyph) + delta};
131     }
132 }
133 
metrics(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])134 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::metrics(
135         SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
136     SkAutoMutexExclusive lock{fMu};
137     auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsOnly, results);
138     return {glyphs, delta};
139 }
140 
preparePaths(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])141 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::preparePaths(
142         SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
143     SkAutoMutexExclusive lock{fMu};
144     auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsAndPath, results);
145     return {glyphs, delta};
146 }
147 
prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,const SkGlyph * results[])148 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::prepareImages(
149         SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
150     const SkGlyph** cursor = results;
151     SkAutoMutexExclusive lock{fMu};
152     size_t delta = 0;
153     for (auto glyphID : glyphIDs) {
154         auto[glyph, glyphSize] = this->glyph(glyphID);
155         auto[_, imageSize] = this->prepareImage(glyph);
156         delta += glyphSize + imageSize;
157         *cursor++ = glyph;
158     }
159 
160     return {{results, glyphIDs.size()}, delta};
161 }
162 
163 template <typename Fn>
commonFilterLoop(SkDrawableGlyphBuffer * drawables,Fn && fn)164 size_t SkScalerCache::commonFilterLoop(SkDrawableGlyphBuffer* drawables, Fn&& fn) {
165     size_t total = 0;
166     for (auto [i, packedID, pos] : SkMakeEnumerate(drawables->input())) {
167         if (SkScalarsAreFinite(pos.x(), pos.y())) {
168             auto [digest, size] = this->digest(packedID);
169             total += size;
170             if (!digest.isEmpty()) {
171                 fn(i, digest, pos);
172             }
173         }
174     }
175     return total;
176 }
177 
prepareForDrawingMasksCPU(SkDrawableGlyphBuffer * drawables)178 size_t SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables) {
179     SkAutoMutexExclusive lock{fMu};
180     size_t imageDelta = 0;
181     size_t delta = this->commonFilterLoop(drawables,
182         [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
183             // If the glyph is too large, then no image is created.
184             SkGlyph* glyph = fGlyphForIndex[digest.index()];
185             auto [image, imageSize] = this->prepareImage(glyph);
186             if (image != nullptr) {
187                 drawables->push_back(glyph, i);
188                 imageDelta += imageSize;
189             }
190         });
191 
192     return delta + imageDelta;
193 }
194 
195 // Note: this does not actually fill out the image. That happens at atlas building time.
prepareForMaskDrawing(SkDrawableGlyphBuffer * drawables,SkSourceGlyphBuffer * rejects)196 size_t SkScalerCache::prepareForMaskDrawing(
197         SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
198     SkAutoMutexExclusive lock{fMu};
199     size_t delta = this->commonFilterLoop(drawables,
200         [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
201             if (digest.canDrawAsMask()) {
202                 drawables->push_back(fGlyphForIndex[digest.index()], i);
203             } else {
204                 rejects->reject(i);
205             }
206         });
207 
208     return delta;
209 }
210 
prepareForSDFTDrawing(SkDrawableGlyphBuffer * drawables,SkSourceGlyphBuffer * rejects)211 size_t SkScalerCache::prepareForSDFTDrawing(
212         SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
213     SkAutoMutexExclusive lock{fMu};
214     size_t delta = this->commonFilterLoop(drawables,
215         [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
216             if (digest.canDrawAsSDFT()) {
217                 drawables->push_back(fGlyphForIndex[digest.index()], i);
218             } else {
219                 rejects->reject(i);
220             }
221         });
222 
223     return delta;
224 }
225 
prepareForPathDrawing(SkDrawableGlyphBuffer * drawables,SkSourceGlyphBuffer * rejects)226 size_t SkScalerCache::prepareForPathDrawing(
227         SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
228     SkAutoMutexExclusive lock{fMu};
229     size_t pathDelta = 0;
230     size_t delta = this->commonFilterLoop(drawables,
231         [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
232             SkGlyph* glyph = fGlyphForIndex[digest.index()];
233             if (!digest.isColor()) {
234                 auto [path, pathSize] = this->preparePath(glyph);
235                 pathDelta += pathSize;
236                 if (path != nullptr) {
237                     // Save off the path to draw later.
238                     drawables->push_back(path, i);
239                 } else {
240                     // Glyph does not have a path. It is probably bitmap only.
241                     rejects->reject(i, glyph->maxDimension());
242                 }
243             } else {
244                 // Glyph is color.
245                 rejects->reject(i, glyph->maxDimension());
246             }
247         });
248 
249     return delta + pathDelta;
250 }
251 
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,SkGlyph * glyph,SkScalar * array,int * count)252 void SkScalerCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
253         SkGlyph* glyph, SkScalar* array, int* count) {
254     SkAutoMutexExclusive lock{fMu};
255     glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
256 }
257 
dump() const258 void SkScalerCache::dump() const {
259     SkAutoMutexExclusive lock{fMu};
260     const SkTypeface* face = fScalerContext->getTypeface();
261     const SkScalerContextRec& rec = fScalerContext->getRec();
262     SkMatrix matrix;
263     rec.getSingleMatrix(&matrix);
264     matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
265     SkString name;
266     face->getFamilyName(&name);
267 
268     SkString msg;
269     SkFontStyle style = face->fontStyle();
270     msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
271                face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
272                rec.dump().c_str(), fDigestForPackedGlyphID.count());
273     SkDebugf("%s\n", msg.c_str());
274 }
275 
276