• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
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 GrAtlasGlyphCache_DEFINED
9 #define GrAtlasGlyphCache_DEFINED
10 
11 #include "GrDrawOpAtlas.h"
12 #include "GrGlyph.h"
13 #include "SkArenaAlloc.h"
14 #include "SkGlyphCache.h"
15 #include "SkTDynamicHash.h"
16 
17 class GrGlyphCache;
18 class GrAtlasManager;
19 class GrGpu;
20 
21 /**
22  *  The GrTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory
23  *  is indexed by a PackedID and SkGlyphCache. The SkGlyphCache is what actually creates the mask.
24  *  The GrTextStrike may outlive the generating SkGlyphCache. However, it retains a copy
25  *  of it's SkDescriptor as a key to access (or regenerate) the SkGlyphCache. GrTextStrikes are
26  *  created by and owned by a GrGlyphCache.
27  */
28 class GrTextStrike : public SkNVRefCnt<GrTextStrike> {
29 public:
30     GrTextStrike(const SkDescriptor& fontScalerKey);
31     ~GrTextStrike();
32 
getGlyph(const SkGlyph & skGlyph,GrGlyph::PackedID packed,SkGlyphCache * cache)33     inline GrGlyph* getGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
34                              SkGlyphCache* cache) {
35         GrGlyph* glyph = fCache.find(packed);
36         if (!glyph) {
37             glyph = this->generateGlyph(skGlyph, packed, cache);
38         }
39         return glyph;
40     }
41 
42     // This variant of the above function is called by GrAtlasTextOp. At this point, it is possible
43     // that the maskformat of the glyph differs from what we expect.  In these cases we will just
44     // draw a clear square.
45     // skbug:4143 crbug:510931
getGlyph(GrGlyph::PackedID packed,GrMaskFormat expectedMaskFormat,SkGlyphCache * cache)46     inline GrGlyph* getGlyph(GrGlyph::PackedID packed,
47                              GrMaskFormat expectedMaskFormat,
48                              SkGlyphCache* cache) {
49         GrGlyph* glyph = fCache.find(packed);
50         if (!glyph) {
51             // We could return this to the caller, but in practice it adds code complexity for
52             // potentially little benefit(ie, if the glyph is not in our font cache, then its not
53             // in the atlas and we're going to be doing a texture upload anyways).
54             const SkGlyph& skGlyph = GrToSkGlyph(cache, packed);
55             glyph = this->generateGlyph(skGlyph, packed, cache);
56             glyph->fMaskFormat = expectedMaskFormat;
57         }
58         return glyph;
59     }
60 
61     // returns true if glyph successfully added to texture atlas, false otherwise.  If the glyph's
62     // mask format has changed, then addGlyphToAtlas will draw a clear box.  This will almost never
63     // happen.
64     // TODO we can handle some of these cases if we really want to, but the long term solution is to
65     // get the actual glyph image itself when we get the glyph metrics.
66     bool addGlyphToAtlas(GrResourceProvider*, GrDeferredUploadTarget*, GrGlyphCache*,
67                          GrAtlasManager*, GrGlyph*,
68                          SkGlyphCache*, GrMaskFormat expectedMaskFormat);
69 
70     // testing
countGlyphs()71     int countGlyphs() const { return fCache.count(); }
72 
73     // remove any references to this plot
74     void removeID(GrDrawOpAtlas::AtlasID);
75 
76     // If a TextStrike is abandoned by the cache, then the caller must get a new strike
isAbandoned()77     bool isAbandoned() const { return fIsAbandoned; }
78 
GetKey(const GrTextStrike & strike)79     static const SkDescriptor& GetKey(const GrTextStrike& strike) {
80         return *strike.fFontScalerKey.getDesc();
81     }
82 
Hash(const SkDescriptor & desc)83     static uint32_t Hash(const SkDescriptor& desc) { return desc.getChecksum(); }
84 
85 private:
86     SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
87     SkAutoDescriptor fFontScalerKey;
88     SkArenaAlloc fPool{512};
89 
90     int fAtlasedGlyphs;
91     bool fIsAbandoned;
92 
GrToSkGlyph(SkGlyphCache * cache,GrGlyph::PackedID id)93     static const SkGlyph& GrToSkGlyph(SkGlyphCache* cache, GrGlyph::PackedID id) {
94         return cache->getGlyphIDMetrics(GrGlyph::UnpackID(id),
95                                         GrGlyph::UnpackFixedX(id),
96                                         GrGlyph::UnpackFixedY(id));
97     }
98 
99     GrGlyph* generateGlyph(const SkGlyph&, GrGlyph::PackedID, SkGlyphCache*);
100 
101     friend class GrGlyphCache;
102 };
103 
104 /**
105  * GrGlyphCache manages strikes which are indexed by a SkGlyphCache. These strikes can then be
106  * used to generate individual Glyph Masks.
107  */
108 class GrGlyphCache {
109 public:
110     GrGlyphCache();
111     ~GrGlyphCache();
112 
setGlyphSizeLimit(SkScalar sizeLimit)113     void setGlyphSizeLimit(SkScalar sizeLimit) { fGlyphSizeLimit = sizeLimit; }
getGlyphSizeLimit()114     SkScalar getGlyphSizeLimit() const { return fGlyphSizeLimit; }
115 
setStrikeToPreserve(GrTextStrike * strike)116     void setStrikeToPreserve(GrTextStrike* strike) { fPreserveStrike = strike; }
117 
118     // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
119     // another client of the cache may cause the strike to be purged while it is still reffed.
120     // Therefore, the caller must check GrTextStrike::isAbandoned() if there are other
121     // interactions with the cache since the strike was received.
getStrike(const SkGlyphCache * cache)122     inline sk_sp<GrTextStrike> getStrike(const SkGlyphCache* cache) {
123         sk_sp<GrTextStrike> strike = sk_ref_sp(fCache.find(cache->getDescriptor()));
124         if (!strike) {
125             strike = this->generateStrike(cache);
126         }
127         return strike;
128     }
129 
130     void freeAll();
131 
132     static void HandleEviction(GrDrawOpAtlas::AtlasID, void*);
133 
134 private:
generateStrike(const SkGlyphCache * cache)135     sk_sp<GrTextStrike> generateStrike(const SkGlyphCache* cache) {
136         // 'fCache' get the construction ref
137         sk_sp<GrTextStrike> strike = sk_ref_sp(new GrTextStrike(cache->getDescriptor()));
138         fCache.add(strike.get());
139         return strike;
140     }
141 
142     using StrikeHash = SkTDynamicHash<GrTextStrike, SkDescriptor>;
143 
144     StrikeHash fCache;
145     GrTextStrike* fPreserveStrike;
146     SkScalar fGlyphSizeLimit;
147 };
148 
149 #endif
150