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