1 /* 2 * Copyright 2014 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 #include "GrStencilAndCoverTextContext.h" 9 #include "GrAtlasTextContext.h" 10 #include "GrDrawContext.h" 11 #include "GrDrawTarget.h" 12 #include "GrPath.h" 13 #include "GrPathRange.h" 14 #include "GrResourceProvider.h" 15 #include "GrTextUtils.h" 16 #include "SkAutoKern.h" 17 #include "SkDraw.h" 18 #include "SkDrawProcs.h" 19 #include "SkGlyphCache.h" 20 #include "SkGpuDevice.h" 21 #include "SkGrPriv.h" 22 #include "SkDrawFilter.h" 23 #include "SkPath.h" 24 #include "SkTextBlobRunIterator.h" 25 #include "SkTextMapStateProc.h" 26 #include "SkTextFormatParams.h" 27 28 #include "batches/GrDrawPathBatch.h" 29 30 template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) { 31 SkASSERT(*val); 32 delete *val; 33 } 34 35 template<typename T> static void delete_hash_table_entry(T* val) { 36 SkASSERT(*val); 37 delete *val; 38 } 39 40 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext() 41 : fFallbackTextContext(nullptr) 42 , fCacheSize(0) { 43 } 44 45 GrStencilAndCoverTextContext* 46 GrStencilAndCoverTextContext::Create() { 47 GrStencilAndCoverTextContext* textContext = new GrStencilAndCoverTextContext(); 48 textContext->fFallbackTextContext = GrAtlasTextContext::Create(); 49 50 return textContext; 51 } 52 53 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { 54 delete fFallbackTextContext; 55 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>); 56 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>); 57 } 58 59 bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) { 60 if (skPaint.getRasterizer()) { 61 return false; 62 } 63 if (skPaint.getMaskFilter()) { 64 return false; 65 } 66 if (SkPathEffect* pe = skPaint.getPathEffect()) { 67 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) { 68 return false; 69 } 70 } 71 // No hairlines. They would require new paths with customized strokes for every new draw matrix. 72 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth(); 73 } 74 75 void GrStencilAndCoverTextContext::drawText(GrContext* context, GrDrawContext* dc, 76 const GrClip& clip, const GrPaint& paint, 77 const SkPaint& skPaint, const SkMatrix& viewMatrix, 78 const SkSurfaceProps& props, 79 const char text[], size_t byteLength, 80 SkScalar x, SkScalar y, const SkIRect& clipBounds) { 81 if (context->abandoned()) { 82 return; 83 } else if (this->canDraw(skPaint, viewMatrix)) { 84 TextRun run(skPaint); 85 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); 86 run.setText(text, byteLength, x, y); 87 run.draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, 0, 0, 88 clipBounds, fFallbackTextContext, skPaint); 89 return; 90 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props, 91 *context->caps()->shaderCaps())) { 92 fFallbackTextContext->drawText(context, dc, clip, paint, skPaint, viewMatrix, props, text, 93 byteLength, x, y, clipBounds); 94 return; 95 } 96 97 // fall back to drawing as a path 98 GrTextUtils::DrawTextAsPath(context, dc, clip, skPaint, viewMatrix, text, byteLength, x, y, 99 clipBounds); 100 } 101 102 void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrDrawContext* dc, 103 const GrClip& clip, 104 const GrPaint& paint, 105 const SkPaint& skPaint, 106 const SkMatrix& viewMatrix, 107 const SkSurfaceProps& props, 108 const char text[], 109 size_t byteLength, 110 const SkScalar pos[], 111 int scalarsPerPosition, 112 const SkPoint& offset, 113 const SkIRect& clipBounds) { 114 if (context->abandoned()) { 115 return; 116 } else if (this->canDraw(skPaint, viewMatrix)) { 117 TextRun run(skPaint); 118 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); 119 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset); 120 run.draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, 0, 0, 121 clipBounds, fFallbackTextContext, skPaint); 122 return; 123 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props, 124 *context->caps()->shaderCaps())) { 125 fFallbackTextContext->drawPosText(context, dc, clip, paint, skPaint, viewMatrix, props, 126 text, byteLength, pos, 127 scalarsPerPosition, offset, clipBounds); 128 return; 129 } 130 131 // fall back to drawing as a path 132 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, skPaint, viewMatrix, text, 133 byteLength, pos, scalarsPerPosition, offset, clipBounds); 134 } 135 136 void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context, 137 GrDrawContext* dc, 138 const GrClip& clip, const SkPaint& skPaint, 139 const SkMatrix& viewMatrix, 140 const SkSurfaceProps& props, 141 const SkTextBlob* blob, 142 SkScalar x, SkScalar y, 143 SkDrawFilter* drawFilter, 144 const SkIRect& clipBounds) { 145 SkPaint runPaint = skPaint; 146 147 SkTextBlobRunIterator it(blob); 148 for (;!it.done(); it.next()) { 149 size_t textLen = it.glyphCount() * sizeof(uint16_t); 150 const SkPoint& offset = it.offset(); 151 152 // applyFontToPaint() always overwrites the exact same attributes, 153 // so it is safe to not re-seed the paint for this reason. 154 it.applyFontToPaint(&runPaint); 155 156 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { 157 // A false return from filter() means we should abort the current draw. 158 runPaint = skPaint; 159 continue; 160 } 161 162 runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint)); 163 164 GrPaint grPaint; 165 if (!SkPaintToGrPaint(context, runPaint, viewMatrix, &grPaint)) { 166 return; 167 } 168 169 switch (it.positioning()) { 170 case SkTextBlob::kDefault_Positioning: 171 this->drawText(context, dc, clip, grPaint, runPaint, viewMatrix, props, 172 (const char *)it.glyphs(), 173 textLen, x + offset.x(), y + offset.y(), clipBounds); 174 break; 175 case SkTextBlob::kHorizontal_Positioning: 176 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props, 177 (const char*)it.glyphs(), 178 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), 179 clipBounds); 180 break; 181 case SkTextBlob::kFull_Positioning: 182 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props, 183 (const char*)it.glyphs(), 184 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds); 185 break; 186 } 187 188 if (drawFilter) { 189 // A draw filter may change the paint arbitrarily, so we must re-seed in this case. 190 runPaint = skPaint; 191 } 192 } 193 } 194 195 void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrDrawContext* dc, 196 const GrClip& clip, const SkPaint& skPaint, 197 const SkMatrix& viewMatrix, 198 const SkSurfaceProps& props, 199 const SkTextBlob* skBlob, SkScalar x, SkScalar y, 200 SkDrawFilter* drawFilter, 201 const SkIRect& clipBounds) { 202 if (context->abandoned()) { 203 return; 204 } 205 206 if (!this->internalCanDraw(skPaint)) { 207 fFallbackTextContext->drawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob, 208 x, y, drawFilter, clipBounds); 209 return; 210 } 211 212 if (drawFilter || skPaint.getPathEffect()) { 213 // This draw can't be cached. 214 this->uncachedDrawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob, x, y, 215 drawFilter, clipBounds); 216 return; 217 } 218 219 GrPaint paint; 220 if (!SkPaintToGrPaint(context, skPaint, viewMatrix, &paint)) { 221 return; 222 } 223 224 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint); 225 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); 226 227 TextBlob::Iter iter(blob); 228 for (TextRun* run = iter.get(); run; run = iter.next()) { 229 run->draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, x, y, 230 clipBounds, fFallbackTextContext, skPaint); 231 run->releaseGlyphCache(); 232 } 233 } 234 235 const GrStencilAndCoverTextContext::TextBlob& 236 GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob, 237 const SkPaint& skPaint) { 238 // The font-related parameters are baked into the text blob and will override this skPaint, so 239 // the only remaining properties that can affect a TextBlob are the ones related to stroke. 240 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path. 241 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) { 242 fLRUList.remove(*found); 243 fLRUList.addToTail(*found); 244 return **found; 245 } 246 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint); 247 this->purgeToFit(*blob); 248 fBlobIdCache.set(skBlob->uniqueID(), blob); 249 fLRUList.addToTail(blob); 250 fCacheSize += blob->cpuMemorySize(); 251 return *blob; 252 } else { 253 GrStrokeInfo stroke(skPaint); 254 SkSTArray<4, uint32_t, true> key; 255 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt()); 256 key[0] = skBlob->uniqueID(); 257 stroke.asUniqueKeyFragment(&key[1]); 258 if (TextBlob** found = fBlobKeyCache.find(key)) { 259 fLRUList.remove(*found); 260 fLRUList.addToTail(*found); 261 return **found; 262 } 263 TextBlob* blob = new TextBlob(key, skBlob, skPaint); 264 this->purgeToFit(*blob); 265 fBlobKeyCache.set(blob); 266 fLRUList.addToTail(blob); 267 fCacheSize += blob->cpuMemorySize(); 268 return *blob; 269 } 270 } 271 272 void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) { 273 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs. 274 275 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize(); 276 while (fCacheSize && fCacheSize > maxSizeForNewBlob) { 277 TextBlob* lru = fLRUList.head(); 278 if (1 == lru->key().count()) { 279 // 1-length keys are unterstood to be the blob id. 280 fBlobIdCache.remove(lru->key()[0]); 281 } else { 282 fBlobKeyCache.remove(lru->key()); 283 } 284 fLRUList.remove(lru); 285 fCacheSize -= lru->cpuMemorySize(); 286 delete lru; 287 } 288 } 289 290 //////////////////////////////////////////////////////////////////////////////////////////////////// 291 292 void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob, 293 const SkPaint& skPaint) { 294 fCpuMemorySize = sizeof(TextBlob); 295 SkPaint runPaint(skPaint); 296 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) { 297 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint. 298 TextRun* run = this->addToTail(runPaint); 299 300 const char* text = reinterpret_cast<const char*>(iter.glyphs()); 301 size_t byteLength = sizeof(uint16_t) * iter.glyphCount(); 302 const SkPoint& runOffset = iter.offset(); 303 304 switch (iter.positioning()) { 305 case SkTextBlob::kDefault_Positioning: 306 run->setText(text, byteLength, runOffset.fX, runOffset.fY); 307 break; 308 case SkTextBlob::kHorizontal_Positioning: 309 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY)); 310 break; 311 case SkTextBlob::kFull_Positioning: 312 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0)); 313 break; 314 } 315 316 fCpuMemorySize += run->computeSizeInCache(); 317 } 318 } 319 320 //////////////////////////////////////////////////////////////////////////////////////////////////// 321 322 class GrStencilAndCoverTextContext::FallbackBlobBuilder { 323 public: 324 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {} 325 326 bool isInitialized() const { return SkToBool(fBuilder); } 327 328 void init(const SkPaint& font, SkScalar textRatio); 329 330 void appendGlyph(uint16_t glyphId, const SkPoint& pos); 331 332 const SkTextBlob* buildIfNeeded(int* count); 333 334 private: 335 enum { kWriteBufferSize = 1024 }; 336 337 void flush(); 338 339 SkAutoTDelete<SkTextBlobBuilder> fBuilder; 340 SkPaint fFont; 341 int fBuffIdx; 342 int fCount; 343 uint16_t fGlyphIds[kWriteBufferSize]; 344 SkPoint fPositions[kWriteBufferSize]; 345 }; 346 347 //////////////////////////////////////////////////////////////////////////////////////////////////// 348 349 GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) 350 : fStroke(fontAndStroke), 351 fFont(fontAndStroke), 352 fTotalGlyphCount(0), 353 fFallbackGlyphCount(0), 354 fDetachedGlyphCache(nullptr), 355 fLastDrawnGlyphsID(SK_InvalidUniqueID) { 356 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported. 357 358 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path 359 // rendering API for stroking). 360 fFont.setStyle(SkPaint::kFill_Style); 361 362 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) { 363 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke. 364 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(), 365 kStdFakeBoldInterpKeys, 366 kStdFakeBoldInterpValues, 367 kStdFakeBoldInterpLength); 368 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale); 369 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra, 370 true /*strokeAndFill*/); 371 372 fFont.setFakeBoldText(false); 373 } 374 375 if (!fFont.getPathEffect() && !fStroke.isDashed()) { 376 // We can draw the glyphs from canonically sized paths. 377 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; 378 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize(); 379 380 // Compensate for the glyphs being scaled by fTextRatio. 381 if (!fStroke.isFillStyle()) { 382 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, 383 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle()); 384 } 385 386 fFont.setLinearText(true); 387 fFont.setLCDRenderText(false); 388 fFont.setAutohinted(false); 389 fFont.setHinting(SkPaint::kNo_Hinting); 390 fFont.setSubpixelText(true); 391 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); 392 393 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() && 394 0 == fFont.getTextSkewX() && 395 !fFont.isFakeBoldText() && 396 !fFont.isVerticalText(); 397 } else { 398 fTextRatio = fTextInverseRatio = 1.0f; 399 fUsingRawGlyphPaths = false; 400 } 401 402 // Generate the key that will be used to cache the GPU glyph path objects. 403 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) { 404 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain(); 405 406 const SkTypeface* typeface = fFont.getTypeface(); 407 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1); 408 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; 409 } else { 410 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain(); 411 412 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt(); 413 if (fUsingRawGlyphPaths) { 414 const SkTypeface* typeface = fFont.getTypeface(); 415 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount); 416 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; 417 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount; 418 fStroke.asUniqueKeyFragment(&builder[2]); 419 } else { 420 SkGlyphCache* glyphCache = this->getGlyphCache(); 421 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface(); 422 const SkDescriptor* desc = &glyphCache->getDescriptor(); 423 int descDataCount = (desc->getLength() + 3) / 4; 424 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 425 2 + strokeDataCount + descDataCount); 426 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; 427 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16); 428 fStroke.asUniqueKeyFragment(&builder[2]); 429 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength()); 430 } 431 } 432 } 433 434 GrStencilAndCoverTextContext::TextRun::~TextRun() { 435 this->releaseGlyphCache(); 436 } 437 438 void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength, 439 SkScalar x, SkScalar y) { 440 SkASSERT(byteLength == 0 || text != nullptr); 441 442 SkGlyphCache* glyphCache = this->getGlyphCache(); 443 SkPaint::GlyphCacheProc glyphCacheProc = fFont.getGlyphCacheProc(true); 444 445 fTotalGlyphCount = fFont.countText(text, byteLength); 446 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType, 447 fTotalGlyphCount)); 448 449 const char* stop = text + byteLength; 450 451 // Measure first if needed. 452 if (fFont.getTextAlign() != SkPaint::kLeft_Align) { 453 SkFixed stopX = 0; 454 SkFixed stopY = 0; 455 456 const char* textPtr = text; 457 while (textPtr < stop) { 458 // We don't need x, y here, since all subpixel variants will have the 459 // same advance. 460 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr); 461 462 stopX += glyph.fAdvanceX; 463 stopY += glyph.fAdvanceY; 464 } 465 SkASSERT(textPtr == stop); 466 467 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; 468 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; 469 470 if (fFont.getTextAlign() == SkPaint::kCenter_Align) { 471 alignX = SkScalarHalf(alignX); 472 alignY = SkScalarHalf(alignY); 473 } 474 475 x -= alignX; 476 y -= alignY; 477 } 478 479 SkAutoKern autokern; 480 481 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); 482 483 SkFixed fx = SkScalarToFixed(x); 484 SkFixed fy = SkScalarToFixed(y); 485 FallbackBlobBuilder fallback; 486 while (text < stop) { 487 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text); 488 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio); 489 if (glyph.fWidth) { 490 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)), 491 &fallback); 492 } 493 494 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio); 495 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); 496 } 497 498 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); 499 } 500 501 void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength, 502 const SkScalar pos[], int scalarsPerPosition, 503 const SkPoint& offset) { 504 SkASSERT(byteLength == 0 || text != nullptr); 505 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 506 507 SkGlyphCache* glyphCache = this->getGlyphCache(); 508 SkPaint::GlyphCacheProc glyphCacheProc = fFont.getGlyphCacheProc(true); 509 510 fTotalGlyphCount = fFont.countText(text, byteLength); 511 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType, 512 fTotalGlyphCount)); 513 514 const char* stop = text + byteLength; 515 516 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition); 517 SkTextAlignProc alignProc(fFont.getTextAlign()); 518 FallbackBlobBuilder fallback; 519 while (text < stop) { 520 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text); 521 if (glyph.fWidth) { 522 SkPoint tmsLoc; 523 tmsProc(pos, &tmsLoc); 524 SkPoint loc; 525 alignProc(tmsLoc, glyph, &loc); 526 527 this->appendGlyph(glyph, loc, &fallback); 528 } 529 pos += scalarsPerPosition; 530 } 531 532 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); 533 } 534 535 GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const { 536 GrPathRange* glyphs = static_cast<GrPathRange*>( 537 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey)); 538 if (nullptr == glyphs) { 539 if (fUsingRawGlyphPaths) { 540 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke); 541 } else { 542 SkGlyphCache* cache = this->getGlyphCache(); 543 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(), 544 &cache->getDescriptor(), 545 fStroke); 546 } 547 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs); 548 } 549 return glyphs; 550 } 551 552 inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph, 553 const SkPoint& pos, 554 FallbackBlobBuilder* fallback) { 555 // Stick the glyphs we can't draw into the fallback text blob. 556 if (SkMask::kARGB32_Format == glyph.fMaskFormat) { 557 if (!fallback->isInitialized()) { 558 fallback->init(fFont, fTextRatio); 559 } 560 fallback->appendGlyph(glyph.getGlyphID(), pos); 561 } else { 562 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(), 563 fTextInverseRatio * pos.y()); 564 } 565 } 566 567 void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, 568 GrDrawContext* dc, 569 GrPipelineBuilder* pipelineBuilder, 570 GrColor color, 571 const SkMatrix& viewMatrix, 572 const SkSurfaceProps& props, 573 SkScalar x, SkScalar y, 574 const SkIRect& clipBounds, 575 GrAtlasTextContext* fallbackTextContext, 576 const SkPaint& originalSkPaint) const { 577 SkASSERT(fInstanceData); 578 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias()); 579 580 if (fInstanceData->count()) { 581 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias()); 582 583 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, 584 kZero_StencilOp, 585 kKeep_StencilOp, 586 kNotEqual_StencilFunc, 587 0xffff, 588 0x0000, 589 0xffff); 590 591 *pipelineBuilder->stencil() = kStencilPass; 592 593 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx)); 594 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) { 595 // Either this is the first draw or the glyphs object was purged since last draw. 596 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count()); 597 fLastDrawnGlyphsID = glyphs->getUniqueID(); 598 } 599 600 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy 601 // the entire dst. Realistically this is a moot point, because any context that supports 602 // NV_path_rendering will also support NV_blend_equation_advanced. 603 // For clipping we'll just skip any optimizations based on the bounds. This does, however, 604 // hurt batching. 605 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(), 606 pipelineBuilder->getRenderTarget()->height()); 607 608 SkAutoTUnref<GrDrawPathBatchBase> batch( 609 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x, 610 fTextInverseRatio * y, color, 611 GrPathRendering::kWinding_FillType, glyphs, fInstanceData, 612 bounds)); 613 614 dc->drawPathBatch(*pipelineBuilder, batch); 615 } 616 617 if (fFallbackTextBlob) { 618 SkPaint fallbackSkPaint(originalSkPaint); 619 fStroke.applyToPaint(&fallbackSkPaint); 620 if (!fStroke.isFillStyle()) { 621 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); 622 } 623 624 fallbackTextContext->drawTextBlob(ctx, dc, pipelineBuilder->clip(), fallbackSkPaint, 625 viewMatrix, props, fFallbackTextBlob, x, y, nullptr, 626 clipBounds); 627 } 628 } 629 630 SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const { 631 if (!fDetachedGlyphCache) { 632 fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::FakeGamma::Off, nullptr); 633 } 634 return fDetachedGlyphCache; 635 } 636 637 638 void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const { 639 if (fDetachedGlyphCache) { 640 SkGlyphCache::AttachCache(fDetachedGlyphCache); 641 fDetachedGlyphCache = nullptr; 642 } 643 } 644 645 size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const { 646 size_t size = sizeof(TextRun) + fGlyphPathsKey.size(); 647 // The instance data always reserves enough space for every glyph. 648 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float)); 649 if (fInstanceData) { 650 size += sizeof(InstanceData); 651 } 652 if (fFallbackTextBlob) { 653 size += sizeof(SkTextBlob); 654 } 655 return size; 656 } 657 658 //////////////////////////////////////////////////////////////////////////////////////////////////// 659 660 void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font, 661 SkScalar textRatio) { 662 SkASSERT(!this->isInitialized()); 663 fBuilder.reset(new SkTextBlobBuilder); 664 fFont = font; 665 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align. 666 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 667 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs 668 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved. 669 fFont.setSubpixelText(false); 670 fFont.setTextSize(fFont.getTextSize() * textRatio); 671 fBuffIdx = 0; 672 } 673 674 void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId, 675 const SkPoint& pos) { 676 SkASSERT(this->isInitialized()); 677 if (fBuffIdx >= kWriteBufferSize) { 678 this->flush(); 679 } 680 fGlyphIds[fBuffIdx] = glyphId; 681 fPositions[fBuffIdx] = pos; 682 fBuffIdx++; 683 fCount++; 684 } 685 686 void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() { 687 SkASSERT(this->isInitialized()); 688 SkASSERT(fBuffIdx <= kWriteBufferSize); 689 if (!fBuffIdx) { 690 return; 691 } 692 // This will automatically merge with previous runs since we use the same font. 693 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx); 694 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t)); 695 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar)); 696 fBuffIdx = 0; 697 } 698 699 const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) { 700 *count = fCount; 701 if (fCount) { 702 this->flush(); 703 return fBuilder->build(); 704 } 705 return nullptr; 706 } 707