xref: /aosp_15_r20/external/skia/src/core/SkStrike.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkFontStyle.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPath.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkTraceMemoryDump.h"
16 #include "include/core/SkTypeface.h"
17 #include "include/private/base/SkDebug.h"
18 #include "include/private/base/SkTFitsIn.h"
19 #include "src/core/SkGlyph.h"
20 #include "src/core/SkMask.h"
21 #include "src/core/SkReadBuffer.h"
22 #include "src/core/SkScalerContext.h"
23 #include "src/core/SkStrikeCache.h"
24 #include "src/core/SkWriteBuffer.h"
25 #include "src/text/StrikeForGPU.h"
26 
27 #include <cctype>
28 #include <new>
29 #include <optional>
30 #include <utility>
31 
32 using namespace skglyph;
33 
use_or_generate_metrics(const SkFontMetrics * metrics,SkScalerContext * context)34 static SkFontMetrics use_or_generate_metrics(
35         const SkFontMetrics* metrics, SkScalerContext* context) {
36     SkFontMetrics answer;
37     if (metrics) {
38         answer = *metrics;
39     } else {
40         context->getFontMetrics(&answer);
41     }
42     return answer;
43 }
44 
SkStrike(SkStrikeCache * strikeCache,const SkStrikeSpec & strikeSpec,std::unique_ptr<SkScalerContext> scaler,const SkFontMetrics * metrics,std::unique_ptr<SkStrikePinner> pinner)45 SkStrike::SkStrike(SkStrikeCache* strikeCache,
46                    const SkStrikeSpec& strikeSpec,
47                    std::unique_ptr<SkScalerContext> scaler,
48                    const SkFontMetrics* metrics,
49                    std::unique_ptr<SkStrikePinner> pinner)
50         : fFontMetrics{use_or_generate_metrics(metrics, scaler.get())}
51         , fRoundingSpec{scaler->isSubpixel(),
52                         scaler->computeAxisAlignmentForHText()}
53         , fStrikeSpec{strikeSpec}
54         , fStrikeCache{strikeCache}
55         , fScalerContext{std::move(scaler)}
56         , fPinner{std::move(pinner)} {
57     SkASSERT(fScalerContext != nullptr);
58 }
59 
60 class SK_SCOPED_CAPABILITY SkStrike::Monitor {
61 public:
62     Monitor(SkStrike* strike) SK_ACQUIRE(strike->fStrikeLock)
63             : fStrike{strike} {
64         fStrike->lock();
65     }
66 
SK_RELEASE_CAPABILITY()67     ~Monitor() SK_RELEASE_CAPABILITY() {
68         fStrike->unlock();
69     }
70 
71 private:
72     SkStrike* const fStrike;
73 };
74 
lock()75 void SkStrike::lock() {
76     fStrikeLock.acquire();
77     fMemoryIncrease = 0;
78 }
79 
unlock()80 void SkStrike::unlock() {
81     const size_t memoryIncrease = fMemoryIncrease;
82     fStrikeLock.release();
83     this->updateMemoryUsage(memoryIncrease);
84 }
85 
86 void
FlattenGlyphsByType(SkWriteBuffer & buffer,SkSpan<SkGlyph> images,SkSpan<SkGlyph> paths,SkSpan<SkGlyph> drawables)87 SkStrike::FlattenGlyphsByType(SkWriteBuffer& buffer,
88                               SkSpan<SkGlyph> images,
89                               SkSpan<SkGlyph> paths,
90                               SkSpan<SkGlyph> drawables) {
91     SkASSERT_RELEASE(SkTFitsIn<int>(images.size()) &&
92                      SkTFitsIn<int>(paths.size()) &&
93                      SkTFitsIn<int>(drawables.size()));
94 
95     buffer.writeInt(images.size());
96     for (SkGlyph& glyph : images) {
97         SkASSERT(SkMask::IsValidFormat(glyph.maskFormat()));
98         glyph.flattenMetrics(buffer);
99         glyph.flattenImage(buffer);
100     }
101 
102     buffer.writeInt(paths.size());
103     for (SkGlyph& glyph : paths) {
104         SkASSERT(SkMask::IsValidFormat(glyph.maskFormat()));
105         glyph.flattenMetrics(buffer);
106         glyph.flattenPath(buffer);
107     }
108 
109     buffer.writeInt(drawables.size());
110     for (SkGlyph& glyph : drawables) {
111         SkASSERT(SkMask::IsValidFormat(glyph.maskFormat()));
112         glyph.flattenMetrics(buffer);
113         glyph.flattenDrawable(buffer);
114     }
115 }
116 
mergeFromBuffer(SkReadBuffer & buffer)117 bool SkStrike::mergeFromBuffer(SkReadBuffer& buffer) {
118     // Read glyphs with images for the current strike.
119     const int imagesCount = buffer.readInt();
120     if (imagesCount == 0 && !buffer.isValid()) {
121         return false;
122     }
123 
124     {
125         Monitor m{this};
126         for (int curImage = 0; curImage < imagesCount; ++curImage) {
127             if (!this->mergeGlyphAndImageFromBuffer(buffer)) {
128                 return false;
129             }
130         }
131     }
132 
133     // Read glyphs with paths for the current strike.
134     const int pathsCount = buffer.readInt();
135     if (pathsCount == 0 && !buffer.isValid()) {
136         return false;
137     }
138     {
139         Monitor m{this};
140         for (int curPath = 0; curPath < pathsCount; ++curPath) {
141             if (!this->mergeGlyphAndPathFromBuffer(buffer)) {
142                 return false;
143             }
144         }
145     }
146 
147     // Read glyphs with drawables for the current strike.
148     const int drawablesCount = buffer.readInt();
149     if (drawablesCount == 0 && !buffer.isValid()) {
150         return false;
151     }
152     {
153         Monitor m{this};
154         for (int curDrawable = 0; curDrawable < drawablesCount; ++curDrawable) {
155             if (!this->mergeGlyphAndDrawableFromBuffer(buffer)) {
156                 return false;
157             }
158         }
159     }
160 
161     return true;
162 }
163 
mergeGlyphAndImage(SkPackedGlyphID toID,const SkGlyph & fromGlyph)164 SkGlyph* SkStrike::mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& fromGlyph) {
165     Monitor m{this};
166     // TODO(herb): remove finding the glyph when setting the metrics and image are separated
167     SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID);
168     if (digest != nullptr) {
169         SkGlyph* glyph = fGlyphForIndex[digest->index()];
170         if (fromGlyph.setImageHasBeenCalled()) {
171             if (glyph->setImageHasBeenCalled()) {
172                 // Should never set an image on a glyph which already has an image.
173                 SkDEBUGFAIL("Re-adding image to existing glyph. This should not happen.");
174             }
175             // TODO: assert that any metrics on fromGlyph are the same.
176             fMemoryIncrease += glyph->setMetricsAndImage(&fAlloc, fromGlyph);
177         }
178         return glyph;
179     } else {
180         SkGlyph* glyph = fAlloc.make<SkGlyph>(toID);
181         fMemoryIncrease += glyph->setMetricsAndImage(&fAlloc, fromGlyph) + sizeof(SkGlyph);
182         (void)this->addGlyphAndDigest(glyph);
183         return glyph;
184     }
185 }
186 
mergePath(SkGlyph * glyph,const SkPath * path,bool hairline,bool modified)187 const SkPath* SkStrike::mergePath(SkGlyph* glyph, const SkPath* path, bool hairline, bool modified) {
188     Monitor m{this};
189     if (glyph->setPathHasBeenCalled()) {
190         SkDEBUGFAIL("Re-adding path to existing glyph. This should not happen.");
191     }
192     if (glyph->setPath(&fAlloc, path, hairline, modified)) {
193         fMemoryIncrease += glyph->path()->approximateBytesUsed();
194     }
195 
196     return glyph->path();
197 }
198 
mergeDrawable(SkGlyph * glyph,sk_sp<SkDrawable> drawable)199 const SkDrawable* SkStrike::mergeDrawable(SkGlyph* glyph, sk_sp<SkDrawable> drawable) {
200     Monitor m{this};
201     if (glyph->setDrawableHasBeenCalled()) {
202         SkDEBUGFAIL("Re-adding drawable to existing glyph. This should not happen.");
203     }
204     if (glyph->setDrawable(&fAlloc, std::move(drawable))) {
205         fMemoryIncrease += glyph->drawable()->approximateBytesUsed();
206         SkASSERT(fMemoryIncrease > 0);
207     }
208 
209     return glyph->drawable();
210 }
211 
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,SkGlyph * glyph,SkScalar * array,int * count)212 void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
213                               SkGlyph* glyph, SkScalar* array, int* count) {
214     SkAutoMutexExclusive lock{fStrikeLock};
215     glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
216 }
217 
metrics(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])218 SkSpan<const SkGlyph*> SkStrike::metrics(
219         SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
220     Monitor m{this};
221     return this->internalPrepare(glyphIDs, kMetricsOnly, results);
222 }
223 
preparePaths(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])224 SkSpan<const SkGlyph*> SkStrike::preparePaths(
225         SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
226     Monitor m{this};
227     return this->internalPrepare(glyphIDs, kMetricsAndPath, results);
228 }
229 
prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,const SkGlyph * results[])230 SkSpan<const SkGlyph*> SkStrike::prepareImages(
231         SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
232     const SkGlyph** cursor = results;
233     Monitor m{this};
234     for (auto glyphID : glyphIDs) {
235         SkGlyph* glyph = this->glyph(glyphID);
236         this->prepareForImage(glyph);
237         *cursor++ = glyph;
238     }
239 
240     return {results, glyphIDs.size()};
241 }
242 
prepareDrawables(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])243 SkSpan<const SkGlyph*> SkStrike::prepareDrawables(
244         SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
245     const SkGlyph** cursor = results;
246     {
247         Monitor m{this};
248         for (auto glyphID : glyphIDs) {
249             SkGlyph* glyph = this->glyph(SkPackedGlyphID{glyphID});
250             this->prepareForDrawable(glyph);
251             *cursor++ = glyph;
252         }
253     }
254 
255     return {results, glyphIDs.size()};
256 }
257 
glyphIDsToPaths(SkSpan<sktext::IDOrPath> idsOrPaths)258 void SkStrike::glyphIDsToPaths(SkSpan<sktext::IDOrPath> idsOrPaths) {
259     Monitor m{this};
260     for (sktext::IDOrPath& idOrPath : idsOrPaths) {
261         SkGlyph* glyph = this->glyph(SkPackedGlyphID{idOrPath.fGlyphID});
262         this->prepareForPath(glyph);
263         new (&idOrPath.fPath) SkPath{*glyph->path()};
264     }
265 }
266 
glyphIDsToDrawables(SkSpan<sktext::IDOrDrawable> idsOrDrawables)267 void SkStrike::glyphIDsToDrawables(SkSpan<sktext::IDOrDrawable> idsOrDrawables) {
268     Monitor m{this};
269     for (sktext::IDOrDrawable& idOrDrawable : idsOrDrawables) {
270         SkGlyph* glyph = this->glyph(SkPackedGlyphID{idOrDrawable.fGlyphID});
271         this->prepareForDrawable(glyph);
272         SkASSERT(glyph->drawable() != nullptr);
273         idOrDrawable.fDrawable = glyph->drawable();
274     }
275 }
276 
dump() const277 void SkStrike::dump() const {
278     SkAutoMutexExclusive lock{fStrikeLock};
279     const SkTypeface* face = fScalerContext->getTypeface();
280     const SkScalerContextRec& rec = fScalerContext->getRec();
281     SkMatrix matrix;
282     rec.getSingleMatrix(&matrix);
283     matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
284     SkString name;
285     face->getFamilyName(&name);
286 
287     SkString msg;
288     SkFontStyle style = face->fontStyle();
289     msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
290                face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
291                rec.dump().c_str(), fDigestForPackedGlyphID.count());
292     SkDebugf("%s\n", msg.c_str());
293 }
294 
dumpMemoryStatistics(SkTraceMemoryDump * dump) const295 void SkStrike::dumpMemoryStatistics(SkTraceMemoryDump* dump) const {
296     SkAutoMutexExclusive lock{fStrikeLock};
297     const SkTypeface* face = fScalerContext->getTypeface();
298     const SkScalerContextRec& rec = fScalerContext->getRec();
299 
300     SkString fontName;
301     face->getFamilyName(&fontName);
302     // Replace all special characters with '_'.
303     for (size_t index = 0; index < fontName.size(); ++index) {
304         if (!std::isalnum(fontName[index])) {
305             fontName[index] = '_';
306         }
307     }
308 
309     SkString dumpName = SkStringPrintf("%s/%s_%u/%p",
310                                        SkStrikeCache::kGlyphCacheDumpName,
311                                        fontName.c_str(),
312                                        rec.fTypefaceID,
313                                        this);
314 
315     dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", fMemoryUsed);
316     dump->dumpNumericValue(dumpName.c_str(),
317                            "glyph_count", "objects",
318                            fDigestForPackedGlyphID.count());
319     dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
320 }
321 
glyph(SkGlyphDigest digest)322 SkGlyph* SkStrike::glyph(SkGlyphDigest digest) {
323     return fGlyphForIndex[digest.index()];
324 }
325 
glyph(SkPackedGlyphID packedGlyphID)326 SkGlyph* SkStrike::glyph(SkPackedGlyphID packedGlyphID) {
327     SkGlyphDigest digest = this->digestFor(kDirectMask, packedGlyphID);
328     return this->glyph(digest);
329 }
330 
digestFor(ActionType actionType,SkPackedGlyphID packedGlyphID)331 SkGlyphDigest SkStrike::digestFor(ActionType actionType, SkPackedGlyphID packedGlyphID) {
332     SkGlyphDigest* digestPtr = fDigestForPackedGlyphID.find(packedGlyphID);
333     if (digestPtr != nullptr && digestPtr->actionFor(actionType) != GlyphAction::kUnset) {
334         return *digestPtr;
335     }
336 
337     SkGlyph* glyph;
338     if (digestPtr != nullptr) {
339         glyph = fGlyphForIndex[digestPtr->index()];
340     } else {
341         glyph = fAlloc.make<SkGlyph>(fScalerContext->makeGlyph(packedGlyphID, &fAlloc));
342         fMemoryIncrease += sizeof(SkGlyph);
343         digestPtr = this->addGlyphAndDigest(glyph);
344     }
345 
346     digestPtr->setActionFor(actionType, glyph, this);
347 
348     return *digestPtr;
349 }
350 
addGlyphAndDigest(SkGlyph * glyph)351 SkGlyphDigest* SkStrike::addGlyphAndDigest(SkGlyph* glyph) {
352     size_t index = fGlyphForIndex.size();
353     SkGlyphDigest digest = SkGlyphDigest{index, *glyph};
354     SkGlyphDigest* newDigest = fDigestForPackedGlyphID.set(digest);
355     fGlyphForIndex.push_back(glyph);
356     return newDigest;
357 }
358 
prepareForImage(SkGlyph * glyph)359 bool SkStrike::prepareForImage(SkGlyph* glyph) {
360     if (glyph->setImage(&fAlloc, fScalerContext.get())) {
361         fMemoryIncrease += glyph->imageSize();
362     }
363     return glyph->image() != nullptr;
364 }
365 
prepareForPath(SkGlyph * glyph)366 bool SkStrike::prepareForPath(SkGlyph* glyph) {
367     if (glyph->setPath(&fAlloc, fScalerContext.get())) {
368         fMemoryIncrease += glyph->path()->approximateBytesUsed();
369     }
370     return glyph->path() !=nullptr;
371 }
372 
prepareForDrawable(SkGlyph * glyph)373 bool SkStrike::prepareForDrawable(SkGlyph* glyph) {
374     if (glyph->setDrawable(&fAlloc, fScalerContext.get())) {
375         size_t increase = glyph->drawable()->approximateBytesUsed();
376         SkASSERT(increase > 0);
377         fMemoryIncrease += increase;
378     }
379     return glyph->drawable() != nullptr;
380 }
381 
mergeGlyphFromBuffer(SkReadBuffer & buffer)382 SkGlyph* SkStrike::mergeGlyphFromBuffer(SkReadBuffer& buffer) {
383     SkASSERT(buffer.isValid());
384     std::optional<SkGlyph> prototypeGlyph = SkGlyph::MakeFromBuffer(buffer);
385     if (!buffer.validate(prototypeGlyph.has_value())) {
386         return nullptr;
387     }
388 
389     // Check if this glyph has already been seen.
390     SkGlyphDigest* digestPtr = fDigestForPackedGlyphID.find(prototypeGlyph->getPackedID());
391     if (digestPtr != nullptr) {
392         return fGlyphForIndex[digestPtr->index()];
393     }
394 
395     // This is the first time. Allocate a new glyph.
396     SkGlyph* glyph = fAlloc.make<SkGlyph>(prototypeGlyph.value());
397     fMemoryIncrease += sizeof(SkGlyph);
398     this->addGlyphAndDigest(glyph);
399     return glyph;
400 }
401 
mergeGlyphAndImageFromBuffer(SkReadBuffer & buffer)402 bool SkStrike::mergeGlyphAndImageFromBuffer(SkReadBuffer& buffer) {
403     SkASSERT(buffer.isValid());
404     SkGlyph* glyph = this->mergeGlyphFromBuffer(buffer);
405     if (!buffer.validate(glyph != nullptr)) {
406         return false;
407     }
408     fMemoryIncrease += glyph->addImageFromBuffer(buffer, &fAlloc);
409     return buffer.isValid();
410 }
411 
mergeGlyphAndPathFromBuffer(SkReadBuffer & buffer)412 bool SkStrike::mergeGlyphAndPathFromBuffer(SkReadBuffer& buffer) {
413     SkASSERT(buffer.isValid());
414     SkGlyph* glyph = this->mergeGlyphFromBuffer(buffer);
415     if (!buffer.validate(glyph != nullptr)) {
416         return false;
417     }
418     fMemoryIncrease += glyph->addPathFromBuffer(buffer, &fAlloc);
419     return buffer.isValid();
420 }
421 
mergeGlyphAndDrawableFromBuffer(SkReadBuffer & buffer)422 bool SkStrike::mergeGlyphAndDrawableFromBuffer(SkReadBuffer& buffer) {
423     SkASSERT(buffer.isValid());
424     SkGlyph* glyph = this->mergeGlyphFromBuffer(buffer);
425     if (!buffer.validate(glyph != nullptr)) {
426         return false;
427     }
428     fMemoryIncrease += glyph->addDrawableFromBuffer(buffer, &fAlloc);
429     return buffer.isValid();
430 }
431 
internalPrepare(SkSpan<const SkGlyphID> glyphIDs,PathDetail pathDetail,const SkGlyph ** results)432 SkSpan<const SkGlyph*> SkStrike::internalPrepare(
433         SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
434     const SkGlyph** cursor = results;
435     for (auto glyphID : glyphIDs) {
436         SkGlyph* glyph = this->glyph(SkPackedGlyphID{glyphID});
437         if (pathDetail == kMetricsAndPath) {
438             this->prepareForPath(glyph);
439         }
440         *cursor++ = glyph;
441     }
442 
443     return {results, glyphIDs.size()};
444 }
445 
updateMemoryUsage(size_t increase)446 void SkStrike::updateMemoryUsage(size_t increase) {
447     if (increase > 0) {
448         // fRemoved and the cache's total memory are managed under the cache's lock. This allows
449         // them to be accessed under LRU operation.
450         SkAutoMutexExclusive lock{fStrikeCache->fLock};
451         fMemoryUsed += increase;
452         if (!fRemoved) {
453             fStrikeCache->fTotalMemoryUsed += increase;
454         }
455     }
456 }
457