Home | History | Annotate | Download | only in core
      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 found in the LICENSE file.
      5  */
      6 
      7 #ifndef SkGlyphCache_DEFINED
      8 #define SkGlyphCache_DEFINED
      9 
     10 #include "SkArenaAlloc.h"
     11 #include "SkBitmap.h"
     12 #include "SkDescriptor.h"
     13 #include "SkGlyph.h"
     14 #include "SkPaint.h"
     15 #include "SkTHash.h"
     16 #include "SkScalerContext.h"
     17 #include "SkTemplates.h"
     18 #include "SkTDArray.h"
     19 #include <memory>
     20 
     21 class SkTraceMemoryDump;
     22 
     23 class SkGlyphCache_Globals;
     24 
     25 /** \class SkGlyphCache
     26 
     27     This class represents a strike: a specific combination of typeface, size, matrix, etc., and
     28     holds the glyphs for that strike. Calling any of the getUnichar.../getGlyphID... methods will
     29     return the requested glyph, either instantly if it is already cached, or by first generating
     30     it and then adding it to the strike.
     31 
     32     The strikes are held in a global list, available to all threads. To interact with one, call
     33     either VisitCache() or DetachCache().
     34 */
     35 class SkGlyphCache {
     36 public:
     37     /** Returns a glyph with valid fAdvance and fDevKern fields. The remaining fields may be
     38         valid, but that is not guaranteed. If you require those, call getUnicharMetrics or
     39         getGlyphIDMetrics instead.
     40     */
     41     const SkGlyph& getUnicharAdvance(SkUnichar);
     42     const SkGlyph& getGlyphIDAdvance(SkGlyphID);
     43 
     44     /** Returns a glyph with all fields valid except fImage and fPath, which may be null. If they
     45         are null, call findImage or findPath for those. If they are not null, then they are valid.
     46 
     47         This call is potentially slower than the matching ...Advance call. If you only need the
     48         fAdvance/fDevKern fields, call those instead.
     49     */
     50     const SkGlyph& getUnicharMetrics(SkUnichar);
     51     const SkGlyph& getGlyphIDMetrics(SkGlyphID);
     52 
     53     /** These are variants that take the device position of the glyph. Call these only if you are
     54         drawing in subpixel mode. Passing 0, 0 is effectively the same as calling the variants
     55         w/o the extra params, though a tiny bit slower.
     56     */
     57     const SkGlyph& getUnicharMetrics(SkUnichar, SkFixed x, SkFixed y);
     58     const SkGlyph& getGlyphIDMetrics(uint16_t, SkFixed x, SkFixed y);
     59 
     60     /** Return the glyphID for the specified Unichar. If the char has already been seen, use the
     61         existing cache entry. If not, ask the scalercontext to compute it for us.
     62     */
     63     SkGlyphID unicharToGlyph(SkUnichar);
     64 
     65     /** Map the glyph to its Unicode equivalent. Unmappable glyphs map to a character code of zero.
     66     */
     67     SkUnichar glyphToUnichar(SkGlyphID);
     68 
     69     /** Returns the number of glyphs for this strike.
     70     */
     71     unsigned getGlyphCount() const;
     72 
     73     /** Return the number of glyphs currently cached. */
     74     int countCachedGlyphs() const;
     75 
     76     /** Return the image associated with the glyph. If it has not been generated this will
     77         trigger that.
     78     */
     79     const void* findImage(const SkGlyph&);
     80 
     81     /** If the advance axis intersects the glyph's path, append the positions scaled and offset
     82         to the array (if non-null), and set the count to the updated array length.
     83     */
     84     void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
     85                         bool yAxis, SkGlyph* , SkScalar* array, int* count);
     86 
     87     /** Return the Path associated with the glyph. If it has not been generated this will trigger
     88         that.
     89     */
     90     const SkPath* findPath(const SkGlyph&);
     91 
     92     /** Return the vertical metrics for this strike.
     93     */
     94     const SkPaint::FontMetrics& getFontMetrics() const {
     95         return fFontMetrics;
     96     }
     97 
     98     const SkDescriptor& getDescriptor() const { return *fDesc; }
     99 
    100     SkMask::Format getMaskFormat() const {
    101         return fScalerContext->getMaskFormat();
    102     }
    103 
    104     bool isSubpixel() const {
    105         return fScalerContext->isSubpixel();
    106     }
    107 
    108     /** Return the approx RAM usage for this cache. */
    109     size_t getMemoryUsed() const { return fMemoryUsed; }
    110 
    111     void dump() const;
    112 
    113     SkScalerContext* getScalerContext() const { return fScalerContext.get(); }
    114 
    115     /** Find a matching cache entry, and call proc() with it. If none is found create a new one.
    116         If the proc() returns true, detach the cache and return it, otherwise leave it and return
    117         nullptr.
    118     */
    119     static SkGlyphCache* VisitCache(SkTypeface*, const SkScalerContextEffects&, const SkDescriptor*,
    120                                     bool (*proc)(const SkGlyphCache*, void*),
    121                                     void* context);
    122 
    123     /** Given a strike that was returned by either VisitCache() or DetachCache() add it back into
    124         the global cache list (after which the caller should not reference it anymore.
    125     */
    126     static void AttachCache(SkGlyphCache*);
    127     using AttachCacheFunctor = SkFunctionWrapper<void, SkGlyphCache, AttachCache>;
    128 
    129     /** Detach a strike from the global cache matching the specified descriptor. Once detached,
    130         it can be queried/modified by the current thread, and when finished, be reattached to the
    131         global cache with AttachCache(). While detached, if another request is made with the same
    132         descriptor, a different strike will be generated. This is fine. It does mean we can have
    133         more than 1 strike for the same descriptor, but that will eventually get purged, and the
    134         win is that different thread will never block each other while a strike is being used.
    135     */
    136     static SkGlyphCache* DetachCache(SkTypeface* typeface, const SkScalerContextEffects& effects,
    137                                      const SkDescriptor* desc) {
    138         return VisitCache(typeface, effects, desc, DetachProc, nullptr);
    139     }
    140 
    141     static SkGlyphCache* DetachCacheUsingPaint(const SkPaint& paint,
    142                                                const SkSurfaceProps* surfaceProps,
    143                                                SkScalerContextFlags scalerContextFlags,
    144                                                const SkMatrix* deviceMatrix);
    145 
    146     static void Dump();
    147 
    148     /** Dump memory usage statistics of all the attaches caches in the process using the
    149         SkTraceMemoryDump interface.
    150     */
    151     static void DumpMemoryStatistics(SkTraceMemoryDump* dump);
    152 
    153     typedef void (*Visitor)(const SkGlyphCache&, void* context);
    154     static void VisitAll(Visitor, void* context);
    155 
    156 #ifdef SK_DEBUG
    157     void validate() const;
    158 #else
    159     void validate() const {}
    160 #endif
    161 
    162     class AutoValidate : SkNoncopyable {
    163     public:
    164         AutoValidate(const SkGlyphCache* cache) : fCache(cache) {
    165             if (fCache) {
    166                 fCache->validate();
    167             }
    168         }
    169         ~AutoValidate() {
    170             if (fCache) {
    171                 fCache->validate();
    172             }
    173         }
    174         void forget() {
    175             fCache = nullptr;
    176         }
    177     private:
    178         const SkGlyphCache* fCache;
    179     };
    180 
    181 private:
    182     friend class SkGlyphCache_Globals;
    183 
    184     enum MetricsType {
    185         kJustAdvance_MetricsType,
    186         kFull_MetricsType
    187     };
    188 
    189     enum {
    190         kHashBits           = 8,
    191         kHashCount          = 1 << kHashBits,
    192         kHashMask           = kHashCount - 1
    193     };
    194 
    195     struct CharGlyphRec {
    196         SkPackedUnicharID fPackedUnicharID;
    197         SkPackedGlyphID fPackedGlyphID;
    198     };
    199 
    200     SkGlyphCache(const SkDescriptor*, std::unique_ptr<SkScalerContext>);
    201     ~SkGlyphCache();
    202 
    203     // Return the SkGlyph* associated with MakeID. The id parameter is the
    204     // combined glyph/x/y id generated by MakeID. If it is just a glyph id
    205     // then x and y are assumed to be zero.
    206     SkGlyph* lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID, MetricsType type);
    207 
    208     // Return a SkGlyph* associated with unicode id and position x and y.
    209     SkGlyph* lookupByChar(SkUnichar id, MetricsType type, SkFixed x = 0, SkFixed y = 0);
    210 
    211     // Return a new SkGlyph for the glyph ID and subpixel position id. Limit the amount
    212     // of work using type.
    213     SkGlyph* allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsType type);
    214 
    215     static bool DetachProc(const SkGlyphCache*, void*) { return true; }
    216 
    217     // The id arg is a combined id generated by MakeID.
    218     CharGlyphRec* getCharGlyphRec(SkPackedUnicharID id);
    219 
    220     static void OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale,
    221                               SkScalar xPos, SkScalar* array, int* count);
    222     static void AddInterval(SkScalar val, SkGlyph::Intercept* intercept);
    223     static void AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2],
    224                           bool yAxis, SkGlyph::Intercept* intercept);
    225     static void AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis,
    226                         SkGlyph::Intercept* intercept);
    227     static void AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis,
    228                         SkGlyph::Intercept* intercept);
    229     static void AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis,
    230                          SkGlyph::Intercept* intercept);
    231     static const SkGlyph::Intercept* MatchBounds(const SkGlyph* glyph,
    232                                                  const SkScalar bounds[2]);
    233 
    234     SkGlyphCache*          fNext;
    235     SkGlyphCache*          fPrev;
    236     const std::unique_ptr<SkDescriptor> fDesc;
    237     const std::unique_ptr<SkScalerContext> fScalerContext;
    238     SkPaint::FontMetrics   fFontMetrics;
    239 
    240     // Map from a combined GlyphID and sub-pixel position to a SkGlyph.
    241     SkTHashTable<SkGlyph, SkPackedGlyphID, SkGlyph::HashTraits> fGlyphMap;
    242 
    243     // so we don't grow our arrays a lot
    244     static constexpr size_t kMinGlyphCount = 8;
    245     static constexpr size_t kMinGlyphImageSize = 16 /* height */ * 8 /* width */;
    246     static constexpr size_t kMinAllocAmount = kMinGlyphImageSize * kMinGlyphCount;
    247 
    248     SkArenaAlloc            fAlloc {kMinAllocAmount};
    249 
    250     std::unique_ptr<CharGlyphRec[]> fPackedUnicharIDToPackedGlyphID;
    251 
    252     // used to track (approx) how much ram is tied-up in this cache
    253     size_t                  fMemoryUsed;
    254 };
    255 
    256 class SkAutoGlyphCache : public std::unique_ptr<SkGlyphCache, SkGlyphCache::AttachCacheFunctor> {
    257 public:
    258     /** deprecated: use get() */
    259     SkGlyphCache* getCache() const { return this->get(); }
    260     SkAutoGlyphCache() = default;
    261     SkAutoGlyphCache(SkGlyphCache* cache) : INHERITED(cache) {}
    262     SkAutoGlyphCache(SkTypeface* typeface, const SkScalerContextEffects& effects,
    263                      const SkDescriptor* desc)
    264         : INHERITED(SkGlyphCache::DetachCache(typeface, effects, desc))
    265     {}
    266     /** deprecated: always enables fake gamma */
    267     SkAutoGlyphCache(const SkPaint& paint,
    268                      const SkSurfaceProps* surfaceProps,
    269                      const SkMatrix* matrix)
    270         : INHERITED(
    271         SkGlyphCache::DetachCacheUsingPaint(
    272             paint, surfaceProps,
    273             SkScalerContextFlags::kFakeGammaAndBoostContrast, matrix))
    274     {}
    275     SkAutoGlyphCache(const SkPaint& paint,
    276                      const SkSurfaceProps* surfaceProps,
    277                      SkScalerContextFlags scalerContextFlags,
    278                      const SkMatrix* matrix)
    279         : INHERITED(
    280             SkGlyphCache::DetachCacheUsingPaint(paint, surfaceProps, scalerContextFlags, matrix))
    281     {}
    282 private:
    283     using INHERITED = std::unique_ptr<SkGlyphCache, SkGlyphCache::AttachCacheFunctor>;
    284 };
    285 
    286 class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCache {
    287 public:
    288     SkAutoGlyphCacheNoGamma(const SkPaint& paint,
    289                             const SkSurfaceProps* surfaceProps,
    290                             const SkMatrix* matrix)
    291         : SkAutoGlyphCache(paint, surfaceProps, SkScalerContextFlags::kNone, matrix)
    292     {}
    293 };
    294 #define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache)
    295 #define SkAutoGlyphCacheNoGamma(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCacheNoGamma)
    296 
    297 #endif
    298