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