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