xref: /third_party/skia/src/core/SkScalerCache.cpp (revision cb93a386)
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
16static 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
27SkScalerCache::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
37std::tuple<SkGlyph*, size_t> SkScalerCache::glyph(SkPackedGlyphID packedGlyphID) {
38    auto [digest, size] = this->digest(packedGlyphID);
39    return {fGlyphForIndex[digest.index()], size};
40}
41
42std::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
53SkGlyphDigest 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
61std::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
69std::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
78int SkScalerCache::countCachedGlyphs() const {
79    SkAutoMutexExclusive lock(fMu);
80    return fDigestForPackedGlyphID.count();
81}
82
83std::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
100std::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
108std::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
127std::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
134std::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
141std::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
156template <typename Fn>
157size_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
171size_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.
189size_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
204size_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
219size_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
245void 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
251void 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