2 * Copyright 2006 The Android Open Source Project
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/core/SkScalerCache.h"
10 #include "include/core/SkDrawable.h"
11 #include "include/core/SkGraphics.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkTypeface.h"
14 #include "src/core/SkEnumerate.h"
15 #include "src/core/SkScalerContext.h"
17 static SkFontMetrics use_or_generate_metrics(
18 const SkFontMetrics* metrics, SkScalerContext* context) {
23 context->getFontMetrics(&answer);
28 SkScalerCache::SkScalerCache(
29 std::unique_ptr<SkScalerContext> scaler,
30 const SkFontMetrics* fontMetrics)
31 : fScalerContext{std::move(scaler)}
32 , fFontMetrics{use_or_generate_metrics(fontMetrics, fScalerContext.get())}
33 , fRoundingSpec{fScalerContext->isSubpixel(),
34 fScalerContext->computeAxisAlignmentForHText()} {
35 SkASSERT(fScalerContext != nullptr);
38 std::tuple<SkGlyph*, size_t> SkScalerCache::glyph(SkPackedGlyphID packedGlyphID) {
39 auto [digest, size] = this->digest(packedGlyphID);
40 return {fGlyphForIndex[digest.index()], size};
43 std::tuple<SkGlyphDigest, size_t> SkScalerCache::digest(SkPackedGlyphID packedGlyphID) {
44 SkGlyphDigest* digest = fDigestForPackedGlyphID.find(packedGlyphID);
46 if (digest != nullptr) {
50 SkGlyph* glyph = fAlloc.make<SkGlyph>(fScalerContext->makeGlyph(packedGlyphID, &fAlloc));
51 return {this->addGlyph(glyph), sizeof(SkGlyph)};
54 SkGlyphDigest SkScalerCache::addGlyph(SkGlyph* glyph) {
55 size_t index = fGlyphForIndex.size();
56 SkGlyphDigest digest = SkGlyphDigest{index, *glyph};
57 fDigestForPackedGlyphID.set(glyph->getPackedID(), digest);
58 fGlyphForIndex.push_back(glyph);
62 size_t SkScalerCache::preparePath(SkGlyph* glyph) {
64 if (glyph->setPath(&fAlloc, fScalerContext.get())) {
65 delta = glyph->path()->approximateBytesUsed();
70 std::tuple<const SkPath*, size_t> SkScalerCache::mergePath(
71 SkGlyph* glyph, const SkPath* path, bool hairline) {
72 SkAutoMutexExclusive lock{fMu};
74 if (glyph->setPathHasBeenCalled()) {
75 SkDEBUGFAIL("Re-adding path to existing glyph. This should not happen.");
77 if (glyph->setPath(&fAlloc, path, hairline)) {
78 pathDelta = glyph->path()->approximateBytesUsed();
80 return {glyph->path(), pathDelta};
83 size_t SkScalerCache::prepareDrawable(SkGlyph* glyph) {
85 if (glyph->setDrawable(&fAlloc, fScalerContext.get())) {
86 delta = glyph->drawable()->approximateBytesUsed();
92 std::tuple<SkDrawable*, size_t> SkScalerCache::mergeDrawable(SkGlyph* glyph,
93 sk_sp<SkDrawable> drawable) {
94 SkAutoMutexExclusive lock{fMu};
96 if (glyph->setDrawableHasBeenCalled()) {
97 SkDEBUGFAIL("Re-adding drawable to existing glyph. This should not happen.");
99 if (glyph->setDrawable(&fAlloc, std::move(drawable))) {
100 delta = glyph->drawable()->approximateBytesUsed();
103 return {glyph->drawable(), delta};
106 int SkScalerCache::countCachedGlyphs() const {
107 SkAutoMutexExclusive lock(fMu);
108 return fDigestForPackedGlyphID.count();
111 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::internalPrepare(
112 SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
113 const SkGlyph** cursor = results;
115 for (auto glyphID : glyphIDs) {
116 auto [glyph, size] = this->glyph(SkPackedGlyphID{glyphID});
118 if (pathDetail == kMetricsAndPath) {
119 size_t pathSize = this->preparePath(glyph);
125 return {{results, glyphIDs.size()}, delta};
128 std::tuple<const void*, size_t> SkScalerCache::prepareImage(SkGlyph* glyph) {
130 if (glyph->setImage(&fAlloc, fScalerContext.get())) {
131 delta = glyph->imageSize();
133 return {glyph->image(), delta};
136 std::tuple<SkGlyph*, size_t> SkScalerCache::mergeGlyphAndImage(
137 SkPackedGlyphID toID, const SkGlyph& from) {
138 SkAutoMutexExclusive lock{fMu};
139 // TODO(herb): remove finding the glyph when setting the metrics and image are separated
140 SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID);
141 if (digest != nullptr) {
142 SkGlyph* to = fGlyphForIndex[digest->index()];
144 if (from.setImageHasBeenCalled()) {
145 if (to->setImageHasBeenCalled()) {
146 // Should never set an image on a glyph which already has an image.
147 SkDEBUGFAIL("Re-adding image to existing glyph. This should not happen.");
149 // TODO: assert that any metrics on `from` are the same.
150 delta = to->setMetricsAndImage(&fAlloc, from);
154 SkGlyph* glyph = fAlloc.make<SkGlyph>(toID);
155 size_t delta = glyph->setMetricsAndImage(&fAlloc, from);
156 (void)this->addGlyph(glyph);
157 return {glyph, sizeof(SkGlyph) + delta};
161 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::metrics(
162 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
163 SkAutoMutexExclusive lock{fMu};
164 auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsOnly, results);
165 return {glyphs, delta};
168 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::preparePaths(
169 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
170 SkAutoMutexExclusive lock{fMu};
171 auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsAndPath, results);
172 return {glyphs, delta};
175 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::prepareImages(
176 SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
177 const SkGlyph** cursor = results;
178 SkAutoMutexExclusive lock{fMu};
180 for (auto glyphID : glyphIDs) {
181 auto[glyph, glyphSize] = this->glyph(glyphID);
182 auto[_, imageSize] = this->prepareImage(glyph);
183 delta += glyphSize + imageSize;
187 return {{results, glyphIDs.size()}, delta};
190 std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::prepareDrawables(
191 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
192 const SkGlyph** cursor = results;
193 SkAutoMutexExclusive lock{fMu};
195 for (auto glyphID : glyphIDs) {
196 auto[glyph, glyphSize] = this->glyph(SkPackedGlyphID{glyphID});
197 size_t drawableSize = this->prepareDrawable(glyph);
198 delta += glyphSize + drawableSize;
202 return {{results, glyphIDs.size()}, delta};
205 template <typename Fn>
206 size_t SkScalerCache::commonFilterLoop(SkDrawableGlyphBuffer* accepted, Fn&& fn) {
208 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
209 if (SkScalarsAreFinite(pos.x(), pos.y())) {
210 auto [digest, size] = this->digest(packedID);
212 if (!digest.isEmpty()) {
220 size_t SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* accepted) {
221 SkAutoMutexExclusive lock{fMu};
222 size_t imageDelta = 0;
223 size_t delta = this->commonFilterLoop(accepted,
224 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
225 // If the glyph is too large, then no image is created.
226 SkGlyph* glyph = fGlyphForIndex[digest.index()];
227 auto [image, imageSize] = this->prepareImage(glyph);
228 if (image != nullptr) {
229 accepted->accept(glyph, i);
230 imageDelta += imageSize;
234 return delta + imageDelta;
237 // Note: this does not actually fill out the image. That happens at atlas building time.
238 size_t SkScalerCache::prepareForMaskDrawing(
239 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
240 SkAutoMutexExclusive lock{fMu};
241 size_t delta = this->commonFilterLoop(accepted,
242 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
243 // N.B. this must have the same behavior as RemoteStrike::prepareForMaskDrawing.
244 if (digest.canDrawAsMask()) {
245 accepted->accept(fGlyphForIndex[digest.index()], i);
247 rejected->reject(i, digest.maxDimension());
254 size_t SkScalerCache::prepareForSDFTDrawing(
255 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
256 SkAutoMutexExclusive lock{fMu};
257 size_t delta = this->commonFilterLoop(accepted,
258 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
259 if (digest.canDrawAsSDFT()) {
260 accepted->accept(fGlyphForIndex[digest.index()], i);
262 // Assume whatever follows SDF doesn't care about the maximum rejected size.
270 size_t SkScalerCache::prepareForPathDrawing(
271 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
272 SkAutoMutexExclusive lock{fMu};
273 size_t pathDelta = 0;
274 size_t delta = this->commonFilterLoop(accepted,
275 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
276 SkGlyph* glyph = fGlyphForIndex[digest.index()];
277 auto pathSize = this->preparePath(glyph);
278 pathDelta += pathSize;
279 if (glyph->path() != nullptr) {
280 // Save off the path to draw later.
281 accepted->accept(glyph, i);
283 // Glyph does not have a path.
284 rejected->reject(i, digest.maxDimension());
288 return delta + pathDelta;
291 size_t SkScalerCache::prepareForDrawableDrawing(
292 SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
293 SkAutoMutexExclusive lock{fMu};
294 size_t drawableDelta = 0;
295 size_t delta = this->commonFilterLoop(accepted,
296 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
297 SkGlyph* glyph = fGlyphForIndex[digest.index()];
298 size_t drawableSize = this->prepareDrawable(glyph);
299 drawableDelta += drawableSize;
300 if (glyph->drawable() != nullptr) {
301 // Save off the drawable to draw later.
302 accepted->accept(glyph, i);
304 // Glyph does not have a drawable.
305 rejected->reject(i, glyph->maxDimension());
309 return delta + drawableDelta;
312 void SkScalerCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
313 SkGlyph* glyph, SkScalar* array, int* count) {
314 SkAutoMutexExclusive lock{fMu};
315 glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
318 void SkScalerCache::dump() const {
319 SkAutoMutexExclusive lock{fMu};
320 const SkTypeface* face = fScalerContext->getTypeface();
321 const SkScalerContextRec& rec = fScalerContext->getRec();
323 rec.getSingleMatrix(&matrix);
324 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
326 face->getFamilyName(&name);
329 SkFontStyle style = face->fontStyle();
330 msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
331 face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
332 rec.dump().c_str(), fDigestForPackedGlyphID.count());
333 SkDebugf("%s\n", msg.c_str());