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 | |
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 | |
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 ------------------------------------------------------------------------------- |
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 | |
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 | |
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 | |
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 | |
73 | const SkDescriptor& SkScalerCache::getDescriptor() const { |
74 | return *fDesc.getDesc(); |
75 | } |
76 | |
77 | int SkScalerCache::countCachedGlyphs() const { |
78 | SkAutoMutexExclusive lock(fMu); |
79 | return fGlyphMap.count(); |
80 | } |
81 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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> |
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 | |
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. |
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 | |
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 | |
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 | |
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 | |
244 | 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 | |