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