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