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 void Dump();
    142 
    143     /** Dump memory usage statistics of all the attaches caches in the process using the
    144         SkTraceMemoryDump interface.
    145     */
    146     static void DumpMemoryStatistics(SkTraceMemoryDump* dump);
    147 
    148     typedef void (*Visitor)(const SkGlyphCache&, void* context);
    149     static void VisitAll(Visitor, void* context);
    150 
    151 #ifdef SK_DEBUG
    152     void validate() const;
    153 #else
    154     void validate() const {}
    155 #endif
    156 
    157     class AutoValidate : SkNoncopyable {
    158     public:
    159         AutoValidate(const SkGlyphCache* cache) : fCache(cache) {
    160             if (fCache) {
    161                 fCache->validate();
    162             }
    163         }
    164         ~AutoValidate() {
    165             if (fCache) {
    166                 fCache->validate();
    167             }
    168         }
    169         void forget() {
    170             fCache = nullptr;
    171         }
    172     private:
    173         const SkGlyphCache* fCache;
    174     };
    175 
    176 private:
    177     friend class SkGlyphCache_Globals;
    178 
    179     enum MetricsType {
    180         kJustAdvance_MetricsType,
    181         kFull_MetricsType
    182     };
    183 
    184     enum {
    185         kHashBits           = 8,
    186         kHashCount          = 1 << kHashBits,
    187         kHashMask           = kHashCount - 1
    188     };
    189 
    190     struct CharGlyphRec {
    191         SkPackedUnicharID fPackedUnicharID;
    192         SkPackedGlyphID fPackedGlyphID;
    193     };
    194 
    195     SkGlyphCache(const SkDescriptor*, std::unique_ptr<SkScalerContext>);
    196     ~SkGlyphCache();
    197 
    198     // Return the SkGlyph* associated with MakeID. The id parameter is the
    199     // combined glyph/x/y id generated by MakeID. If it is just a glyph id
    200     // then x and y are assumed to be zero.
    201     SkGlyph* lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID, MetricsType type);
    202 
    203     // Return a SkGlyph* associated with unicode id and position x and y.
    204     SkGlyph* lookupByChar(SkUnichar id, MetricsType type, SkFixed x = 0, SkFixed y = 0);
    205 
    206     // Return a new SkGlyph for the glyph ID and subpixel position id. Limit the amount
    207     // of work using type.
    208     SkGlyph* allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsType type);
    209 
    210     static bool DetachProc(const SkGlyphCache*, void*) { return true; }
    211 
    212     // The id arg is a combined id generated by MakeID.
    213     CharGlyphRec* getCharGlyphRec(SkPackedUnicharID id);
    214 
    215     static void OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale,
    216                               SkScalar xPos, SkScalar* array, int* count);
    217     static void AddInterval(SkScalar val, SkGlyph::Intercept* intercept);
    218     static void AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2],
    219                           bool yAxis, SkGlyph::Intercept* intercept);
    220     static void AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis,
    221                         SkGlyph::Intercept* intercept);
    222     static void AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis,
    223                         SkGlyph::Intercept* intercept);
    224     static void AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis,
    225                          SkGlyph::Intercept* intercept);
    226     static const SkGlyph::Intercept* MatchBounds(const SkGlyph* glyph,
    227                                                  const SkScalar bounds[2]);
    228 
    229     SkGlyphCache*          fNext;
    230     SkGlyphCache*          fPrev;
    231     const std::unique_ptr<SkDescriptor> fDesc;
    232     const std::unique_ptr<SkScalerContext> fScalerContext;
    233     SkPaint::FontMetrics   fFontMetrics;
    234 
    235     // Map from a combined GlyphID and sub-pixel position to a SkGlyph.
    236     SkTHashTable<SkGlyph, SkPackedGlyphID, SkGlyph::HashTraits> fGlyphMap;
    237 
    238     // so we don't grow our arrays a lot
    239     static constexpr size_t kMinGlyphCount = 8;
    240     static constexpr size_t kMinGlyphImageSize = 16 /* height */ * 8 /* width */;
    241     static constexpr size_t kMinAllocAmount = kMinGlyphImageSize * kMinGlyphCount;
    242 
    243     SkArenaAlloc            fAlloc {kMinAllocAmount};
    244 
    245     std::unique_ptr<CharGlyphRec[]> fPackedUnicharIDToPackedGlyphID;
    246 
    247     // used to track (approx) how much ram is tied-up in this cache
    248     size_t                  fMemoryUsed;
    249 };
    250 
    251 class SkAutoGlyphCache : public std::unique_ptr<SkGlyphCache, SkGlyphCache::AttachCacheFunctor> {
    252 public:
    253     /** deprecated: use get() */
    254     SkGlyphCache* getCache() const { return this->get(); }
    255     SkAutoGlyphCache() = default;
    256     SkAutoGlyphCache(SkGlyphCache* cache) : INHERITED(cache) {}
    257     SkAutoGlyphCache(SkTypeface* typeface, const SkScalerContextEffects& effects,
    258                      const SkDescriptor* desc)
    259         : INHERITED(SkGlyphCache::DetachCache(typeface, effects, desc))
    260     {}
    261     /** deprecated: always enables fake gamma */
    262     SkAutoGlyphCache(const SkPaint& paint,
    263                      const SkSurfaceProps* surfaceProps,
    264                      const SkMatrix* matrix)
    265         : INHERITED(paint.detachCache(surfaceProps,
    266                                       SkPaint::kFakeGammaAndBoostContrast_ScalerContextFlags,
    267                                       matrix))
    268     {}
    269     SkAutoGlyphCache(const SkPaint& paint,
    270                      const SkSurfaceProps* surfaceProps,
    271                      uint32_t scalerContextFlags,
    272                      const SkMatrix* matrix)
    273         : INHERITED(paint.detachCache(surfaceProps, scalerContextFlags, matrix))
    274     {}
    275 private:
    276     using INHERITED = std::unique_ptr<SkGlyphCache, SkGlyphCache::AttachCacheFunctor>;
    277 };
    278 
    279 class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCache {
    280 public:
    281     SkAutoGlyphCacheNoGamma(const SkPaint& paint,
    282                             const SkSurfaceProps* surfaceProps,
    283                             const SkMatrix* matrix)
    284         : SkAutoGlyphCache(paint, surfaceProps, SkPaint::kNone_ScalerContextFlags, matrix)
    285     {}
    286 };
    287 #define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache)
    288 #define SkAutoGlyphCacheNoGamma(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCacheNoGamma)
    289 
    290 #endif
    291