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/SkStrike.h"
9
10 #include "include/core/SkDrawable.h"
11 #include "include/core/SkGraphics.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkTraceMemoryDump.h"
14 #include "include/core/SkTypeface.h"
15 #include "src/core/SkDistanceFieldGen.h"
16 #include "src/core/SkEnumerate.h"
17 #include "src/core/SkGlyph.h"
18 #include "src/core/SkGlyphBuffer.h"
19 #include "src/core/SkScalerContext.h"
20 #include "src/core/SkStrikeCache.h"
21 #include "src/text/StrikeForGPU.h"
22
23 #if defined(SK_GANESH)
24 #include "src/text/gpu/StrikeCache.h"
25 #endif
26
27 using namespace skglyph;
28
use_or_generate_metrics(const SkFontMetrics * metrics,SkScalerContext * context)29 static SkFontMetrics use_or_generate_metrics(
30 const SkFontMetrics* metrics, SkScalerContext* context) {
31 SkFontMetrics answer;
32 if (metrics) {
33 answer = *metrics;
34 } else {
35 context->getFontMetrics(&answer);
36 }
37 return answer;
38 }
39
SkStrike(SkStrikeCache * strikeCache,const SkStrikeSpec & strikeSpec,std::unique_ptr<SkScalerContext> scaler,const SkFontMetrics * metrics,std::unique_ptr<SkStrikePinner> pinner)40 SkStrike::SkStrike(SkStrikeCache* strikeCache,
41 const SkStrikeSpec& strikeSpec,
42 std::unique_ptr<SkScalerContext> scaler,
43 const SkFontMetrics* metrics,
44 std::unique_ptr<SkStrikePinner> pinner)
45 : fFontMetrics{use_or_generate_metrics(metrics, scaler.get())}
46 , fRoundingSpec{scaler->isSubpixel(),
47 scaler->computeAxisAlignmentForHText()}
48 , fStrikeSpec{strikeSpec}
49 , fStrikeCache{strikeCache}
50 , fScalerContext{std::move(scaler)}
51 , fPinner{std::move(pinner)} {
52 SkASSERT(fScalerContext != nullptr);
53 }
54
55 class SK_SCOPED_CAPABILITY SkStrike::Monitor {
56 public:
57 Monitor(SkStrike* strike) SK_ACQUIRE(strike->fStrikeLock)
58 : fStrike{strike} {
59 fStrike->lock();
60 }
61
SK_RELEASE_CAPABILITY()62 ~Monitor() SK_RELEASE_CAPABILITY() {
63 fStrike->unlock();
64 }
65
66 private:
67 SkStrike* const fStrike;
68 };
69
lock()70 void SkStrike::lock() {
71 fStrikeLock.acquire();
72 fMemoryIncrease = 0;
73 }
74
unlock()75 void SkStrike::unlock() {
76 const size_t memoryIncrease = fMemoryIncrease;
77 fStrikeLock.release();
78 this->updateMemoryUsage(memoryIncrease);
79 }
80
mergeGlyphAndImage(SkPackedGlyphID toID,const SkGlyph & fromGlyph)81 SkGlyph* SkStrike::mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& fromGlyph) {
82 Monitor m{this};
83 // TODO(herb): remove finding the glyph when setting the metrics and image are separated
84 SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID);
85 if (digest != nullptr) {
86 SkGlyph* glyph = fGlyphForIndex[digest->index()];
87 if (fromGlyph.setImageHasBeenCalled()) {
88 if (glyph->setImageHasBeenCalled()) {
89 // Should never set an image on a glyph which already has an image.
90 SkDEBUGFAIL("Re-adding image to existing glyph. This should not happen.");
91 }
92 // TODO: assert that any metrics on fromGlyph are the same.
93 fMemoryIncrease += glyph->setMetricsAndImage(&fAlloc, fromGlyph);
94 }
95 return glyph;
96 } else {
97 SkGlyph* glyph = fAlloc.make<SkGlyph>(toID);
98 fMemoryIncrease += glyph->setMetricsAndImage(&fAlloc, fromGlyph) + sizeof(SkGlyph);
99 (void)this->addGlyphAndDigest(glyph);
100 return glyph;
101 }
102 }
103
mergePath(SkGlyph * glyph,const SkPath * path,bool hairline)104 const SkPath* SkStrike::mergePath(SkGlyph* glyph, const SkPath* path, bool hairline) {
105 Monitor m{this};
106 if (glyph->setPathHasBeenCalled()) {
107 SkDEBUGFAIL("Re-adding path to existing glyph. This should not happen.");
108 }
109 if (glyph->setPath(&fAlloc, path, hairline)) {
110 fMemoryIncrease += glyph->path()->approximateBytesUsed();
111 }
112
113 return glyph->path();
114 }
115
mergeDrawable(SkGlyph * glyph,sk_sp<SkDrawable> drawable)116 const SkDrawable* SkStrike::mergeDrawable(SkGlyph* glyph, sk_sp<SkDrawable> drawable) {
117 Monitor m{this};
118 if (glyph->setDrawableHasBeenCalled()) {
119 SkDEBUGFAIL("Re-adding drawable to existing glyph. This should not happen.");
120 }
121 if (glyph->setDrawable(&fAlloc, std::move(drawable))) {
122 fMemoryIncrease += glyph->drawable()->approximateBytesUsed();
123 SkASSERT(fMemoryIncrease > 0);
124 }
125
126 return glyph->drawable();
127 }
128
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,SkGlyph * glyph,SkScalar * array,int * count)129 void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
130 SkGlyph* glyph, SkScalar* array, int* count) {
131 SkAutoMutexExclusive lock{fStrikeLock};
132 glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
133 }
134
metrics(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])135 SkSpan<const SkGlyph*> SkStrike::metrics(
136 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
137 Monitor m{this};
138 return this->internalPrepare(glyphIDs, kMetricsOnly, results);
139 }
140
preparePaths(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])141 SkSpan<const SkGlyph*> SkStrike::preparePaths(
142 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
143 Monitor m{this};
144 return this->internalPrepare(glyphIDs, kMetricsAndPath, results);
145 }
146
prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,const SkGlyph * results[])147 SkSpan<const SkGlyph*> SkStrike::prepareImages(
148 SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
149 const SkGlyph** cursor = results;
150 Monitor m{this};
151 for (auto glyphID : glyphIDs) {
152 SkGlyph* glyph = this->glyph(glyphID);
153 this->prepareForImage(glyph);
154 *cursor++ = glyph;
155 }
156
157 return {results, glyphIDs.size()};
158 }
159
prepareDrawables(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])160 SkSpan<const SkGlyph*> SkStrike::prepareDrawables(
161 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
162 const SkGlyph** cursor = results;
163 {
164 Monitor m{this};
165 for (auto glyphID : glyphIDs) {
166 SkGlyph* glyph = this->glyph(SkPackedGlyphID{glyphID});
167 this->prepareForDrawable(glyph);
168 *cursor++ = glyph;
169 }
170 }
171
172 return {results, glyphIDs.size()};
173 }
174
glyphIDsToPaths(SkSpan<sktext::IDOrPath> idsOrPaths)175 void SkStrike::glyphIDsToPaths(SkSpan<sktext::IDOrPath> idsOrPaths) {
176 Monitor m{this};
177 for (sktext::IDOrPath& idOrPath : idsOrPaths) {
178 SkGlyph* glyph = this->glyph(SkPackedGlyphID{idOrPath.fGlyphID});
179 this->prepareForPath(glyph);
180 new (&idOrPath.fPath) SkPath{*glyph->path()};
181 }
182 }
183
glyphIDsToDrawables(SkSpan<sktext::IDOrDrawable> idsOrDrawables)184 void SkStrike::glyphIDsToDrawables(SkSpan<sktext::IDOrDrawable> idsOrDrawables) {
185 Monitor m{this};
186 for (sktext::IDOrDrawable& idOrDrawable : idsOrDrawables) {
187 SkGlyph* glyph = this->glyph(SkPackedGlyphID{idOrDrawable.fGlyphID});
188 this->prepareForDrawable(glyph);
189 SkASSERT(glyph->drawable() != nullptr);
190 idOrDrawable.fDrawable = glyph->drawable();
191 }
192 }
193
dump() const194 void SkStrike::dump() const {
195 SkAutoMutexExclusive lock{fStrikeLock};
196 const SkTypeface* face = fScalerContext->getTypeface();
197 const SkScalerContextRec& rec = fScalerContext->getRec();
198 SkMatrix matrix;
199 rec.getSingleMatrix(&matrix);
200 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
201 SkString name;
202 face->getFamilyName(&name);
203
204 SkString msg;
205 SkFontStyle style = face->fontStyle();
206 msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
207 face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
208 rec.dump().c_str(), fDigestForPackedGlyphID.count());
209 SkDebugf("%s\n", msg.c_str());
210 }
211
dumpMemoryStatistics(SkTraceMemoryDump * dump) const212 void SkStrike::dumpMemoryStatistics(SkTraceMemoryDump* dump) const {
213 SkAutoMutexExclusive lock{fStrikeLock};
214 const SkTypeface* face = fScalerContext->getTypeface();
215 const SkScalerContextRec& rec = fScalerContext->getRec();
216
217 SkString fontName;
218 face->getFamilyName(&fontName);
219 // Replace all special characters with '_'.
220 for (size_t index = 0; index < fontName.size(); ++index) {
221 if (!std::isalnum(fontName[index])) {
222 fontName[index] = '_';
223 }
224 }
225
226 SkString dumpName = SkStringPrintf("%s/%s_%d/%p",
227 SkStrikeCache::kGlyphCacheDumpName,
228 fontName.c_str(),
229 rec.fTypefaceID,
230 this);
231
232 dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", fMemoryUsed);
233 dump->dumpNumericValue(dumpName.c_str(),
234 "glyph_count", "objects",
235 fDigestForPackedGlyphID.count());
236 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
237 }
238
glyph(SkGlyphDigest digest)239 SkGlyph* SkStrike::glyph(SkGlyphDigest digest) {
240 return fGlyphForIndex[digest.index()];
241 }
242
glyph(SkPackedGlyphID packedGlyphID)243 SkGlyph* SkStrike::glyph(SkPackedGlyphID packedGlyphID) {
244 SkGlyphDigest digest = this->digestFor(kDirectMask, packedGlyphID);
245 return this->glyph(digest);
246 }
247
digestFor(ActionType actionType,SkPackedGlyphID packedGlyphID)248 SkGlyphDigest SkStrike::digestFor(ActionType actionType, SkPackedGlyphID packedGlyphID) {
249 SkGlyphDigest* digestPtr = fDigestForPackedGlyphID.find(packedGlyphID);
250 if (digestPtr != nullptr && digestPtr->actionFor(actionType) != GlyphAction::kUnset) {
251 return *digestPtr;
252 }
253
254 SkGlyph* glyph;
255 if (digestPtr != nullptr) {
256 glyph = fGlyphForIndex[digestPtr->index()];
257 } else {
258 glyph = fAlloc.make<SkGlyph>(fScalerContext->makeGlyph(packedGlyphID, &fAlloc));
259 fMemoryIncrease += sizeof(SkGlyph);
260 digestPtr = this->addGlyphAndDigest(glyph);
261 }
262
263 digestPtr->setActionFor(actionType, glyph, this);
264
265 return *digestPtr;
266 }
267
addGlyphAndDigest(SkGlyph * glyph)268 SkGlyphDigest* SkStrike::addGlyphAndDigest(SkGlyph* glyph) {
269 size_t index = fGlyphForIndex.size();
270 SkGlyphDigest digest = SkGlyphDigest{index, *glyph};
271 SkGlyphDigest* newDigest = fDigestForPackedGlyphID.set(glyph->getPackedID(), digest);
272 fGlyphForIndex.push_back(glyph);
273 return newDigest;
274 }
275
prepareForImage(SkGlyph * glyph)276 bool SkStrike::prepareForImage(SkGlyph* glyph) {
277 if (glyph->setImage(&fAlloc, fScalerContext.get())) {
278 fMemoryIncrease += glyph->imageSize();
279 }
280 return glyph->image() != nullptr;
281 }
282
prepareForPath(SkGlyph * glyph)283 bool SkStrike::prepareForPath(SkGlyph* glyph) {
284 if (glyph->setPath(&fAlloc, fScalerContext.get())) {
285 fMemoryIncrease += glyph->path()->approximateBytesUsed();
286 }
287 return glyph->path() !=nullptr;
288 }
289
prepareForDrawable(SkGlyph * glyph)290 bool SkStrike::prepareForDrawable(SkGlyph* glyph) {
291 if (glyph->setDrawable(&fAlloc, fScalerContext.get())) {
292 size_t increase = glyph->drawable()->approximateBytesUsed();
293 SkASSERT(increase > 0);
294 fMemoryIncrease += increase;
295 }
296 return glyph->drawable() != nullptr;
297 }
298
internalPrepare(SkSpan<const SkGlyphID> glyphIDs,PathDetail pathDetail,const SkGlyph ** results)299 SkSpan<const SkGlyph*> SkStrike::internalPrepare(
300 SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
301 const SkGlyph** cursor = results;
302 for (auto glyphID : glyphIDs) {
303 SkGlyph* glyph = this->glyph(SkPackedGlyphID{glyphID});
304 if (pathDetail == kMetricsAndPath) {
305 this->prepareForPath(glyph);
306 }
307 *cursor++ = glyph;
308 }
309
310 return {results, glyphIDs.size()};
311 }
312
updateMemoryUsage(size_t increase)313 void SkStrike::updateMemoryUsage(size_t increase) {
314 if (increase > 0) {
315 // fRemoved and the cache's total memory are managed under the cache's lock. This allows
316 // them to be accessed under LRU operation.
317 SkAutoMutexExclusive lock{fStrikeCache->fLock};
318 fMemoryUsed += increase;
319 if (!fRemoved) {
320 fStrikeCache->fTotalMemoryUsed += increase;
321 }
322 }
323 }
324