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 GrAtlasTextContext_DEFINED 9 #define GrAtlasTextContext_DEFINED 10 11 #include "GrTextContext.h" 12 13 #include "GrBatchAtlas.h" 14 #include "GrBatchFontCache.h" 15 #include "GrGeometryProcessor.h" 16 #include "SkDescriptor.h" 17 #include "GrMemoryPool.h" 18 #include "SkMaskFilter.h" 19 #include "SkTextBlob.h" 20 #include "SkTInternalLList.h" 21 22 #ifdef GR_TEST_UTILS 23 #include "GrBatchTest.h" 24 #endif 25 26 class BitmapTextBatch; 27 class GrPipelineBuilder; 28 class GrTextBlobCache; 29 30 /* 31 * This class implements GrTextContext using standard bitmap fonts, and can also process textblobs. 32 * TODO replace GrBitmapTextContext 33 */ 34 class GrAtlasTextContext : public GrTextContext { 35 public: 36 static GrAtlasTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&, 37 bool enableDistanceFields); 38 39 private: 40 GrAtlasTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&, 41 bool enableDistanceFields); ~GrAtlasTextContext()42 ~GrAtlasTextContext() override {} 43 44 bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&, 45 const SkPaint&, const SkMatrix& viewMatrix) override; 46 47 void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, 48 const SkMatrix& viewMatrix, const char text[], size_t byteLength, 49 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override; 50 void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, 51 const SkMatrix& viewMatrix, 52 const char text[], size_t byteLength, 53 const SkScalar pos[], int scalarsPerPosition, 54 const SkPoint& offset, const SkIRect& regionClipBounds) override; 55 void drawTextBlob(GrRenderTarget*, const GrClip&, const SkPaint&, 56 const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y, 57 SkDrawFilter*, const SkIRect& clipBounds) override; 58 59 /* 60 * A BitmapTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing 61 * on the GPU. These are initially created with valid positions and colors, but invalid 62 * texture coordinates. The BitmapTextBlob itself has a few Blob-wide properties, and also 63 * consists of a number of runs. Runs inside a blob are flushed individually so they can be 64 * reordered. 65 * 66 * The only thing(aside from a memcopy) required to flush a BitmapTextBlob is to ensure that 67 * the GrAtlas will not evict anything the Blob needs. 68 */ 69 struct BitmapTextBlob : public SkRefCnt { 70 SK_DECLARE_INTERNAL_LLIST_INTERFACE(BitmapTextBlob); 71 72 /* 73 * Each Run inside of the blob can have its texture coordinates regenerated if required. 74 * To determine if regeneration is necessary, fAtlasGeneration is used. If there have been 75 * any evictions inside of the atlas, then we will simply regenerate Runs. We could track 76 * this at a more fine grained level, but its not clear if this is worth it, as evictions 77 * should be fairly rare. 78 * 79 * One additional point, each run can contain glyphs with any of the three mask formats. 80 * We call these SubRuns. Because a subrun must be a contiguous range, we have to create 81 * a new subrun each time the mask format changes in a run. In theory, a run can have as 82 * many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8. In 83 * practice, the vast majority of runs have only a single subrun. 84 * 85 * Finally, for runs where the entire thing is too large for the GrAtlasTextContext to 86 * handle, we have a bit to mark the run as flusahable via rendering as paths. It is worth 87 * pointing. It would be a bit expensive to figure out ahead of time whether or not a run 88 * can flush in this manner, so we always allocate vertices for the run, regardless of 89 * whether or not it is too large. The benefit of this strategy is that we can always reuse 90 * a blob allocation regardless of viewmatrix changes. We could store positions for these 91 * glyphs. However, its not clear if this is a win because we'd still have to either go the 92 * glyph cache to get the path at flush time, or hold onto the path in the cache, which 93 * would greatly increase the memory of these cached items. 94 */ 95 struct Run { RunBitmapTextBlob::Run96 Run() 97 : fColor(GrColor_ILLEGAL) 98 , fInitialized(false) 99 , fDrawAsPaths(false) { 100 fVertexBounds.setLargestInverted(); 101 // To ensure we always have one subrun, we push back a fresh run here 102 fSubRunInfo.push_back(); 103 } 104 struct SubRunInfo { SubRunInfoBitmapTextBlob::Run::SubRunInfo105 SubRunInfo() 106 : fAtlasGeneration(GrBatchAtlas::kInvalidAtlasGeneration) 107 , fVertexStartIndex(0) 108 , fVertexEndIndex(0) 109 , fGlyphStartIndex(0) 110 , fGlyphEndIndex(0) 111 , fDrawAsDistanceFields(false) {} 112 // Distance field text cannot draw coloremoji, and so has to fall back. However, 113 // though the distance field text and the coloremoji may share the same run, they 114 // will have different descriptors. If fOverrideDescriptor is non-NULL, then it 115 // will be used in place of the run's descriptor to regen texture coords 116 // TODO we could have a descriptor cache, it would reduce the size of these blobs 117 // significantly, and then the subrun could just have a refed pointer to the 118 // correct descriptor. 119 GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken; 120 uint64_t fAtlasGeneration; 121 size_t fVertexStartIndex; 122 size_t fVertexEndIndex; 123 uint32_t fGlyphStartIndex; 124 uint32_t fGlyphEndIndex; 125 SkScalar fTextRatio; // df property 126 GrMaskFormat fMaskFormat; 127 bool fDrawAsDistanceFields; // df property 128 bool fUseLCDText; // df property 129 }; 130 push_backBitmapTextBlob::Run131 SubRunInfo& push_back() { 132 // Forward glyph / vertex information to seed the new sub run 133 SubRunInfo& prevSubRun = fSubRunInfo.back(); 134 SubRunInfo& newSubRun = fSubRunInfo.push_back(); 135 newSubRun.fGlyphStartIndex = prevSubRun.fGlyphEndIndex; 136 newSubRun.fGlyphEndIndex = prevSubRun.fGlyphEndIndex; 137 138 newSubRun.fVertexStartIndex = prevSubRun.fVertexEndIndex; 139 newSubRun.fVertexEndIndex = prevSubRun.fVertexEndIndex; 140 return newSubRun; 141 } 142 static const int kMinSubRuns = 1; 143 SkAutoTUnref<GrBatchTextStrike> fStrike; 144 SkAutoTUnref<SkTypeface> fTypeface; 145 SkRect fVertexBounds; 146 SkSTArray<kMinSubRuns, SubRunInfo> fSubRunInfo; 147 SkAutoDescriptor fDescriptor; 148 SkAutoTDelete<SkAutoDescriptor> fOverrideDescriptor; // df properties 149 GrColor fColor; 150 bool fInitialized; 151 bool fDrawAsPaths; 152 }; 153 154 struct BigGlyph { BigGlyphBitmapTextBlob::BigGlyph155 BigGlyph(const SkPath& path, SkScalar vx, SkScalar vy) 156 : fPath(path) 157 , fVx(vx) 158 , fVy(vy) {} 159 SkPath fPath; 160 SkScalar fVx; 161 SkScalar fVy; 162 }; 163 164 struct Key { KeyBitmapTextBlob::Key165 Key() { 166 sk_bzero(this, sizeof(Key)); 167 } 168 uint32_t fUniqueID; 169 // Color may affect the gamma of the mask we generate, but in a fairly limited way. 170 // Each color is assigned to on of a fixed number of buckets based on its 171 // luminance. For each luminance bucket there is a "canonical color" that 172 // represents the bucket. This functionality is currently only supported for A8 173 SkColor fCanonicalColor; 174 SkPaint::Style fStyle; 175 SkPixelGeometry fPixelGeometry; 176 bool fHasBlur; 177 178 bool operator==(const Key& other) const { 179 return 0 == memcmp(this, &other, sizeof(Key)); 180 } 181 }; 182 183 struct StrokeInfo { 184 SkScalar fFrameWidth; 185 SkScalar fMiterLimit; 186 SkPaint::Join fJoin; 187 }; 188 189 enum TextType { 190 kHasDistanceField_TextType = 0x1, 191 kHasBitmap_TextType = 0x2, 192 }; 193 194 // all glyph / vertex offsets are into these pools. 195 unsigned char* fVertices; 196 GrGlyph** fGlyphs; 197 Run* fRuns; 198 GrMemoryPool* fPool; 199 SkMaskFilter::BlurRec fBlurRec; 200 StrokeInfo fStrokeInfo; 201 SkTArray<BigGlyph> fBigGlyphs; 202 Key fKey; 203 SkMatrix fViewMatrix; 204 SkColor fPaintColor; 205 SkScalar fX; 206 SkScalar fY; 207 208 // We can reuse distance field text, but only if the new viewmatrix would not result in 209 // a mip change. Because there can be multiple runs in a blob, we track the overall 210 // maximum minimum scale, and minimum maximum scale, we can support before we need to regen 211 SkScalar fMaxMinScale; 212 SkScalar fMinMaxScale; 213 int fRunCount; 214 uint8_t fTextType; 215 BitmapTextBlobBitmapTextBlob216 BitmapTextBlob() 217 : fMaxMinScale(-SK_ScalarMax) 218 , fMinMaxScale(SK_ScalarMax) 219 , fTextType(0) {} 220 ~BitmapTextBlobBitmapTextBlob221 ~BitmapTextBlob() override { 222 for (int i = 0; i < fRunCount; i++) { 223 fRuns[i].~Run(); 224 } 225 } 226 GetKeyBitmapTextBlob227 static const Key& GetKey(const BitmapTextBlob& blob) { 228 return blob.fKey; 229 } 230 HashBitmapTextBlob231 static uint32_t Hash(const Key& key) { 232 return SkChecksum::Murmur3(&key, sizeof(Key)); 233 } 234 deleteBitmapTextBlob235 void operator delete(void* p) { 236 BitmapTextBlob* blob = reinterpret_cast<BitmapTextBlob*>(p); 237 blob->fPool->release(p); 238 } newBitmapTextBlob239 void* operator new(size_t) { 240 SkFAIL("All blobs are created by placement new."); 241 return sk_malloc_throw(0); 242 } 243 newBitmapTextBlob244 void* operator new(size_t, void* p) { return p; } deleteBitmapTextBlob245 void operator delete(void* target, void* placement) { 246 ::operator delete(target, placement); 247 } 248 hasDistanceFieldBitmapTextBlob249 bool hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); } hasBitmapBitmapTextBlob250 bool hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); } setHasDistanceFieldBitmapTextBlob251 void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; } setHasBitmapBitmapTextBlob252 void setHasBitmap() { fTextType |= kHasBitmap_TextType; } 253 }; 254 255 typedef BitmapTextBlob::Run Run; 256 typedef Run::SubRunInfo PerSubRunInfo; 257 258 inline bool canDrawAsDistanceFields(const SkPaint&, const SkMatrix& viewMatrix); 259 BitmapTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint, 260 const SkMatrix& viewMatrix, SkGlyphCache** cache, 261 SkPaint* dfPaint, SkScalar* textRatio); 262 void bmpAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top, 263 GrColor color, GrFontScaler*, const SkIRect& clipRect); 264 bool dfAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, SkScalar sx, SkScalar sy, 265 GrColor color, GrFontScaler*, const SkIRect& clipRect, SkScalar textRatio, 266 const SkMatrix& viewMatrix); 267 inline void appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph, 268 GrFontScaler* scaler, SkScalar x, SkScalar y); 269 inline void appendGlyphCommon(BitmapTextBlob*, Run*, Run::SubRunInfo*, 270 const SkRect& positions, GrColor color, 271 size_t vertexStride, bool useVertexColor, 272 GrGlyph*); 273 274 inline void flushRunAsPaths(const SkTextBlob::RunIterator&, const SkPaint&, SkDrawFilter*, 275 const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x, 276 SkScalar y); 277 inline BitmapTextBatch* createBatch(BitmapTextBlob*, const PerSubRunInfo&, 278 int glyphCount, int run, int subRun, 279 GrColor, SkScalar transX, SkScalar transY, 280 const SkPaint&); 281 inline void flushRun(GrDrawTarget*, GrPipelineBuilder*, BitmapTextBlob*, int run, GrColor, 282 SkScalar transX, SkScalar transY, const SkPaint&); 283 inline void flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt, 284 const SkPaint& skPaint, 285 SkScalar transX, SkScalar transY, const SkIRect& clipBounds); 286 287 // We have to flush SkTextBlobs differently from drawText / drawPosText 288 void flush(GrDrawTarget*, const SkTextBlob*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&, 289 const GrPaint&, SkDrawFilter*, const GrClip&, const SkMatrix& viewMatrix, 290 const SkIRect& clipBounds, SkScalar x, SkScalar y, SkScalar transX, SkScalar transY); 291 void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&, 292 const GrPaint&, const GrClip&, const SkIRect& clipBounds); 293 294 // A helper for drawing BitmapText in a run of distance fields 295 inline void fallbackDrawPosText(BitmapTextBlob*, int runIndex, 296 GrRenderTarget*, const GrClip&, 297 const GrPaint&, 298 const SkPaint&, const SkMatrix& viewMatrix, 299 const SkTDArray<char>& fallbackTxt, 300 const SkTDArray<SkScalar>& fallbackPos, 301 int scalarsPerPosition, 302 const SkPoint& offset, 303 const SkIRect& clipRect); 304 305 void internalDrawBMPText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&, 306 GrColor color, const SkMatrix& viewMatrix, 307 const char text[], size_t byteLength, 308 SkScalar x, SkScalar y, const SkIRect& clipRect); 309 void internalDrawBMPPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&, 310 GrColor color, const SkMatrix& viewMatrix, 311 const char text[], size_t byteLength, 312 const SkScalar pos[], int scalarsPerPosition, 313 const SkPoint& offset, const SkIRect& clipRect); 314 315 void internalDrawDFText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&, 316 GrColor color, const SkMatrix& viewMatrix, 317 const char text[], size_t byteLength, 318 SkScalar x, SkScalar y, const SkIRect& clipRect, 319 SkScalar textRatio, 320 SkTDArray<char>* fallbackTxt, 321 SkTDArray<SkScalar>* fallbackPos, 322 SkPoint* offset, const SkPaint& origPaint); 323 void internalDrawDFPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&, 324 GrColor color, const SkMatrix& viewMatrix, 325 const char text[], size_t byteLength, 326 const SkScalar pos[], int scalarsPerPosition, 327 const SkPoint& offset, const SkIRect& clipRect, 328 SkScalar textRatio, 329 SkTDArray<char>* fallbackTxt, 330 SkTDArray<SkScalar>* fallbackPos); 331 332 // sets up the descriptor on the blob and returns a detached cache. Client must attach 333 inline static GrColor ComputeCanonicalColor(const SkPaint&, bool lcd); 334 inline SkGlyphCache* setupCache(Run*, const SkPaint&, const SkMatrix* viewMatrix, bool noGamma); 335 static inline bool MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY, 336 const BitmapTextBlob&, const SkPaint&, 337 const SkMaskFilter::BlurRec&, 338 const SkMatrix& viewMatrix, SkScalar x, SkScalar y); 339 void regenerateTextBlob(BitmapTextBlob* bmp, const SkPaint& skPaint, GrColor, 340 const SkMatrix& viewMatrix, 341 const SkTextBlob* blob, SkScalar x, SkScalar y, 342 SkDrawFilter* drawFilter, const SkIRect& clipRect, GrRenderTarget*, 343 const GrClip&, const GrPaint&); 344 inline static bool HasLCD(const SkTextBlob*); 345 inline void initDistanceFieldPaint(BitmapTextBlob*, SkPaint*, SkScalar* textRatio, 346 const SkMatrix&); 347 348 // Test methods 349 // TODO this is really ugly. It'd be much nicer if positioning could be moved to batch 350 inline BitmapTextBlob* createDrawTextBlob(GrRenderTarget*, const GrClip&, const GrPaint&, 351 const SkPaint&, const SkMatrix& viewMatrix, 352 const char text[], size_t byteLength, 353 SkScalar x, SkScalar y, 354 const SkIRect& regionClipBounds); 355 inline BitmapTextBlob* createDrawPosTextBlob(GrRenderTarget*, const GrClip&, const GrPaint&, 356 const SkPaint&, const SkMatrix& viewMatrix, 357 const char text[], size_t byteLength, 358 const SkScalar pos[], int scalarsPerPosition, 359 const SkPoint& offset, 360 const SkIRect& regionClipBounds); 361 362 // Distance field text needs this table to compute a value for use in the fragment shader. 363 // Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be 364 // refcnted and malloced 365 struct DistanceAdjustTable : public SkNVRefCnt<DistanceAdjustTable> { DistanceAdjustTableDistanceAdjustTable366 DistanceAdjustTable(float gamma) { this->buildDistanceAdjustTable(gamma); } ~DistanceAdjustTableDistanceAdjustTable367 ~DistanceAdjustTable() { SkDELETE_ARRAY(fTable); } 368 369 void buildDistanceAdjustTable(float gamma); 370 371 SkScalar& operator[] (int i) { 372 return fTable[i]; 373 } 374 375 const SkScalar& operator[] (int i) const { 376 return fTable[i]; 377 } 378 379 SkScalar* fTable; 380 }; 381 382 GrBatchTextStrike* fCurrStrike; 383 GrTextBlobCache* fCache; 384 bool fEnableDFRendering; 385 SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable; 386 387 friend class GrTextBlobCache; 388 friend class BitmapTextBatch; 389 390 #ifdef GR_TEST_UTILS 391 BATCH_TEST_FRIEND(TextBlobBatch); 392 #endif 393 394 typedef GrTextContext INHERITED; 395 }; 396 397 #endif 398