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 {
KeyKey52         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 
setupKey(const GrAtlasTextBlob::Key & key,const SkMaskFilter::BlurRec & blurRec,const SkPaint & paint)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 
GetKey(const GrAtlasTextBlob & blob)84     static const Key& GetKey(const GrAtlasTextBlob& blob) {
85         return blob.fKey;
86     }
87 
Hash(const Key & key)88     static uint32_t Hash(const Key& key) {
89         return SkChecksum::Murmur3(&key, sizeof(Key));
90     }
91 
delete(void * p)92     void operator delete(void* p) {
93         GrAtlasTextBlob* blob = reinterpret_cast<GrAtlasTextBlob*>(p);
94         blob->fPool->release(p);
95     }
new(size_t)96     void* operator new(size_t) {
97         SkFAIL("All blobs are created by placement new.");
98         return sk_malloc_throw(0);
99     }
100 
new(size_t,void * p)101     void* operator new(size_t, void* p) { return p; }
delete(void * target,void * placement)102     void operator delete(void* target, void* placement) {
103         ::operator delete(target, placement);
104     }
105 
hasDistanceField()106     bool hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); }
hasBitmap()107     bool hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
setHasDistanceField()108     void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
setHasBitmap()109     void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
110 
runCount()111     int runCount() const { return fRunCount; }
112 
push_back_run(int currRun)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
setSubRunHasDistanceFields(int runIndex,bool hasLCD)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 
setRunDrawAsPaths(int runIndex)130     void setRunDrawAsPaths(int runIndex) {
131         fRuns[runIndex].fDrawAsPaths = true;
132     }
133 
setMinAndMaxScale(SkScalar scaledMax,SkScalar scaledMin)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
initOverride(int runIndex)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 
GetVertexStride(GrMaskFormat maskFormat)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 
computeSubRunBounds(SkRect * outBounds,int runIndex,int subRunIndex,const SkMatrix & viewMatrix,SkScalar x,SkScalar y)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.
initReusableBlob(GrColor color,const SkMatrix & viewMatrix,SkScalar x,SkScalar y)249     void initReusableBlob(GrColor color, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
250         fPaintColor = color;
251         this->setupViewMatrix(viewMatrix, x, y);
252     }
253 
initThrowawayBlob(const SkMatrix & viewMatrix,SkScalar x,SkScalar y)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 
key()266     const Key& key() const { return fKey; }
267 
~GrAtlasTextBlob()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:
GrAtlasTextBlob()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.
setupViewMatrix(const SkMatrix & viewMatrix,SkScalar x,SkScalar y)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 {
RunRun353         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 {
SubRunInfoRun::SubRunInfo360             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             }
SubRunInfoRun::SubRunInfo372             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
resetBulkUseTokenRun::SubRunInfo391             void resetBulkUseToken() { fBulkUseToken.reset(); }
bulkUseTokenRun::SubRunInfo392             GrBatchAtlas::BulkUseTokenUpdater* bulkUseToken() { return &fBulkUseToken; }
setStrikeRun::SubRunInfo393             void setStrike(GrBatchTextStrike* strike) { fStrike.reset(SkRef(strike)); }
strikeRun::SubRunInfo394             GrBatchTextStrike* strike() const { return fStrike.get(); }
395 
setAtlasGenerationRun::SubRunInfo396             void setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
atlasGenerationRun::SubRunInfo397             uint64_t atlasGeneration() const { return fAtlasGeneration; }
398 
byteCountRun::SubRunInfo399             size_t byteCount() const { return fVertexEndIndex - fVertexStartIndex; }
vertexStartIndexRun::SubRunInfo400             size_t vertexStartIndex() const { return fVertexStartIndex; }
vertexEndIndexRun::SubRunInfo401             size_t vertexEndIndex() const { return fVertexEndIndex; }
appendVerticesRun::SubRunInfo402             void appendVertices(size_t vertexStride) {
403                 fVertexEndIndex += vertexStride * kVerticesPerGlyph;
404             }
405 
glyphCountRun::SubRunInfo406             uint32_t glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; }
glyphStartIndexRun::SubRunInfo407             uint32_t glyphStartIndex() const { return fGlyphStartIndex; }
glyphEndIndexRun::SubRunInfo408             uint32_t glyphEndIndex() const { return fGlyphEndIndex; }
glyphAppendedRun::SubRunInfo409             void glyphAppended() { fGlyphEndIndex++; }
setColorRun::SubRunInfo410             void setColor(GrColor color) { fColor = color; }
colorRun::SubRunInfo411             GrColor color() const { return fColor; }
setMaskFormatRun::SubRunInfo412             void setMaskFormat(GrMaskFormat format) { fMaskFormat = format; }
maskFormatRun::SubRunInfo413             GrMaskFormat maskFormat() const { return fMaskFormat; }
414 
setAsSuccessorRun::SubRunInfo415             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 
vertexBoundsRun::SubRunInfo426             const SkRect& vertexBounds() const { return fVertexBounds; }
joinGlyphBoundsRun::SubRunInfo427             void joinGlyphBounds(const SkRect& glyphBounds) {
428                 fVertexBounds.joinNonEmptyArg(glyphBounds);
429             }
430 
initRun::SubRunInfo431             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
setUseLCDTextRun::SubRunInfo442             void setUseLCDText(bool useLCDText) { fUseLCDText = useLCDText; }
hasUseLCDTextRun::SubRunInfo443             bool hasUseLCDText() const { return fUseLCDText; }
setDrawAsDistanceFieldsRun::SubRunInfo444             void setDrawAsDistanceFields() { fDrawAsDistanceFields = true; }
drawAsDistanceFieldsRun::SubRunInfo445             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 
push_backRun465         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 {
BigGlyphBigGlyph507         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