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