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