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 #ifndef SkGlyphCache_DEFINED
9 #define SkGlyphCache_DEFINED
10 
11 #include "SkBitmap.h"
12 #include "SkChecksum.h"
13 #include "SkChunkAlloc.h"
14 #include "SkDescriptor.h"
15 #include "SkGlyph.h"
16 #include "SkScalerContext.h"
17 #include "SkTemplates.h"
18 #include "SkTDArray.h"
19 
20 struct SkDeviceProperties;
21 class SkPaint;
22 
23 class SkGlyphCache_Globals;
24 
25 // Enable this locally to add stats for hash-table hit rates. It also extends the dump()
26 // output to show those stats.
27 //#define SK_GLYPHCACHE_TRACK_HASH_STATS
28 
29 /** \class SkGlyphCache
30 
31     This class represents a strike: a specific combination of typeface, size,
32     matrix, etc., and holds the glyphs for that strike. Calling any of the
33     getUnichar.../getGlyphID... methods will return the requested glyph,
34     either instantly if it is already cached, or by first generating it and then
35     adding it to the strike.
36 
37     The strikes are held in a global list, available to all threads. To interact
38     with one, call either VisitCache() or DetachCache().
39 */
40 class SkGlyphCache {
41 public:
42     /** Returns a glyph with valid fAdvance and fDevKern fields.
43         The remaining fields may be valid, but that is not guaranteed. If you
44         require those, call getUnicharMetrics or getGlyphIDMetrics instead.
45     */
46     const SkGlyph& getUnicharAdvance(SkUnichar);
47     const SkGlyph& getGlyphIDAdvance(uint16_t);
48 
49     /** Returns a glyph with all fields valid except fImage and fPath, which
50         may be null. If they are null, call findImage or findPath for those.
51         If they are not null, then they are valid.
52 
53         This call is potentially slower than the matching ...Advance call. If
54         you only need the fAdvance/fDevKern fields, call those instead.
55     */
56     const SkGlyph& getUnicharMetrics(SkUnichar);
57     const SkGlyph& getGlyphIDMetrics(uint16_t);
58 
59     /** These are variants that take the device position of the glyph. Call
60         these only if you are drawing in subpixel mode. Passing 0, 0 is
61         effectively the same as calling the variants w/o the extra params, tho
62         a tiny bit slower.
63     */
64     const SkGlyph& getUnicharMetrics(SkUnichar, SkFixed x, SkFixed y);
65     const SkGlyph& getGlyphIDMetrics(uint16_t, SkFixed x, SkFixed y);
66 
67     /** Return the glyphID for the specified Unichar. If the char has already
68         been seen, use the existing cache entry. If not, ask the scalercontext
69         to compute it for us.
70     */
71     uint16_t unicharToGlyph(SkUnichar);
72 
73     /** Map the glyph to its Unicode equivalent. Unmappable glyphs map to
74         a character code of zero.
75     */
76     SkUnichar glyphToUnichar(uint16_t);
77 
78     /** Returns the number of glyphs for this strike.
79     */
80     unsigned getGlyphCount();
81 
82     /** Return the image associated with the glyph. If it has not been generated
83         this will trigger that.
84     */
85     const void* findImage(const SkGlyph&);
86     /** Return the Path associated with the glyph. If it has not been generated
87         this will trigger that.
88     */
89     const SkPath* findPath(const SkGlyph&);
90 
91     /** Return the vertical metrics for this strike.
92     */
getFontMetrics()93     const SkPaint::FontMetrics& getFontMetrics() const {
94         return fFontMetrics;
95     }
96 
getDescriptor()97     const SkDescriptor& getDescriptor() const { return *fDesc; }
98 
getMaskFormat()99     SkMask::Format getMaskFormat() const {
100         return fScalerContext->getMaskFormat();
101     }
102 
isSubpixel()103     bool isSubpixel() const {
104         return fScalerContext->isSubpixel();
105     }
106 
107     void dump() const;
108 
109     /*  AuxProc/Data allow a client to associate data with this cache entry.
110         Multiple clients can use this, as their data is keyed with a function
111         pointer. In addition to serving as a key, the function pointer is called
112         with the data when the glyphcache object is deleted, so the client can
113         cleanup their data as well. NOTE: the auxProc must not try to access
114         this glyphcache in any way, since it may be in the process of being
115         deleted.
116     */
117 
118     //! If the proc is found, return true and set *dataPtr to its data
119     bool getAuxProcData(void (*auxProc)(void*), void** dataPtr) const;
120     //! Add a proc/data pair to the glyphcache. proc should be non-null
121     void setAuxProc(void (*auxProc)(void*), void* auxData);
122 
getScalerContext()123     SkScalerContext* getScalerContext() const { return fScalerContext; }
124 
125     /** Find a matching cache entry, and call proc() with it. If none is found
126         create a new one. If the proc() returns true, detach the cache and
127         return it, otherwise leave it and return NULL.
128     */
129     static SkGlyphCache* VisitCache(SkTypeface*, const SkDescriptor* desc,
130                                     bool (*proc)(const SkGlyphCache*, void*),
131                                     void* context);
132 
133     /** Given a strike that was returned by either VisitCache() or DetachCache()
134         add it back into the global cache list (after which the caller should
135         not reference it anymore.
136     */
137     static void AttachCache(SkGlyphCache*);
138 
139     /** Detach a strike from the global cache matching the specified descriptor.
140         Once detached, it can be queried/modified by the current thread, and
141         when finished, be reattached to the global cache with AttachCache().
142         While detached, if another request is made with the same descriptor,
143         a different strike will be generated. This is fine. It does mean we
144         can have more than 1 strike for the same descriptor, but that will
145         eventually get purged, and the win is that different thread will never
146         block each other while a strike is being used.
147     */
DetachCache(SkTypeface * typeface,const SkDescriptor * desc)148     static SkGlyphCache* DetachCache(SkTypeface* typeface,
149                                      const SkDescriptor* desc) {
150         return VisitCache(typeface, desc, DetachProc, NULL);
151     }
152 
153     static void Dump();
154 
155 #ifdef SK_DEBUG
156     void validate() const;
157 #else
validate()158     void validate() const {}
159 #endif
160 
161     class AutoValidate : SkNoncopyable {
162     public:
AutoValidate(const SkGlyphCache * cache)163         AutoValidate(const SkGlyphCache* cache) : fCache(cache) {
164             if (fCache) {
165                 fCache->validate();
166             }
167         }
~AutoValidate()168         ~AutoValidate() {
169             if (fCache) {
170                 fCache->validate();
171             }
172         }
forget()173         void forget() {
174             fCache = NULL;
175         }
176     private:
177         const SkGlyphCache* fCache;
178     };
179 
180 private:
181     // we take ownership of the scalercontext
182     SkGlyphCache(SkTypeface*, const SkDescriptor*, SkScalerContext*);
183     ~SkGlyphCache();
184 
185     enum MetricsType {
186         kJustAdvance_MetricsType,
187         kFull_MetricsType
188     };
189 
190     // Return the SkGlyph* associated with MakeID. The id parameter is the combined glyph/x/y
191     // id generated by MakeID. If it is just a glyph id then x and y are assuemd to be zero.
192     SkGlyph* lookupByCombinedID(uint32_t id, MetricsType type);
193 
194     // Return a SkGlyph* associated with unicode id and position x and y.
195     SkGlyph* lookupByChar(SkUnichar id, MetricsType type, SkFixed x = 0, SkFixed y = 0);
196 
197     // Return the index of id in the fGlyphArray. If it does
198     // not exist, create a new one using MetricsType.
199     uint16_t lookupMetrics(uint32_t id, MetricsType type);
DetachProc(const SkGlyphCache *,void *)200     static bool DetachProc(const SkGlyphCache*, void*) { return true; }
201 
202     SkGlyphCache*        fNext, *fPrev;
203     SkDescriptor*        fDesc;
204     SkScalerContext*     fScalerContext;
205     SkPaint::FontMetrics fFontMetrics;
206 
207     enum {
208         kHashBits           = 8,
209         kHashCount          = 1 << kHashBits,
210         kHashMask           = kHashCount - 1
211     };
212 
213     // A quick lookup to avoid the binary search looking for glyphs in fGlyphArray.
214     uint16_t             fGlyphHash[kHashCount];
215     // Contains the SkGlyphs that are used by fGlyphHash and fCharToGlyphHash. The zero element
216     // is reserved for a sentinel SkGlyph that reduces the logic to check for collisions in the
217     // hash arrays. The zero element has an fID of SkGlyph::kImpossibleID which never matches
218     // any combined id generated for a char or a glyph.
219     SkTDArray<SkGlyph>   fGlyphArray;
220     SkChunkAlloc         fGlyphAlloc;
221 
222     struct CharGlyphRec {
223         uint32_t    fID;    // unichar + subpixel
224         uint16_t    fGlyphIndex;
225     };
226 
227     // no reason to use the same kHashCount as fGlyphHash, but we do for now
228     // Dynamically allocated when chars are encountered.
229     SkAutoTArray<CharGlyphRec> fCharToGlyphHash;
230 
231     // The id arg is a combined id generated by MakeID.
232     CharGlyphRec* getCharGlyphRec(uint32_t id);
233     void adjustCaches(int insertion_index);
234 
ID2HashIndex(uint32_t h)235     static inline unsigned ID2HashIndex(uint32_t h) {
236         return SkChecksum::CheapMix(h) & kHashMask;
237     }
238 
239     // used to track (approx) how much ram is tied-up in this cache
240     size_t  fMemoryUsed;
241 
242 #ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
243     int fHashHitCount;
244     int fHashMissCount;
245 #endif
246 
247     struct AuxProcRec {
248         AuxProcRec* fNext;
249         void (*fProc)(void*);
250         void* fData;
251     };
252     AuxProcRec* fAuxProcList;
253     void invokeAndRemoveAuxProcs();
254 
255     inline static SkGlyphCache* FindTail(SkGlyphCache* head);
256 
257     friend class SkGlyphCache_Globals;
258 };
259 
260 class SkAutoGlyphCacheBase {
261 public:
getCache()262     SkGlyphCache* getCache() const { return fCache; }
263 
release()264     void release() {
265         if (fCache) {
266             SkGlyphCache::AttachCache(fCache);
267             fCache = NULL;
268         }
269     }
270 
271 protected:
272     // Hide the constructors so we can't create one of these directly.
273     // Create SkAutoGlyphCache or SkAutoGlyphCacheNoCache instead.
SkAutoGlyphCacheBase(SkGlyphCache * cache)274     SkAutoGlyphCacheBase(SkGlyphCache* cache) : fCache(cache) {}
SkAutoGlyphCacheBase(SkTypeface * typeface,const SkDescriptor * desc)275     SkAutoGlyphCacheBase(SkTypeface* typeface, const SkDescriptor* desc) {
276         fCache = SkGlyphCache::DetachCache(typeface, desc);
277     }
SkAutoGlyphCacheBase(const SkPaint &,const SkDeviceProperties *,const SkMatrix *)278     SkAutoGlyphCacheBase(const SkPaint& /*paint*/,
279                          const SkDeviceProperties* /*deviceProperties*/,
280                          const SkMatrix* /*matrix*/) {
281         fCache = NULL;
282     }
SkAutoGlyphCacheBase()283     SkAutoGlyphCacheBase() {
284         fCache = NULL;
285     }
~SkAutoGlyphCacheBase()286     ~SkAutoGlyphCacheBase() {
287         if (fCache) {
288             SkGlyphCache::AttachCache(fCache);
289         }
290     }
291 
292     SkGlyphCache*   fCache;
293 
294 private:
295     static bool DetachProc(const SkGlyphCache*, void*);
296 };
297 
298 class SkAutoGlyphCache : public SkAutoGlyphCacheBase {
299 public:
SkAutoGlyphCache(SkGlyphCache * cache)300     SkAutoGlyphCache(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {}
SkAutoGlyphCache(SkTypeface * typeface,const SkDescriptor * desc)301     SkAutoGlyphCache(SkTypeface* typeface, const SkDescriptor* desc) :
302         SkAutoGlyphCacheBase(typeface, desc) {}
SkAutoGlyphCache(const SkPaint & paint,const SkDeviceProperties * deviceProperties,const SkMatrix * matrix)303     SkAutoGlyphCache(const SkPaint& paint,
304                      const SkDeviceProperties* deviceProperties,
305                      const SkMatrix* matrix) {
306         fCache = paint.detachCache(deviceProperties, matrix, false);
307     }
308 
309 private:
SkAutoGlyphCache()310     SkAutoGlyphCache() : SkAutoGlyphCacheBase() {}
311 };
312 #define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache)
313 
314 class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCacheBase {
315 public:
SkAutoGlyphCacheNoGamma(SkGlyphCache * cache)316     SkAutoGlyphCacheNoGamma(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {}
SkAutoGlyphCacheNoGamma(SkTypeface * typeface,const SkDescriptor * desc)317     SkAutoGlyphCacheNoGamma(SkTypeface* typeface, const SkDescriptor* desc) :
318         SkAutoGlyphCacheBase(typeface, desc) {}
SkAutoGlyphCacheNoGamma(const SkPaint & paint,const SkDeviceProperties * deviceProperties,const SkMatrix * matrix)319     SkAutoGlyphCacheNoGamma(const SkPaint& paint,
320                             const SkDeviceProperties* deviceProperties,
321                             const SkMatrix* matrix) {
322         fCache = paint.detachCache(deviceProperties, matrix, true);
323     }
324 
325 private:
SkAutoGlyphCacheNoGamma()326     SkAutoGlyphCacheNoGamma() : SkAutoGlyphCacheBase() {}
327 };
328 #define SkAutoGlyphCacheNoGamma(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCacheNoGamma)
329 
330 #endif
331