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 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 -------------------------------------------------------------------------------
40std::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
46std::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
56std::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
64std::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
73const SkDescriptor& SkScalerCache::getDescriptor() const {
74 return *fDesc.getDesc();
75}
76
77int SkScalerCache::countCachedGlyphs() const {
78 SkAutoMutexExclusive lock(fMu);
79 return fGlyphMap.count();
80}
81
82std::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
99std::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
107std::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
122std::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
129std::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
136std::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
151template <typename Fn>
152size_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
166size_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.
183size_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
198size_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
213size_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
238void 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
244void 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