Home | History | Annotate | Download | only in text
      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 GrAtlasTextBlob_DEFINED
      9 #define GrAtlasTextBlob_DEFINED
     10 
     11 #include "GrBatchAtlas.h"
     12 #include "GrBatchFontCache.h"
     13 #include "GrColor.h"
     14 #include "GrMemoryPool.h"
     15 #include "SkDescriptor.h"
     16 #include "SkMaskFilter.h"
     17 #include "SkSurfaceProps.h"
     18 #include "SkTInternalLList.h"
     19 
     20 class GrBlobRegenHelper;
     21 struct GrDistanceFieldAdjustTable;
     22 class GrMemoryPool;
     23 class SkDrawFilter;
     24 class SkTextBlob;
     25 class SkTextBlobRunIterator;
     26 
     27 // With this flag enabled, the GrAtlasTextContext will, as a sanity check, regenerate every blob
     28 // that comes in to verify the integrity of its cache
     29 #define CACHE_SANITY_CHECK 0
     30 
     31 /*
     32  * A GrAtlasTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
     33  * on the GPU.  These are initially created with valid positions and colors, but invalid
     34  * texture coordinates.  The GrAtlasTextBlob itself has a few Blob-wide properties, and also
     35  * consists of a number of runs.  Runs inside a blob are flushed individually so they can be
     36  * reordered.
     37  *
     38  * The only thing(aside from a memcopy) required to flush a GrAtlasTextBlob is to ensure that
     39  * the GrAtlas will not evict anything the Blob needs.
     40  *
     41  * Note: This struct should really be named GrCachedAtasTextBlob, but that is too verbose.
     42  *
     43  * *WARNING* If you add new fields to this struct, then you may need to to update AssertEqual
     44  */
     45 class GrAtlasTextBlob : public SkNVRefCnt<GrAtlasTextBlob> {
     46 public:
     47     SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrAtlasTextBlob);
     48 
     49     static GrAtlasTextBlob* Create(GrMemoryPool* pool, int glyphCount, int runCount);
     50 
     51     struct Key {
     52         Key() {
     53             sk_bzero(this, sizeof(Key));
     54         }
     55         uint32_t fUniqueID;
     56         // Color may affect the gamma of the mask we generate, but in a fairly limited way.
     57         // Each color is assigned to on of a fixed number of buckets based on its
     58         // luminance. For each luminance bucket there is a "canonical color" that
     59         // represents the bucket.  This functionality is currently only supported for A8
     60         SkColor fCanonicalColor;
     61         SkPaint::Style fStyle;
     62         SkPixelGeometry fPixelGeometry;
     63         bool fHasBlur;
     64 
     65         bool operator==(const Key& other) const {
     66             return 0 == memcmp(this, &other, sizeof(Key));
     67         }
     68     };
     69 
     70     void setupKey(const GrAtlasTextBlob::Key& key,
     71                   const SkMaskFilter::BlurRec& blurRec,
     72                   const SkPaint& paint) {
     73         fKey = key;
     74         if (key.fHasBlur) {
     75             fBlurRec = blurRec;
     76         }
     77         if (key.fStyle != SkPaint::kFill_Style) {
     78             fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
     79             fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
     80             fStrokeInfo.fJoin = paint.getStrokeJoin();
     81         }
     82     }
     83 
     84     static const Key& GetKey(const GrAtlasTextBlob& blob) {
     85         return blob.fKey;
     86     }
     87 
     88     static uint32_t Hash(const Key& key) {
     89         return SkChecksum::Murmur3(&key, sizeof(Key));
     90     }
     91 
     92     void operator delete(void* p) {
     93         GrAtlasTextBlob* blob = reinterpret_cast<GrAtlasTextBlob*>(p);
     94         blob->fPool->release(p);
     95     }
     96     void* operator new(size_t) {
     97         SkFAIL("All blobs are created by placement new.");
     98         return sk_malloc_throw(0);
     99     }
    100 
    101     void* operator new(size_t, void* p) { return p; }
    102     void operator delete(void* target, void* placement) {
    103         ::operator delete(target, placement);
    104     }
    105 
    106     bool hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); }
    107     bool hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
    108     void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
    109     void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
    110 
    111     int runCount() const { return fRunCount; }
    112 
    113     void push_back_run(int currRun) {
    114         SkASSERT(currRun < fRunCount);
    115         if (currRun > 0) {
    116             Run::SubRunInfo& newRun = fRuns[currRun].fSubRunInfo.back();
    117             Run::SubRunInfo& lastRun = fRuns[currRun - 1].fSubRunInfo.back();
    118             newRun.setAsSuccessor(lastRun);
    119         }
    120     }
    121 
    122     // sets the last subrun of runIndex to use distance field text
    123     void setSubRunHasDistanceFields(int runIndex, bool hasLCD) {
    124         Run& run = fRuns[runIndex];
    125         Run::SubRunInfo& subRun = run.fSubRunInfo.back();
    126         subRun.setUseLCDText(hasLCD);
    127         subRun.setDrawAsDistanceFields();
    128     }
    129 
    130     void setRunDrawAsPaths(int runIndex) {
    131         fRuns[runIndex].fDrawAsPaths = true;
    132     }
    133 
    134     void setMinAndMaxScale(SkScalar scaledMax, SkScalar scaledMin) {
    135         // we init fMaxMinScale and fMinMaxScale in the constructor
    136         fMaxMinScale = SkMaxScalar(scaledMax, fMaxMinScale);
    137         fMinMaxScale = SkMinScalar(scaledMin, fMinMaxScale);
    138     }
    139 
    140     // inits the override descriptor on the current run.  All following subruns must use this
    141     // descriptor
    142     void initOverride(int runIndex) {
    143         Run& run = fRuns[runIndex];
    144         // Push back a new subrun to fill and set the override descriptor
    145         run.push_back();
    146         run.fOverrideDescriptor.reset(new SkAutoDescriptor);
    147     }
    148 
    149     SkGlyphCache* setupCache(int runIndex,
    150                              const SkSurfaceProps& props,
    151                              SkPaint::FakeGamma fakeGamma,
    152                              const SkPaint& skPaint,
    153                              const SkMatrix* viewMatrix);
    154 
    155     // Appends a glyph to the blob.  If the glyph is too large, the glyph will be appended
    156     // as a path.
    157     void appendGlyph(int runIndex,
    158                      const SkRect& positions,
    159                      GrColor color,
    160                      GrBatchTextStrike* strike,
    161                      GrGlyph* glyph,
    162                      GrFontScaler* scaler, const SkGlyph& skGlyph,
    163                      SkScalar x, SkScalar y, SkScalar scale, bool applyVM);
    164 
    165     static size_t GetVertexStride(GrMaskFormat maskFormat) {
    166         switch (maskFormat) {
    167             case kA8_GrMaskFormat:
    168                 return kGrayTextVASize;
    169             case kARGB_GrMaskFormat:
    170                 return kColorTextVASize;
    171             default:
    172                 return kLCDTextVASize;
    173         }
    174     }
    175 
    176     bool mustRegenerate(const SkPaint& paint, GrColor color, const SkMaskFilter::BlurRec& blurRec,
    177                         const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
    178 
    179     // flush a GrAtlasTextBlob associated with a SkTextBlob
    180     void flushCached(GrContext* context,
    181                      GrDrawContext* dc,
    182                      const SkTextBlob* blob,
    183                      const SkSurfaceProps& props,
    184                      const GrDistanceFieldAdjustTable* distanceAdjustTable,
    185                      const SkPaint& skPaint,
    186                      const GrPaint& grPaint,
    187                      SkDrawFilter* drawFilter,
    188                      const GrClip& clip,
    189                      const SkMatrix& viewMatrix,
    190                      const SkIRect& clipBounds,
    191                      SkScalar x, SkScalar y);
    192 
    193     // flush a throwaway GrAtlasTextBlob *not* associated with an SkTextBlob
    194     void flushThrowaway(GrContext* context,
    195                         GrDrawContext* dc,
    196                         const SkSurfaceProps& props,
    197                         const GrDistanceFieldAdjustTable* distanceAdjustTable,
    198                         const SkPaint& skPaint,
    199                         const GrPaint& grPaint,
    200                         const GrClip& clip,
    201                         const SkMatrix& viewMatrix,
    202                         const SkIRect& clipBounds,
    203                         SkScalar x, SkScalar y);
    204 
    205     void computeSubRunBounds(SkRect* outBounds, int runIndex, int subRunIndex,
    206                              const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
    207         // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
    208         // into device space.
    209         // We handle vertex bounds differently for distance field text and bitmap text because
    210         // the vertex bounds of bitmap text are in device space.  If we are flushing multiple runs
    211         // from one blob then we are going to pay the price here of mapping the rect for each run.
    212         const Run& run = fRuns[runIndex];
    213         const Run::SubRunInfo& subRun = run.fSubRunInfo[subRunIndex];
    214         *outBounds = subRun.vertexBounds();
    215         if (subRun.drawAsDistanceFields()) {
    216             // Distance field text is positioned with the (X,Y) as part of the glyph position,
    217             // and currently the view matrix is applied on the GPU
    218             outBounds->offset(x - fInitialX, y - fInitialY);
    219             viewMatrix.mapRect(outBounds);
    220         } else {
    221             // Bitmap text is fully positioned on the CPU, and offset by an (X,Y) translate in
    222             // device space.
    223             SkMatrix boundsMatrix = fInitialViewMatrixInverse;
    224 
    225             boundsMatrix.postTranslate(-fInitialX, -fInitialY);
    226 
    227             boundsMatrix.postTranslate(x, y);
    228 
    229             boundsMatrix.postConcat(viewMatrix);
    230             boundsMatrix.mapRect(outBounds);
    231 
    232             // Due to floating point numerical inaccuracies, we have to round out here
    233             outBounds->roundOut(outBounds);
    234         }
    235     }
    236 
    237     // position + local coord
    238     static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
    239     static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
    240     static const size_t kLCDTextVASize = kGrayTextVASize;
    241     static const size_t kMaxVASize = kGrayTextVASize;
    242     static const int kVerticesPerGlyph = 4;
    243 
    244     static void AssertEqual(const GrAtlasTextBlob&, const GrAtlasTextBlob&);
    245 
    246     // The color here is the GrPaint color, and it is used to determine whether we
    247     // have to regenerate LCD text blobs.
    248     // We use this color vs the SkPaint color because it has the colorfilter applied.
    249     void initReusableBlob(GrColor color, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
    250         fPaintColor = color;
    251         this->setupViewMatrix(viewMatrix, x, y);
    252     }
    253 
    254     void initThrowawayBlob(const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
    255         this->setupViewMatrix(viewMatrix, x, y);
    256     }
    257 
    258     void regenInBatch(GrDrawBatch::Target* target, GrBatchFontCache* fontCache,
    259                       GrBlobRegenHelper *helper, int run, int subRun, SkGlyphCache** cache,
    260                       SkTypeface** typeface, GrFontScaler** scaler,
    261                       const SkDescriptor** desc, size_t vertexStride,
    262                       const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    263                       GrColor color,
    264                       void** vertices, size_t* byteCount, int* glyphCount);
    265 
    266     const Key& key() const { return fKey; }
    267 
    268     ~GrAtlasTextBlob() {
    269         for (int i = 0; i < fRunCount; i++) {
    270             fRuns[i].~Run();
    271         }
    272     }
    273 
    274     ////////////////////////////////////////////////////////////////////////////////////////////////
    275     // Internal test methods
    276     GrDrawBatch* test_createBatch(int glyphCount, int run, int subRun,
    277                                   const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
    278                                   const SkPaint& skPaint, const SkSurfaceProps& props,
    279                                   const GrDistanceFieldAdjustTable* distanceAdjustTable,
    280                                   GrBatchFontCache* cache);
    281 
    282 private:
    283     GrAtlasTextBlob()
    284         : fMaxMinScale(-SK_ScalarMax)
    285         , fMinMaxScale(SK_ScalarMax)
    286         , fTextType(0) {}
    287 
    288     void appendLargeGlyph(GrGlyph* glyph, GrFontScaler* scaler, const SkGlyph& skGlyph,
    289                           SkScalar x, SkScalar y, SkScalar scale, bool applyVM);
    290 
    291     inline void flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder,
    292                          int run, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
    293                          const SkPaint& skPaint, const SkSurfaceProps& props,
    294                          const GrDistanceFieldAdjustTable* distanceAdjustTable,
    295                          GrBatchFontCache* cache);
    296 
    297     void flushBigGlyphs(GrContext* context, GrDrawContext* dc,
    298                         const GrClip& clip, const SkPaint& skPaint,
    299                         const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    300                         const SkIRect& clipBounds);
    301 
    302     void flushRunAsPaths(GrContext* context,
    303                          GrDrawContext* dc,
    304                          const SkSurfaceProps& props,
    305                          const SkTextBlobRunIterator& it,
    306                          const GrClip& clip, const SkPaint& skPaint,
    307                          SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
    308                          const SkIRect& clipBounds, SkScalar x, SkScalar y);
    309 
    310     // This function will only be called when we are generating a blob from scratch. We record the
    311     // initial view matrix and initial offsets(x,y), because we record vertex bounds relative to
    312     // these numbers.  When blobs are reused with new matrices, we need to return to model space so
    313     // we can update the vertex bounds appropriately.
    314     void setupViewMatrix(const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
    315         fInitialViewMatrix = viewMatrix;
    316         if (!viewMatrix.invert(&fInitialViewMatrixInverse)) {
    317             fInitialViewMatrixInverse = SkMatrix::I();
    318             SkDebugf("Could not invert viewmatrix\n");
    319         }
    320         fInitialX = x;
    321         fInitialY = y;
    322 
    323         // make sure all initial subruns have the correct VM and X/Y applied
    324         for (int i = 0; i < fRunCount; i++) {
    325             fRuns[i].fSubRunInfo[0].init(fInitialViewMatrix, x, y);
    326         }
    327     }
    328 
    329     /*
    330      * Each Run inside of the blob can have its texture coordinates regenerated if required.
    331      * To determine if regeneration is necessary, fAtlasGeneration is used.  If there have been
    332      * any evictions inside of the atlas, then we will simply regenerate Runs.  We could track
    333      * this at a more fine grained level, but its not clear if this is worth it, as evictions
    334      * should be fairly rare.
    335      *
    336      * One additional point, each run can contain glyphs with any of the three mask formats.
    337      * We call these SubRuns.  Because a subrun must be a contiguous range, we have to create
    338      * a new subrun each time the mask format changes in a run.  In theory, a run can have as
    339      * many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8.  In
    340      * practice, the vast majority of runs have only a single subrun.
    341      *
    342      * Finally, for runs where the entire thing is too large for the GrAtlasTextContext to
    343      * handle, we have a bit to mark the run as flusahable via rendering as paths.  It is worth
    344      * pointing. It would be a bit expensive to figure out ahead of time whether or not a run
    345      * can flush in this manner, so we always allocate vertices for the run, regardless of
    346      * whether or not it is too large.  The benefit of this strategy is that we can always reuse
    347      * a blob allocation regardless of viewmatrix changes.  We could store positions for these
    348      * glyphs.  However, its not clear if this is a win because we'd still have to either go the
    349      * glyph cache to get the path at flush time, or hold onto the path in the cache, which
    350      * would greatly increase the memory of these cached items.
    351      */
    352     struct Run {
    353         Run()
    354             : fInitialized(false)
    355             , fDrawAsPaths(false) {
    356             // To ensure we always have one subrun, we push back a fresh run here
    357             fSubRunInfo.push_back();
    358         }
    359         struct SubRunInfo {
    360             SubRunInfo()
    361                 : fAtlasGeneration(GrBatchAtlas::kInvalidAtlasGeneration)
    362                 , fVertexStartIndex(0)
    363                 , fVertexEndIndex(0)
    364                 , fGlyphStartIndex(0)
    365                 , fGlyphEndIndex(0)
    366                 , fColor(GrColor_ILLEGAL)
    367                 , fMaskFormat(kA8_GrMaskFormat)
    368                 , fDrawAsDistanceFields(false)
    369                 , fUseLCDText(false) {
    370                 fVertexBounds.setLargestInverted();
    371             }
    372             SubRunInfo(const SubRunInfo& that)
    373                 : fBulkUseToken(that.fBulkUseToken)
    374                 , fStrike(SkSafeRef(that.fStrike.get()))
    375                 , fCurrentViewMatrix(that.fCurrentViewMatrix)
    376                 , fVertexBounds(that.fVertexBounds)
    377                 , fAtlasGeneration(that.fAtlasGeneration)
    378                 , fVertexStartIndex(that.fVertexStartIndex)
    379                 , fVertexEndIndex(that.fVertexEndIndex)
    380                 , fGlyphStartIndex(that.fGlyphStartIndex)
    381                 , fGlyphEndIndex(that.fGlyphEndIndex)
    382                 , fX(that.fX)
    383                 , fY(that.fY)
    384                 , fColor(that.fColor)
    385                 , fMaskFormat(that.fMaskFormat)
    386                 , fDrawAsDistanceFields(that.fDrawAsDistanceFields)
    387                 , fUseLCDText(that.fUseLCDText) {
    388             }
    389 
    390             // TODO when this object is more internal, drop the privacy
    391             void resetBulkUseToken() { fBulkUseToken.reset(); }
    392             GrBatchAtlas::BulkUseTokenUpdater* bulkUseToken() { return &fBulkUseToken; }
    393             void setStrike(GrBatchTextStrike* strike) { fStrike.reset(SkRef(strike)); }
    394             GrBatchTextStrike* strike() const { return fStrike.get(); }
    395 
    396             void setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
    397             uint64_t atlasGeneration() const { return fAtlasGeneration; }
    398 
    399             size_t byteCount() const { return fVertexEndIndex - fVertexStartIndex; }
    400             size_t vertexStartIndex() const { return fVertexStartIndex; }
    401             size_t vertexEndIndex() const { return fVertexEndIndex; }
    402             void appendVertices(size_t vertexStride) {
    403                 fVertexEndIndex += vertexStride * kVerticesPerGlyph;
    404             }
    405 
    406             uint32_t glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; }
    407             uint32_t glyphStartIndex() const { return fGlyphStartIndex; }
    408             uint32_t glyphEndIndex() const { return fGlyphEndIndex; }
    409             void glyphAppended() { fGlyphEndIndex++; }
    410             void setColor(GrColor color) { fColor = color; }
    411             GrColor color() const { return fColor; }
    412             void setMaskFormat(GrMaskFormat format) { fMaskFormat = format; }
    413             GrMaskFormat maskFormat() const { return fMaskFormat; }
    414 
    415             void setAsSuccessor(const SubRunInfo& prev) {
    416                 fGlyphStartIndex = prev.glyphEndIndex();
    417                 fGlyphEndIndex = prev.glyphEndIndex();
    418 
    419                 fVertexStartIndex = prev.vertexEndIndex();
    420                 fVertexEndIndex = prev.vertexEndIndex();
    421 
    422                 // copy over viewmatrix settings
    423                 this->init(prev.fCurrentViewMatrix, prev.fX, prev.fY);
    424             }
    425 
    426             const SkRect& vertexBounds() const { return fVertexBounds; }
    427             void joinGlyphBounds(const SkRect& glyphBounds) {
    428                 fVertexBounds.joinNonEmptyArg(glyphBounds);
    429             }
    430 
    431             void init(const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
    432                 fCurrentViewMatrix = viewMatrix;
    433                 fX = x;
    434                 fY = y;
    435             }
    436 
    437             // This function assumes the translation will be applied before it is called again
    438             void computeTranslation(const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    439                                     SkScalar*transX, SkScalar* transY);
    440 
    441             // df properties
    442             void setUseLCDText(bool useLCDText) { fUseLCDText = useLCDText; }
    443             bool hasUseLCDText() const { return fUseLCDText; }
    444             void setDrawAsDistanceFields() { fDrawAsDistanceFields = true; }
    445             bool drawAsDistanceFields() const { return fDrawAsDistanceFields; }
    446 
    447         private:
    448             GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken;
    449             SkAutoTUnref<GrBatchTextStrike> fStrike;
    450             SkMatrix fCurrentViewMatrix;
    451             SkRect fVertexBounds;
    452             uint64_t fAtlasGeneration;
    453             size_t fVertexStartIndex;
    454             size_t fVertexEndIndex;
    455             uint32_t fGlyphStartIndex;
    456             uint32_t fGlyphEndIndex;
    457             SkScalar fX;
    458             SkScalar fY;
    459             GrColor fColor;
    460             GrMaskFormat fMaskFormat;
    461             bool fDrawAsDistanceFields; // df property
    462             bool fUseLCDText; // df property
    463         };
    464 
    465         SubRunInfo& push_back() {
    466             // Forward glyph / vertex information to seed the new sub run
    467             SubRunInfo& newSubRun = fSubRunInfo.push_back();
    468             const SubRunInfo& prevSubRun = fSubRunInfo.fromBack(1);
    469 
    470             newSubRun.setAsSuccessor(prevSubRun);
    471             return newSubRun;
    472         }
    473         static const int kMinSubRuns = 1;
    474         SkAutoTUnref<SkTypeface> fTypeface;
    475         SkSTArray<kMinSubRuns, SubRunInfo> fSubRunInfo;
    476         SkAutoDescriptor fDescriptor;
    477 
    478         // Distance field text cannot draw coloremoji, and so has to fall back.  However,
    479         // though the distance field text and the coloremoji may share the same run, they
    480         // will have different descriptors.  If fOverrideDescriptor is non-nullptr, then it
    481         // will be used in place of the run's descriptor to regen texture coords
    482         SkAutoTDelete<SkAutoDescriptor> fOverrideDescriptor; // df properties
    483         bool fInitialized;
    484         bool fDrawAsPaths;
    485     };
    486 
    487     template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
    488     void regenInBatch(GrDrawBatch::Target* target,
    489                       GrBatchFontCache* fontCache,
    490                       GrBlobRegenHelper* helper,
    491                       Run* run, Run::SubRunInfo* info, SkGlyphCache** cache,
    492                       SkTypeface** typeface, GrFontScaler** scaler,
    493                       const SkDescriptor** desc,
    494                       int glyphCount, size_t vertexStride,
    495                       GrColor color, SkScalar transX,
    496                       SkScalar transY) const;
    497 
    498     inline GrDrawBatch* createBatch(const Run::SubRunInfo& info,
    499                                     int glyphCount, int run, int subRun,
    500                                     const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
    501                                     GrColor color,
    502                                     const SkPaint& skPaint, const SkSurfaceProps& props,
    503                                     const GrDistanceFieldAdjustTable* distanceAdjustTable,
    504                                     GrBatchFontCache* cache);
    505 
    506     struct BigGlyph {
    507         BigGlyph(const SkPath& path, SkScalar vx, SkScalar vy, SkScalar scale, bool applyVM)
    508             : fPath(path)
    509             , fScale(scale)
    510             , fX(vx)
    511             , fY(vy)
    512             , fApplyVM(applyVM) {}
    513         SkPath fPath;
    514         SkScalar fScale;
    515         SkScalar fX;
    516         SkScalar fY;
    517         bool fApplyVM;
    518     };
    519 
    520     struct StrokeInfo {
    521         SkScalar fFrameWidth;
    522         SkScalar fMiterLimit;
    523         SkPaint::Join fJoin;
    524     };
    525 
    526     enum TextType {
    527         kHasDistanceField_TextType = 0x1,
    528         kHasBitmap_TextType = 0x2,
    529     };
    530 
    531     // all glyph / vertex offsets are into these pools.
    532     unsigned char* fVertices;
    533     GrGlyph** fGlyphs;
    534     Run* fRuns;
    535     GrMemoryPool* fPool;
    536     SkMaskFilter::BlurRec fBlurRec;
    537     StrokeInfo fStrokeInfo;
    538     SkTArray<BigGlyph> fBigGlyphs;
    539     Key fKey;
    540     SkMatrix fInitialViewMatrix;
    541     SkMatrix fInitialViewMatrixInverse;
    542     size_t fSize;
    543     GrColor fPaintColor;
    544     SkScalar fInitialX;
    545     SkScalar fInitialY;
    546 
    547     // We can reuse distance field text, but only if the new viewmatrix would not result in
    548     // a mip change.  Because there can be multiple runs in a blob, we track the overall
    549     // maximum minimum scale, and minimum maximum scale, we can support before we need to regen
    550     SkScalar fMaxMinScale;
    551     SkScalar fMinMaxScale;
    552     int fRunCount;
    553     uint8_t fTextType;
    554 };
    555 
    556 #endif
    557