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