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