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