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