| OLD | NEW |
| (Empty) |
| 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 "SkAutoKern.h" | |
| 16 #include "SkDraw.h" | |
| 17 #include "SkDrawProcs.h" | |
| 18 #include "SkGlyphCache.h" | |
| 19 #include "SkGpuDevice.h" | |
| 20 #include "SkGrPriv.h" | |
| 21 #include "SkPath.h" | |
| 22 #include "SkTextBlobRunIterator.h" | |
| 23 #include "SkTextMapStateProc.h" | |
| 24 #include "SkTextFormatParams.h" | |
| 25 | |
| 26 #include "batches/GrDrawPathBatch.h" | |
| 27 | |
| 28 template<typename Key, typename Val> static void delete_hash_map_entry(const Key
&, Val* val) { | |
| 29 SkASSERT(*val); | |
| 30 delete *val; | |
| 31 } | |
| 32 | |
| 33 template<typename T> static void delete_hash_table_entry(T* val) { | |
| 34 SkASSERT(*val); | |
| 35 delete *val; | |
| 36 } | |
| 37 | |
| 38 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context, | |
| 39 const SkSurfaceProps&
surfaceProps) | |
| 40 : INHERITED(context, surfaceProps), | |
| 41 fCacheSize(0) { | |
| 42 } | |
| 43 | |
| 44 GrStencilAndCoverTextContext* | |
| 45 GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& s
urfaceProps) { | |
| 46 GrStencilAndCoverTextContext* textContext = | |
| 47 new GrStencilAndCoverTextContext(context, surfaceProps); | |
| 48 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surf
aceProps); | |
| 49 | |
| 50 return textContext; | |
| 51 } | |
| 52 | |
| 53 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { | |
| 54 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>); | |
| 55 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>); | |
| 56 } | |
| 57 | |
| 58 bool 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 ev
ery new draw matrix. | |
| 71 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrok
eWidth(); | |
| 72 } | |
| 73 | |
| 74 void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, | |
| 75 const GrClip& clip, | |
| 76 const GrPaint& paint, | |
| 77 const SkPaint& skPaint, | |
| 78 const SkMatrix& viewMatrix, | |
| 79 const char text[], | |
| 80 size_t byteLength, | |
| 81 SkScalar x, SkScalar y, | |
| 82 const SkIRect& clipBounds) { | |
| 83 TextRun run(skPaint); | |
| 84 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); | |
| 85 run.setText(text, byteLength, x, y); | |
| 86 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0,
clipBounds, | |
| 87 fFallbackTextContext, skPaint); | |
| 88 } | |
| 89 | |
| 90 void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, | |
| 91 const GrClip& clip, | |
| 92 const GrPaint& paint, | |
| 93 const SkPaint& skPaint, | |
| 94 const SkMatrix& viewMatrix, | |
| 95 const char text[], | |
| 96 size_t byteLength, | |
| 97 const SkScalar pos[], | |
| 98 int scalarsPerPosition, | |
| 99 const SkPoint& offset, | |
| 100 const SkIRect& clipBounds) { | |
| 101 TextRun run(skPaint); | |
| 102 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); | |
| 103 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset); | |
| 104 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0,
clipBounds, | |
| 105 fFallbackTextContext, skPaint); | |
| 106 } | |
| 107 | |
| 108 void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, | |
| 109 const GrClip& clip, const SkPain
t& skPaint, | |
| 110 const SkMatrix& viewMatrix, | |
| 111 const SkTextBlob* skBlob, SkScal
ar x, SkScalar y, | |
| 112 SkDrawFilter* drawFilter, | |
| 113 const SkIRect& clipBounds) { | |
| 114 if (!this->internalCanDraw(skPaint)) { | |
| 115 fFallbackTextContext->drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob
, x, y, | |
| 116 drawFilter, clipBounds); | |
| 117 return; | |
| 118 } | |
| 119 | |
| 120 if (drawFilter || skPaint.getPathEffect()) { | |
| 121 // This draw can't be cached. | |
| 122 INHERITED::drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y, dra
wFilter, | |
| 123 clipBounds); | |
| 124 return; | |
| 125 } | |
| 126 | |
| 127 if (fContext->abandoned()) { | |
| 128 return; | |
| 129 } | |
| 130 | |
| 131 GrPaint paint; | |
| 132 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) { | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint); | |
| 137 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); | |
| 138 | |
| 139 TextBlob::Iter iter(blob); | |
| 140 for (TextRun* run = iter.get(); run; run = iter.next()) { | |
| 141 run->draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix,
x, y, clipBounds, | |
| 142 fFallbackTextContext, skPaint); | |
| 143 run->releaseGlyphCache(); | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 const GrStencilAndCoverTextContext::TextBlob& | |
| 148 GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob, | |
| 149 const SkPaint& skPaint) { | |
| 150 // The font-related parameters are baked into the text blob and will overrid
e this skPaint, so | |
| 151 // the only remaining properties that can affect a TextBlob are the ones rel
ated to stroke. | |
| 152 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path. | |
| 153 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) { | |
| 154 fLRUList.remove(*found); | |
| 155 fLRUList.addToTail(*found); | |
| 156 return **found; | |
| 157 } | |
| 158 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint); | |
| 159 this->purgeToFit(*blob); | |
| 160 fBlobIdCache.set(skBlob->uniqueID(), blob); | |
| 161 fLRUList.addToTail(blob); | |
| 162 fCacheSize += blob->cpuMemorySize(); | |
| 163 return *blob; | |
| 164 } else { | |
| 165 GrStrokeInfo stroke(skPaint); | |
| 166 SkSTArray<4, uint32_t, true> key; | |
| 167 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt()); | |
| 168 key[0] = skBlob->uniqueID(); | |
| 169 stroke.asUniqueKeyFragment(&key[1]); | |
| 170 if (TextBlob** found = fBlobKeyCache.find(key)) { | |
| 171 fLRUList.remove(*found); | |
| 172 fLRUList.addToTail(*found); | |
| 173 return **found; | |
| 174 } | |
| 175 TextBlob* blob = new TextBlob(key, skBlob, skPaint); | |
| 176 this->purgeToFit(*blob); | |
| 177 fBlobKeyCache.set(blob); | |
| 178 fLRUList.addToTail(blob); | |
| 179 fCacheSize += blob->cpuMemorySize(); | |
| 180 return *blob; | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) { | |
| 185 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for
caching text blobs. | |
| 186 | |
| 187 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize(); | |
| 188 while (fCacheSize && fCacheSize > maxSizeForNewBlob) { | |
| 189 TextBlob* lru = fLRUList.head(); | |
| 190 if (1 == lru->key().count()) { | |
| 191 // 1-length keys are unterstood to be the blob id. | |
| 192 fBlobIdCache.remove(lru->key()[0]); | |
| 193 } else { | |
| 194 fBlobKeyCache.remove(lru->key()); | |
| 195 } | |
| 196 fLRUList.remove(lru); | |
| 197 fCacheSize -= lru->cpuMemorySize(); | |
| 198 delete lru; | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
| 203 | |
| 204 void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob, | |
| 205 const SkPaint& skPaint) { | |
| 206 fCpuMemorySize = sizeof(TextBlob); | |
| 207 SkPaint runPaint(skPaint); | |
| 208 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) { | |
| 209 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint. | |
| 210 TextRun* run = this->addToTail(runPaint); | |
| 211 | |
| 212 const char* text = reinterpret_cast<const char*>(iter.glyphs()); | |
| 213 size_t byteLength = sizeof(uint16_t) * iter.glyphCount(); | |
| 214 const SkPoint& runOffset = iter.offset(); | |
| 215 | |
| 216 switch (iter.positioning()) { | |
| 217 case SkTextBlob::kDefault_Positioning: | |
| 218 run->setText(text, byteLength, runOffset.fX, runOffset.fY); | |
| 219 break; | |
| 220 case SkTextBlob::kHorizontal_Positioning: | |
| 221 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0
, runOffset.fY)); | |
| 222 break; | |
| 223 case SkTextBlob::kFull_Positioning: | |
| 224 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0
, 0)); | |
| 225 break; | |
| 226 } | |
| 227 | |
| 228 fCpuMemorySize += run->computeSizeInCache(); | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
| 233 | |
| 234 class GrStencilAndCoverTextContext::FallbackBlobBuilder { | |
| 235 public: | |
| 236 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {} | |
| 237 | |
| 238 bool isInitialized() const { return SkToBool(fBuilder); } | |
| 239 | |
| 240 void init(const SkPaint& font, SkScalar textRatio); | |
| 241 | |
| 242 void appendGlyph(uint16_t glyphId, const SkPoint& pos); | |
| 243 | |
| 244 const SkTextBlob* buildIfNeeded(int* count); | |
| 245 | |
| 246 private: | |
| 247 enum { kWriteBufferSize = 1024 }; | |
| 248 | |
| 249 void flush(); | |
| 250 | |
| 251 SkAutoTDelete<SkTextBlobBuilder> fBuilder; | |
| 252 SkPaint fFont; | |
| 253 int fBuffIdx; | |
| 254 int fCount; | |
| 255 uint16_t fGlyphIds[kWriteBufferSize]; | |
| 256 SkPoint fPositions[kWriteBufferSize]; | |
| 257 }; | |
| 258 | |
| 259 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
| 260 | |
| 261 GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) | |
| 262 : fStroke(fontAndStroke), | |
| 263 fFont(fontAndStroke), | |
| 264 fTotalGlyphCount(0), | |
| 265 fFallbackGlyphCount(0), | |
| 266 fDetachedGlyphCache(nullptr), | |
| 267 fLastDrawnGlyphsID(SK_InvalidUniqueID) { | |
| 268 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported. | |
| 269 | |
| 270 // Setting to "fill" ensures that no strokes get baked into font outlines. (
We use the GPU path | |
| 271 // rendering API for stroking). | |
| 272 fFont.setStyle(SkPaint::kFill_Style); | |
| 273 | |
| 274 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle
()) { | |
| 275 // Instead of letting fake bold get baked into the glyph outlines, do it
with GPU stroke. | |
| 276 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(), | |
| 277 kStdFakeBoldInterpKeys, | |
| 278 kStdFakeBoldInterpValues, | |
| 279 kStdFakeBoldInterpLength); | |
| 280 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale); | |
| 281 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extr
a : extra, | |
| 282 true /*strokeAndFill*/); | |
| 283 | |
| 284 fFont.setFakeBoldText(false); | |
| 285 } | |
| 286 | |
| 287 if (!fFont.getPathEffect() && !fStroke.isDashed()) { | |
| 288 // We can draw the glyphs from canonically sized paths. | |
| 289 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; | |
| 290 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextS
ize(); | |
| 291 | |
| 292 // Compensate for the glyphs being scaled by fTextRatio. | |
| 293 if (!fStroke.isFillStyle()) { | |
| 294 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, | |
| 295 SkStrokeRec::kStrokeAndFill_Style == fStroke.
getStyle()); | |
| 296 } | |
| 297 | |
| 298 fFont.setLinearText(true); | |
| 299 fFont.setLCDRenderText(false); | |
| 300 fFont.setAutohinted(false); | |
| 301 fFont.setHinting(SkPaint::kNo_Hinting); | |
| 302 fFont.setSubpixelText(true); | |
| 303 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); | |
| 304 | |
| 305 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() && | |
| 306 0 == fFont.getTextSkewX() && | |
| 307 !fFont.isFakeBoldText() && | |
| 308 !fFont.isVerticalText(); | |
| 309 } else { | |
| 310 fTextRatio = fTextInverseRatio = 1.0f; | |
| 311 fUsingRawGlyphPaths = false; | |
| 312 } | |
| 313 | |
| 314 // Generate the key that will be used to cache the GPU glyph path objects. | |
| 315 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) { | |
| 316 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::
GenerateDomain(); | |
| 317 | |
| 318 const SkTypeface* typeface = fFont.getTypeface(); | |
| 319 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1
); | |
| 320 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID(
) : 0; | |
| 321 } else { | |
| 322 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::Generat
eDomain(); | |
| 323 | |
| 324 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt(); | |
| 325 if (fUsingRawGlyphPaths) { | |
| 326 const SkTypeface* typeface = fFont.getTypeface(); | |
| 327 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 +
strokeDataCount); | |
| 328 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; | |
| 329 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount; | |
| 330 fStroke.asUniqueKeyFragment(&builder[2]); | |
| 331 } else { | |
| 332 SkGlyphCache* glyphCache = this->getGlyphCache(); | |
| 333 const SkTypeface* typeface = glyphCache->getScalerContext()->getType
face(); | |
| 334 const SkDescriptor* desc = &glyphCache->getDescriptor(); | |
| 335 int descDataCount = (desc->getLength() + 3) / 4; | |
| 336 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, | |
| 337 2 + strokeDataCount + descDataCount); | |
| 338 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; | |
| 339 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDat
aCount << 16); | |
| 340 fStroke.asUniqueKeyFragment(&builder[2]); | |
| 341 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength()); | |
| 342 } | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 GrStencilAndCoverTextContext::TextRun::~TextRun() { | |
| 347 this->releaseGlyphCache(); | |
| 348 } | |
| 349 | |
| 350 void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t by
teLength, | |
| 351 SkScalar x, SkScalar y) { | |
| 352 SkASSERT(byteLength == 0 || text != nullptr); | |
| 353 | |
| 354 SkGlyphCache* glyphCache = this->getGlyphCache(); | |
| 355 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); | |
| 356 | |
| 357 fTotalGlyphCount = fFont.countText(text, byteLength); | |
| 358 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTran
sformType, | |
| 359 fTotalGlyphCount)); | |
| 360 | |
| 361 const char* stop = text + byteLength; | |
| 362 | |
| 363 // Measure first if needed. | |
| 364 if (fFont.getTextAlign() != SkPaint::kLeft_Align) { | |
| 365 SkFixed stopX = 0; | |
| 366 SkFixed stopY = 0; | |
| 367 | |
| 368 const char* textPtr = text; | |
| 369 while (textPtr < stop) { | |
| 370 // We don't need x, y here, since all subpixel variants will have th
e | |
| 371 // same advance. | |
| 372 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0); | |
| 373 | |
| 374 stopX += glyph.fAdvanceX; | |
| 375 stopY += glyph.fAdvanceY; | |
| 376 } | |
| 377 SkASSERT(textPtr == stop); | |
| 378 | |
| 379 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; | |
| 380 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; | |
| 381 | |
| 382 if (fFont.getTextAlign() == SkPaint::kCenter_Align) { | |
| 383 alignX = SkScalarHalf(alignX); | |
| 384 alignY = SkScalarHalf(alignY); | |
| 385 } | |
| 386 | |
| 387 x -= alignX; | |
| 388 y -= alignY; | |
| 389 } | |
| 390 | |
| 391 SkAutoKern autokern; | |
| 392 | |
| 393 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); | |
| 394 | |
| 395 SkFixed fx = SkScalarToFixed(x); | |
| 396 SkFixed fy = SkScalarToFixed(y); | |
| 397 FallbackBlobBuilder fallback; | |
| 398 while (text < stop) { | |
| 399 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0); | |
| 400 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio); | |
| 401 if (glyph.fWidth) { | |
| 402 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedT
oScalar(fy)), | |
| 403 &fallback); | |
| 404 } | |
| 405 | |
| 406 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio); | |
| 407 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); | |
| 408 } | |
| 409 | |
| 410 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); | |
| 411 } | |
| 412 | |
| 413 void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t
byteLength, | |
| 414 const SkScalar pos[], int
scalarsPerPosition, | |
| 415 const SkPoint& offset) { | |
| 416 SkASSERT(byteLength == 0 || text != nullptr); | |
| 417 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
| 418 | |
| 419 SkGlyphCache* glyphCache = this->getGlyphCache(); | |
| 420 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); | |
| 421 | |
| 422 fTotalGlyphCount = fFont.countText(text, byteLength); | |
| 423 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTran
sformType, | |
| 424 fTotalGlyphCount)); | |
| 425 | |
| 426 const char* stop = text + byteLength; | |
| 427 | |
| 428 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition); | |
| 429 SkTextAlignProc alignProc(fFont.getTextAlign()); | |
| 430 FallbackBlobBuilder fallback; | |
| 431 while (text < stop) { | |
| 432 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0); | |
| 433 if (glyph.fWidth) { | |
| 434 SkPoint tmsLoc; | |
| 435 tmsProc(pos, &tmsLoc); | |
| 436 SkPoint loc; | |
| 437 alignProc(tmsLoc, glyph, &loc); | |
| 438 | |
| 439 this->appendGlyph(glyph, loc, &fallback); | |
| 440 } | |
| 441 pos += scalarsPerPosition; | |
| 442 } | |
| 443 | |
| 444 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); | |
| 445 } | |
| 446 | |
| 447 GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx)
const { | |
| 448 GrPathRange* glyphs = static_cast<GrPathRange*>( | |
| 449 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKe
y)); | |
| 450 if (nullptr == glyphs) { | |
| 451 if (fUsingRawGlyphPaths) { | |
| 452 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(),
nullptr, fStroke); | |
| 453 } else { | |
| 454 SkGlyphCache* cache = this->getGlyphCache(); | |
| 455 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerConte
xt()->getTypeface(), | |
| 456 &cache->getDescriptor
(), | |
| 457 fStroke); | |
| 458 } | |
| 459 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyph
s); | |
| 460 } | |
| 461 return glyphs; | |
| 462 } | |
| 463 | |
| 464 inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& gl
yph, | |
| 465 const SkPoint& po
s, | |
| 466 FallbackBlobBuild
er* fallback) { | |
| 467 // Stick the glyphs we can't draw into the fallback text blob. | |
| 468 if (SkMask::kARGB32_Format == glyph.fMaskFormat) { | |
| 469 if (!fallback->isInitialized()) { | |
| 470 fallback->init(fFont, fTextRatio); | |
| 471 } | |
| 472 fallback->appendGlyph(glyph.getGlyphID(), pos); | |
| 473 } else { | |
| 474 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(), | |
| 475 fTextInverseRatio * pos.y()); | |
| 476 } | |
| 477 } | |
| 478 | |
| 479 void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, | |
| 480 GrDrawContext* dc, | |
| 481 GrPipelineBuilder* pipelineBuil
der, | |
| 482 GrColor color, | |
| 483 const SkMatrix& viewMatrix, | |
| 484 SkScalar x, SkScalar y, | |
| 485 const SkIRect& clipBounds, | |
| 486 GrTextContext* fallbackTextCont
ext, | |
| 487 const SkPaint& originalSkPaint)
const { | |
| 488 SkASSERT(fInstanceData); | |
| 489 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.i
sAntiAlias()); | |
| 490 | |
| 491 if (fInstanceData->count()) { | |
| 492 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.is
AntiAlias()); | |
| 493 | |
| 494 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | |
| 495 kZero_StencilOp, | |
| 496 kKeep_StencilOp, | |
| 497 kNotEqual_StencilFunc, | |
| 498 0xffff, | |
| 499 0x0000, | |
| 500 0xffff); | |
| 501 | |
| 502 *pipelineBuilder->stencil() = kStencilPass; | |
| 503 | |
| 504 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx)); | |
| 505 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) { | |
| 506 // Either this is the first draw or the glyphs object was purged sin
ce last draw. | |
| 507 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->c
ount()); | |
| 508 fLastDrawnGlyphsID = glyphs->getUniqueID(); | |
| 509 } | |
| 510 | |
| 511 // Don't compute a bounding box. For dst copy texture, we'll opt instead
for it to just copy | |
| 512 // the entire dst. Realistically this is a moot point, because any conte
xt that supports | |
| 513 // NV_path_rendering will also support NV_blend_equation_advanced. | |
| 514 // For clipping we'll just skip any optimizations based on the bounds. T
his does, however, | |
| 515 // hurt batching. | |
| 516 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->widt
h(), | |
| 517 pipelineBuilder->getRenderTarget()->heig
ht()); | |
| 518 | |
| 519 SkAutoTUnref<GrDrawPathBatchBase> batch( | |
| 520 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRat
io * x, | |
| 521 fTextInverseRatio * y, color, | |
| 522 GrPathRendering::kWinding_FillType, gly
phs, fInstanceData, | |
| 523 bounds)); | |
| 524 | |
| 525 dc->drawPathBatch(*pipelineBuilder, batch); | |
| 526 } | |
| 527 | |
| 528 if (fFallbackTextBlob) { | |
| 529 SkPaint fallbackSkPaint(originalSkPaint); | |
| 530 fStroke.applyToPaint(&fallbackSkPaint); | |
| 531 if (!fStroke.isFillStyle()) { | |
| 532 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); | |
| 533 } | |
| 534 | |
| 535 fallbackTextContext->drawTextBlob(dc, pipelineBuilder->clip(), fallbackS
kPaint, viewMatrix, | |
| 536 fFallbackTextBlob, x, y, nullptr, clip
Bounds); | |
| 537 } | |
| 538 } | |
| 539 | |
| 540 SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const { | |
| 541 if (!fDetachedGlyphCache) { | |
| 542 fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreG
amma*/); | |
| 543 } | |
| 544 return fDetachedGlyphCache; | |
| 545 } | |
| 546 | |
| 547 | |
| 548 void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const { | |
| 549 if (fDetachedGlyphCache) { | |
| 550 SkGlyphCache::AttachCache(fDetachedGlyphCache); | |
| 551 fDetachedGlyphCache = nullptr; | |
| 552 } | |
| 553 } | |
| 554 | |
| 555 size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const { | |
| 556 size_t size = sizeof(TextRun) + fGlyphPathsKey.size(); | |
| 557 // The instance data always reserves enough space for every glyph. | |
| 558 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * s
izeof(float)); | |
| 559 if (fInstanceData) { | |
| 560 size += sizeof(InstanceData); | |
| 561 } | |
| 562 if (fFallbackTextBlob) { | |
| 563 size += sizeof(SkTextBlob); | |
| 564 } | |
| 565 return size; | |
| 566 } | |
| 567 | |
| 568 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
| 569 | |
| 570 void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font
, | |
| 571 SkScalar textRatio)
{ | |
| 572 SkASSERT(!this->isInitialized()); | |
| 573 fBuilder.reset(new SkTextBlobBuilder); | |
| 574 fFont = font; | |
| 575 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will alread
y account for align. | |
| 576 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
| 577 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non
-bitmap color glyphs | |
| 578 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets res
olved. | |
| 579 fFont.setSubpixelText(false); | |
| 580 fFont.setTextSize(fFont.getTextSize() * textRatio); | |
| 581 fBuffIdx = 0; | |
| 582 } | |
| 583 | |
| 584 void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t gly
phId, | |
| 585 const SkPoin
t& pos) { | |
| 586 SkASSERT(this->isInitialized()); | |
| 587 if (fBuffIdx >= kWriteBufferSize) { | |
| 588 this->flush(); | |
| 589 } | |
| 590 fGlyphIds[fBuffIdx] = glyphId; | |
| 591 fPositions[fBuffIdx] = pos; | |
| 592 fBuffIdx++; | |
| 593 fCount++; | |
| 594 } | |
| 595 | |
| 596 void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() { | |
| 597 SkASSERT(this->isInitialized()); | |
| 598 SkASSERT(fBuffIdx <= kWriteBufferSize); | |
| 599 if (!fBuffIdx) { | |
| 600 return; | |
| 601 } | |
| 602 // This will automatically merge with previous runs since we use the same fo
nt. | |
| 603 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuf
fIdx); | |
| 604 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t)); | |
| 605 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar))
; | |
| 606 fBuffIdx = 0; | |
| 607 } | |
| 608 | |
| 609 const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeed
ed(int *count) { | |
| 610 *count = fCount; | |
| 611 if (fCount) { | |
| 612 this->flush(); | |
| 613 return fBuilder->build(); | |
| 614 } | |
| 615 return nullptr; | |
| 616 } | |
| OLD | NEW |