1 /* 2 * Copyright 2015 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 #include "GrAtlasTextContext.h" 8 9 #include "GrBatch.h" 10 #include "GrBatchFontCache.h" 11 #include "GrBatchTarget.h" 12 #include "GrBatchTest.h" 13 #include "GrDefaultGeoProcFactory.h" 14 #include "GrDrawTarget.h" 15 #include "GrFontScaler.h" 16 #include "GrIndexBuffer.h" 17 #include "GrResourceProvider.h" 18 #include "GrStrokeInfo.h" 19 #include "GrTextBlobCache.h" 20 #include "GrTexturePriv.h" 21 #include "GrVertexBuffer.h" 22 23 #include "SkAutoKern.h" 24 #include "SkColorPriv.h" 25 #include "SkColorFilter.h" 26 #include "SkDistanceFieldGen.h" 27 #include "SkDraw.h" 28 #include "SkDrawFilter.h" 29 #include "SkDrawProcs.h" 30 #include "SkGlyphCache.h" 31 #include "SkGpuDevice.h" 32 #include "SkGr.h" 33 #include "SkPath.h" 34 #include "SkRTConf.h" 35 #include "SkStrokeRec.h" 36 #include "SkTextBlob.h" 37 #include "SkTextMapStateProc.h" 38 39 #include "effects/GrBitmapTextGeoProc.h" 40 #include "effects/GrDistanceFieldGeoProc.h" 41 42 namespace { 43 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); 44 45 // position + local coord 46 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); 47 48 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16); 49 50 static const int kMinDFFontSize = 18; 51 static const int kSmallDFFontSize = 32; 52 static const int kSmallDFFontLimit = 32; 53 static const int kMediumDFFontSize = 72; 54 static const int kMediumDFFontLimit = 72; 55 static const int kLargeDFFontSize = 162; 56 static const int kLargeDFFontLimit = 2 * kLargeDFFontSize; 57 58 SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;) 59 static const int kDistanceAdjustLumShift = 5; 60 61 static const int kVerticesPerGlyph = 4; 62 static const int kIndicesPerGlyph = 6; 63 64 static size_t get_vertex_stride(GrMaskFormat maskFormat) { 65 switch (maskFormat) { 66 case kA8_GrMaskFormat: 67 return kGrayTextVASize; 68 case kARGB_GrMaskFormat: 69 return kColorTextVASize; 70 default: 71 return kLCDTextVASize; 72 } 73 } 74 75 static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) { 76 SkASSERT(maskFormat == kA8_GrMaskFormat); 77 if (useLCDText) { 78 return kLCDTextVASize; 79 } else { 80 return kGrayTextVASize; 81 } 82 } 83 84 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { 85 unsigned r = SkColorGetR(c); 86 unsigned g = SkColorGetG(c); 87 unsigned b = SkColorGetB(c); 88 return GrColorPackRGBA(r, g, b, 0xff); 89 } 90 91 }; 92 93 // TODO 94 // Distance field text in textblobs 95 96 GrAtlasTextContext::GrAtlasTextContext(GrContext* context, 97 SkGpuDevice* gpuDevice, 98 const SkDeviceProperties& properties, 99 bool enableDistanceFields) 100 : INHERITED(context, gpuDevice, properties) 101 , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) { 102 // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest 103 // vertexStride 104 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize, 105 vertex_attribute_changed); 106 fCurrStrike = NULL; 107 fCache = context->getTextBlobCache(); 108 109 #if SK_FORCE_DISTANCE_FIELD_TEXT 110 fEnableDFRendering = true; 111 #else 112 fEnableDFRendering = enableDistanceFields; 113 #endif 114 } 115 116 void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) { 117 118 // This is used for an approximation of the mask gamma hack, used by raster and bitmap 119 // text. The mask gamma hack is based off of guessing what the blend color is going to 120 // be, and adjusting the mask so that when run through the linear blend will 121 // produce the value closest to the desired result. However, in practice this means 122 // that the 'adjusted' mask is just increasing or decreasing the coverage of 123 // the mask depending on what it is thought it will blit against. For black (on 124 // assumed white) this means that coverages are decreased (on a curve). For white (on 125 // assumed black) this means that coverages are increased (on a a curve). At 126 // middle (perceptual) gray (which could be blit against anything) the coverages 127 // remain the same. 128 // 129 // The idea here is that instead of determining the initial (real) coverage and 130 // then adjusting that coverage, we determine an adjusted coverage directly by 131 // essentially manipulating the geometry (in this case, the distance to the glyph 132 // edge). So for black (on assumed white) this thins a bit; for white (on 133 // assumed black) this fake bolds the geometry a bit. 134 // 135 // The distance adjustment is calculated by determining the actual coverage value which 136 // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This 137 // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the 138 // actual edge. So by subtracting this distance adjustment and computing without the 139 // the coverage adjustment we should get 0.5 coverage at the same point. 140 // 141 // This has several implications: 142 // For non-gray lcd smoothed text, each subpixel essentially is using a 143 // slightly different geometry. 144 // 145 // For black (on assumed white) this may not cover some pixels which were 146 // previously covered; however those pixels would have been only slightly 147 // covered and that slight coverage would have been decreased anyway. Also, some pixels 148 // which were previously fully covered may no longer be fully covered. 149 // 150 // For white (on assumed black) this may cover some pixels which weren't 151 // previously covered at all. 152 153 int width, height; 154 size_t size; 155 156 #ifdef SK_GAMMA_CONTRAST 157 SkScalar contrast = SK_GAMMA_CONTRAST; 158 #else 159 SkScalar contrast = 0.5f; 160 #endif 161 SkScalar paintGamma = gamma; 162 SkScalar deviceGamma = gamma; 163 164 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, 165 &width, &height); 166 167 SkASSERT(kExpectedDistanceAdjustTableSize == height); 168 fTable = SkNEW_ARRAY(SkScalar, height); 169 170 SkAutoTArray<uint8_t> data((int)size); 171 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); 172 173 // find the inverse points where we cross 0.5 174 // binsearch might be better, but we only need to do this once on creation 175 for (int row = 0; row < height; ++row) { 176 uint8_t* rowPtr = data.get() + row*width; 177 for (int col = 0; col < width - 1; ++col) { 178 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { 179 // compute point where a mask value will give us a result of 0.5 180 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); 181 float borderAlpha = (col + interp) / 255.f; 182 183 // compute t value for that alpha 184 // this is an approximate inverse for smoothstep() 185 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; 186 187 // compute distance which gives us that t value 188 const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor 189 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; 190 191 fTable[row] = d; 192 break; 193 } 194 } 195 } 196 } 197 198 GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context, 199 SkGpuDevice* gpuDevice, 200 const SkDeviceProperties& props, 201 bool enableDistanceFields) { 202 return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields)); 203 } 204 205 bool GrAtlasTextContext::canDraw(const GrRenderTarget*, 206 const GrClip&, 207 const GrPaint&, 208 const SkPaint& skPaint, 209 const SkMatrix& viewMatrix) { 210 return this->canDrawAsDistanceFields(skPaint, viewMatrix) || 211 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); 212 } 213 214 GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) { 215 GrColor canonicalColor = paint.computeLuminanceColor(); 216 if (lcd) { 217 // This is the correct computation, but there are tons of cases where LCD can be overridden. 218 // For now we just regenerate if any run in a textblob has LCD. 219 // TODO figure out where all of these overrides are and see if we can incorporate that logic 220 // at a higher level *OR* use sRGB 221 SkASSERT(false); 222 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor); 223 } else { 224 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have 225 // gamma corrected masks anyways, nor color 226 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor), 227 SkColorGetG(canonicalColor), 228 SkColorGetB(canonicalColor)); 229 // reduce to our finite number of bits 230 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum)); 231 } 232 return canonicalColor; 233 } 234 235 // TODO if this function ever shows up in profiling, then we can compute this value when the 236 // textblob is being built and cache it. However, for the time being textblobs mostly only have 1 237 // run so this is not a big deal to compute here. 238 bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) { 239 SkTextBlob::RunIterator it(blob); 240 for (; !it.done(); it.next()) { 241 if (it.isLCD()) { 242 return true; 243 } 244 } 245 return false; 246 } 247 248 bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY, 249 const BitmapTextBlob& blob, const SkPaint& paint, 250 const SkMaskFilter::BlurRec& blurRec, 251 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { 252 // If we have LCD text then our canonical color will be set to transparent, in this case we have 253 // to regenerate the blob on any color change 254 if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) { 255 return true; 256 } 257 258 if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) { 259 return true; 260 } 261 262 if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) { 263 return true; 264 } 265 266 // We only cache one masked version 267 if (blob.fKey.fHasBlur && 268 (blob.fBlurRec.fSigma != blurRec.fSigma || 269 blob.fBlurRec.fStyle != blurRec.fStyle || 270 blob.fBlurRec.fQuality != blurRec.fQuality)) { 271 return true; 272 } 273 274 // Similarly, we only cache one version for each style 275 if (blob.fKey.fStyle != SkPaint::kFill_Style && 276 (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() || 277 blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() || 278 blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) { 279 return true; 280 } 281 282 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls 283 // for mixed blobs if this becomes an issue. 284 if (blob.hasBitmap() && blob.hasDistanceField()) { 285 // Identical viewmatrices and we can reuse in all cases 286 if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) { 287 return false; 288 } 289 return true; 290 } 291 292 if (blob.hasBitmap()) { 293 if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() || 294 blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() || 295 blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() || 296 blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) { 297 return true; 298 } 299 300 // We can update the positions in the cachedtextblobs without regenerating the whole blob, 301 // but only for integer translations. 302 // This cool bit of math will determine the necessary translation to apply to the already 303 // generated vertex coordinates to move them to the correct position 304 SkScalar transX = viewMatrix.getTranslateX() + 305 viewMatrix.getScaleX() * (x - blob.fX) + 306 viewMatrix.getSkewX() * (y - blob.fY) - 307 blob.fViewMatrix.getTranslateX(); 308 SkScalar transY = viewMatrix.getTranslateY() + 309 viewMatrix.getSkewY() * (x - blob.fX) + 310 viewMatrix.getScaleY() * (y - blob.fY) - 311 blob.fViewMatrix.getTranslateY(); 312 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) { 313 return true; 314 } 315 316 (*outTransX) = transX; 317 (*outTransY) = transY; 318 } else if (blob.hasDistanceField()) { 319 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different 320 // distance field being generated, so we have to regenerate in those cases 321 SkScalar newMaxScale = viewMatrix.getMaxScale(); 322 SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale(); 323 SkScalar scaleAdjust = newMaxScale / oldMaxScale; 324 if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) { 325 return true; 326 } 327 328 (*outTransX) = x - blob.fX; 329 (*outTransY) = y - blob.fY; 330 } 331 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case 332 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate 333 // the blob anyways at flush time, so no need to regenerate explicitly 334 335 return false; 336 } 337 338 339 inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run, 340 const SkPaint& skPaint, 341 const SkMatrix* viewMatrix, 342 bool noGamma) { 343 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma); 344 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface())); 345 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc()); 346 } 347 348 void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, 349 const SkPaint& skPaint, const SkMatrix& viewMatrix, 350 const SkTextBlob* blob, SkScalar x, SkScalar y, 351 SkDrawFilter* drawFilter, const SkIRect& clipBounds) { 352 // If we have been abandoned, then don't draw 353 if (!fContext->getTextTarget()) { 354 return; 355 } 356 357 SkAutoTUnref<BitmapTextBlob> cacheBlob; 358 SkMaskFilter::BlurRec blurRec; 359 BitmapTextBlob::Key key; 360 // It might be worth caching these things, but its not clear at this time 361 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here 362 const SkMaskFilter* mf = skPaint.getMaskFilter(); 363 bool canCache = !(skPaint.getPathEffect() || 364 (mf && !mf->asABlur(&blurRec)) || 365 drawFilter); 366 367 if (canCache) { 368 bool hasLCD = HasLCD(blob); 369 370 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry 371 SkPixelGeometry pixelGeometry = hasLCD ? fDeviceProperties.pixelGeometry() : 372 kUnknown_SkPixelGeometry; 373 374 // TODO we want to figure out a way to be able to use the canonical color on LCD text, 375 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to 376 // ensure we always match the same key 377 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT : 378 ComputeCanonicalColor(skPaint, hasLCD); 379 380 key.fPixelGeometry = pixelGeometry; 381 key.fUniqueID = blob->uniqueID(); 382 key.fStyle = skPaint.getStyle(); 383 key.fHasBlur = SkToBool(mf); 384 key.fCanonicalColor = canonicalColor; 385 cacheBlob.reset(SkSafeRef(fCache->find(key))); 386 } 387 388 SkIRect clipRect; 389 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); 390 391 SkScalar transX = 0.f; 392 SkScalar transY = 0.f; 393 394 // Though for the time being runs in the textblob can override the paint, they only touch font 395 // info. 396 GrPaint grPaint; 397 if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) { 398 return; 399 } 400 401 if (cacheBlob) { 402 if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) { 403 // We have to remake the blob because changes may invalidate our masks. 404 // TODO we could probably get away reuse most of the time if the pointer is unique, 405 // but we'd have to clear the subrun information 406 fCache->remove(cacheBlob); 407 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint, 408 kGrayTextVASize))); 409 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y, 410 drawFilter, clipRect, rt, clip, grPaint); 411 } else { 412 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y 413 // offsets 414 cacheBlob->fViewMatrix = viewMatrix; 415 cacheBlob->fX = x; 416 cacheBlob->fY = y; 417 fCache->makeMRU(cacheBlob); 418 } 419 } else { 420 if (canCache) { 421 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint, 422 kGrayTextVASize))); 423 } else { 424 cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize)); 425 } 426 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y, 427 drawFilter, clipRect, rt, clip, grPaint); 428 } 429 430 cacheBlob->fPaintColor = skPaint.getColor(); 431 this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter, 432 clip, viewMatrix, clipBounds, x, y, transX, transY); 433 } 434 435 inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, 436 const SkMatrix& viewMatrix) { 437 // TODO: support perspective (need getMaxScale replacement) 438 if (viewMatrix.hasPerspective()) { 439 return false; 440 } 441 442 SkScalar maxScale = viewMatrix.getMaxScale(); 443 SkScalar scaledTextSize = maxScale*skPaint.getTextSize(); 444 // Hinted text looks far better at small resolutions 445 // Scaling up beyond 2x yields undesireable artifacts 446 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) { 447 return false; 448 } 449 450 if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() && 451 scaledTextSize < kLargeDFFontSize) { 452 return false; 453 } 454 455 // rasterizers and mask filters modify alpha, which doesn't 456 // translate well to distance 457 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || 458 !fContext->getTextTarget()->caps()->shaderCaps()->shaderDerivativeSupport()) { 459 return false; 460 } 461 462 // TODO: add some stroking support 463 if (skPaint.getStyle() != SkPaint::kFill_Style) { 464 return false; 465 } 466 467 return true; 468 } 469 470 void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob, 471 const SkPaint& skPaint, GrColor color, 472 const SkMatrix& viewMatrix, 473 const SkTextBlob* blob, SkScalar x, SkScalar y, 474 SkDrawFilter* drawFilter, const SkIRect& clipRect, 475 GrRenderTarget* rt, const GrClip& clip, 476 const GrPaint& paint) { 477 cacheBlob->fViewMatrix = viewMatrix; 478 cacheBlob->fX = x; 479 cacheBlob->fY = y; 480 481 // Regenerate textblob 482 SkPaint runPaint = skPaint; 483 SkTextBlob::RunIterator it(blob); 484 for (int run = 0; !it.done(); it.next(), run++) { 485 int glyphCount = it.glyphCount(); 486 size_t textLen = glyphCount * sizeof(uint16_t); 487 const SkPoint& offset = it.offset(); 488 // applyFontToPaint() always overwrites the exact same attributes, 489 // so it is safe to not re-seed the paint for this reason. 490 it.applyFontToPaint(&runPaint); 491 492 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { 493 // A false return from filter() means we should abort the current draw. 494 runPaint = skPaint; 495 continue; 496 } 497 498 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint)); 499 500 // setup vertex / glyphIndex for the new run 501 if (run > 0) { 502 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back(); 503 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back(); 504 505 newRun.fVertexStartIndex = lastRun.fVertexEndIndex; 506 newRun.fVertexEndIndex = lastRun.fVertexEndIndex; 507 508 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex; 509 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex; 510 } 511 512 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) { 513 cacheBlob->setHasDistanceField(); 514 SkPaint dfPaint = runPaint; 515 SkScalar textRatio; 516 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix); 517 Run& runIdx = cacheBlob->fRuns[run]; 518 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back(); 519 subRun.fUseLCDText = runPaint.isLCDRenderText(); 520 subRun.fDrawAsDistanceFields = true; 521 522 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true); 523 524 SkTDArray<char> fallbackTxt; 525 SkTDArray<SkScalar> fallbackPos; 526 SkPoint dfOffset; 527 int scalarsPerPosition = 2; 528 switch (it.positioning()) { 529 case SkTextBlob::kDefault_Positioning: { 530 this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix, 531 (const char *)it.glyphs(), textLen, 532 x + offset.x(), y + offset.y(), clipRect, textRatio, 533 &fallbackTxt, &fallbackPos, &dfOffset, runPaint); 534 break; 535 } 536 case SkTextBlob::kHorizontal_Positioning: { 537 scalarsPerPosition = 1; 538 dfOffset = SkPoint::Make(x, y + offset.y()); 539 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix, 540 (const char*)it.glyphs(), textLen, it.pos(), 541 scalarsPerPosition, dfOffset, clipRect, textRatio, 542 &fallbackTxt, &fallbackPos); 543 break; 544 } 545 case SkTextBlob::kFull_Positioning: { 546 dfOffset = SkPoint::Make(x, y); 547 this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix, 548 (const char*)it.glyphs(), textLen, it.pos(), 549 scalarsPerPosition, dfOffset, clipRect, textRatio, 550 &fallbackTxt, &fallbackPos); 551 break; 552 } 553 } 554 if (fallbackTxt.count()) { 555 this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix, 556 fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset, 557 clipRect); 558 } 559 560 SkGlyphCache::AttachCache(cache); 561 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) { 562 cacheBlob->fRuns[run].fDrawAsPaths = true; 563 } else { 564 cacheBlob->setHasBitmap(); 565 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix, 566 false); 567 switch (it.positioning()) { 568 case SkTextBlob::kDefault_Positioning: 569 this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix, 570 (const char *)it.glyphs(), textLen, 571 x + offset.x(), y + offset.y(), clipRect); 572 break; 573 case SkTextBlob::kHorizontal_Positioning: 574 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix, 575 (const char*)it.glyphs(), textLen, it.pos(), 1, 576 SkPoint::Make(x, y + offset.y()), clipRect); 577 break; 578 case SkTextBlob::kFull_Positioning: 579 this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix, 580 (const char*)it.glyphs(), textLen, it.pos(), 2, 581 SkPoint::Make(x, y), clipRect); 582 break; 583 } 584 SkGlyphCache::AttachCache(cache); 585 } 586 587 if (drawFilter) { 588 // A draw filter may change the paint arbitrarily, so we must re-seed in this case. 589 runPaint = skPaint; 590 } 591 } 592 } 593 594 inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob, 595 SkPaint* skPaint, 596 SkScalar* textRatio, 597 const SkMatrix& viewMatrix) { 598 // getMaxScale doesn't support perspective, so neither do we at the moment 599 SkASSERT(!viewMatrix.hasPerspective()); 600 SkScalar maxScale = viewMatrix.getMaxScale(); 601 SkScalar textSize = skPaint->getTextSize(); 602 SkScalar scaledTextSize = textSize; 603 // if we have non-unity scale, we need to choose our base text size 604 // based on the SkPaint's text size multiplied by the max scale factor 605 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? 606 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { 607 scaledTextSize *= maxScale; 608 } 609 610 // We have three sizes of distance field text, and within each size 'bucket' there is a floor 611 // and ceiling. A scale outside of this range would require regenerating the distance fields 612 SkScalar dfMaskScaleFloor; 613 SkScalar dfMaskScaleCeil; 614 if (scaledTextSize <= kSmallDFFontLimit) { 615 dfMaskScaleFloor = kMinDFFontSize; 616 dfMaskScaleCeil = kSmallDFFontLimit; 617 *textRatio = textSize / kSmallDFFontSize; 618 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize)); 619 } else if (scaledTextSize <= kMediumDFFontLimit) { 620 dfMaskScaleFloor = kSmallDFFontLimit; 621 dfMaskScaleCeil = kMediumDFFontLimit; 622 *textRatio = textSize / kMediumDFFontSize; 623 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize)); 624 } else { 625 dfMaskScaleFloor = kMediumDFFontLimit; 626 dfMaskScaleCeil = kLargeDFFontLimit; 627 *textRatio = textSize / kLargeDFFontSize; 628 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize)); 629 } 630 631 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and 632 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale 633 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can 634 // tolerate before we'd have to move to a large mip size. When we actually test these values 635 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test 636 // against these values to decide if we can reuse or not(ie, will a given scale change our mip 637 // level) 638 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil); 639 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale); 640 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale); 641 642 skPaint->setLCDRenderText(false); 643 skPaint->setAutohinted(false); 644 skPaint->setHinting(SkPaint::kNormal_Hinting); 645 skPaint->setSubpixelText(true); 646 } 647 648 inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob, 649 int runIndex, 650 GrRenderTarget* rt, const GrClip& clip, 651 const GrPaint& paint, 652 const SkPaint& skPaint, 653 const SkMatrix& viewMatrix, 654 const SkTDArray<char>& fallbackTxt, 655 const SkTDArray<SkScalar>& fallbackPos, 656 int scalarsPerPosition, 657 const SkPoint& offset, 658 const SkIRect& clipRect) { 659 SkASSERT(fallbackTxt.count()); 660 blob->setHasBitmap(); 661 Run& run = blob->fRuns[runIndex]; 662 // Push back a new subrun to fill and set the override descriptor 663 run.push_back(); 664 run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor)); 665 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor, 666 &fDeviceProperties, &viewMatrix, false); 667 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface, 668 run.fOverrideDescriptor->getDesc()); 669 this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix, 670 fallbackTxt.begin(), fallbackTxt.count(), 671 fallbackPos.begin(), scalarsPerPosition, offset, clipRect); 672 SkGlyphCache::AttachCache(cache); 673 } 674 675 inline GrAtlasTextContext::BitmapTextBlob* 676 GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint, 677 const SkMatrix& viewMatrix, SkGlyphCache** cache, 678 SkPaint* dfPaint, SkScalar* textRatio) { 679 BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize); 680 681 *dfPaint = origPaint; 682 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix); 683 blob->fViewMatrix = viewMatrix; 684 Run& run = blob->fRuns[0]; 685 PerSubRunInfo& subRun = run.fSubRunInfo.back(); 686 subRun.fUseLCDText = origPaint.isLCDRenderText(); 687 subRun.fDrawAsDistanceFields = true; 688 689 *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true); 690 return blob; 691 } 692 693 inline GrAtlasTextContext::BitmapTextBlob* 694 GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip, 695 const GrPaint& paint, const SkPaint& skPaint, 696 const SkMatrix& viewMatrix, 697 const char text[], size_t byteLength, 698 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { 699 int glyphCount = skPaint.countText(text, byteLength); 700 SkIRect clipRect; 701 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); 702 703 BitmapTextBlob* blob; 704 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { 705 SkPaint dfPaint; 706 SkScalar textRatio; 707 SkGlyphCache* cache; 708 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio); 709 710 SkTDArray<char> fallbackTxt; 711 SkTDArray<SkScalar> fallbackPos; 712 SkPoint offset; 713 this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text, 714 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos, 715 &offset, skPaint); 716 SkGlyphCache::AttachCache(cache); 717 if (fallbackTxt.count()) { 718 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt, 719 fallbackPos, 2, offset, clipRect); 720 } 721 } else { 722 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize); 723 blob->fViewMatrix = viewMatrix; 724 725 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false); 726 this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text, 727 byteLength, x, y, clipRect); 728 SkGlyphCache::AttachCache(cache); 729 } 730 return blob; 731 } 732 733 inline GrAtlasTextContext::BitmapTextBlob* 734 GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip, 735 const GrPaint& paint, const SkPaint& skPaint, 736 const SkMatrix& viewMatrix, 737 const char text[], size_t byteLength, 738 const SkScalar pos[], int scalarsPerPosition, 739 const SkPoint& offset, const SkIRect& regionClipBounds) { 740 int glyphCount = skPaint.countText(text, byteLength); 741 742 SkIRect clipRect; 743 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); 744 745 BitmapTextBlob* blob; 746 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { 747 SkPaint dfPaint; 748 SkScalar textRatio; 749 SkGlyphCache* cache; 750 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio); 751 752 SkTDArray<char> fallbackTxt; 753 SkTDArray<SkScalar> fallbackPos; 754 this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text, 755 byteLength, pos, scalarsPerPosition, offset, clipRect, 756 textRatio, &fallbackTxt, &fallbackPos); 757 SkGlyphCache::AttachCache(cache); 758 if (fallbackTxt.count()) { 759 this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt, 760 fallbackPos, scalarsPerPosition, offset, clipRect); 761 } 762 } else { 763 blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize); 764 blob->fViewMatrix = viewMatrix; 765 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false); 766 this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text, 767 byteLength, pos, scalarsPerPosition, offset, clipRect); 768 SkGlyphCache::AttachCache(cache); 769 } 770 return blob; 771 } 772 773 void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip, 774 const GrPaint& paint, const SkPaint& skPaint, 775 const SkMatrix& viewMatrix, 776 const char text[], size_t byteLength, 777 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { 778 SkAutoTUnref<BitmapTextBlob> blob( 779 this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix, 780 text, byteLength, x, y, regionClipBounds)); 781 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds); 782 } 783 784 void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip, 785 const GrPaint& paint, const SkPaint& skPaint, 786 const SkMatrix& viewMatrix, 787 const char text[], size_t byteLength, 788 const SkScalar pos[], int scalarsPerPosition, 789 const SkPoint& offset, const SkIRect& regionClipBounds) { 790 SkAutoTUnref<BitmapTextBlob> blob( 791 this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix, 792 text, byteLength, 793 pos, scalarsPerPosition, 794 offset, regionClipBounds)); 795 796 this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds); 797 } 798 799 void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex, 800 SkGlyphCache* cache, const SkPaint& skPaint, 801 GrColor color, 802 const SkMatrix& viewMatrix, 803 const char text[], size_t byteLength, 804 SkScalar x, SkScalar y, const SkIRect& clipRect) { 805 SkASSERT(byteLength == 0 || text != NULL); 806 807 // nothing to draw 808 if (text == NULL || byteLength == 0) { 809 return; 810 } 811 812 fCurrStrike = NULL; 813 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); 814 815 // Get GrFontScaler from cache 816 GrFontScaler* fontScaler = GetGrFontScaler(cache); 817 818 // transform our starting point 819 { 820 SkPoint loc; 821 viewMatrix.mapXY(x, y, &loc); 822 x = loc.fX; 823 y = loc.fY; 824 } 825 826 // need to measure first 827 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) { 828 SkVector stopVector; 829 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector); 830 831 SkScalar stopX = stopVector.fX; 832 SkScalar stopY = stopVector.fY; 833 834 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { 835 stopX = SkScalarHalf(stopX); 836 stopY = SkScalarHalf(stopY); 837 } 838 x -= stopX; 839 y -= stopY; 840 } 841 842 const char* stop = text + byteLength; 843 844 SkAutoKern autokern; 845 846 SkFixed fxMask = ~0; 847 SkFixed fyMask = ~0; 848 SkScalar halfSampleX, halfSampleY; 849 if (cache->isSubpixel()) { 850 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); 851 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); 852 if (kX_SkAxisAlignment == baseline) { 853 fyMask = 0; 854 halfSampleY = SK_ScalarHalf; 855 } else if (kY_SkAxisAlignment == baseline) { 856 fxMask = 0; 857 halfSampleX = SK_ScalarHalf; 858 } 859 } else { 860 halfSampleX = halfSampleY = SK_ScalarHalf; 861 } 862 863 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX); 864 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY); 865 866 while (text < stop) { 867 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask); 868 869 fx += autokern.adjust(glyph); 870 871 if (glyph.fWidth) { 872 this->bmpAppendGlyph(blob, 873 runIndex, 874 GrGlyph::Pack(glyph.getGlyphID(), 875 glyph.getSubXFixed(), 876 glyph.getSubYFixed(), 877 GrGlyph::kCoverage_MaskStyle), 878 Sk48Dot16FloorToInt(fx), 879 Sk48Dot16FloorToInt(fy), 880 color, 881 fontScaler, 882 clipRect); 883 } 884 885 fx += glyph.fAdvanceX; 886 fy += glyph.fAdvanceY; 887 } 888 } 889 890 void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex, 891 SkGlyphCache* cache, const SkPaint& skPaint, 892 GrColor color, 893 const SkMatrix& viewMatrix, 894 const char text[], size_t byteLength, 895 const SkScalar pos[], int scalarsPerPosition, 896 const SkPoint& offset, const SkIRect& clipRect) { 897 SkASSERT(byteLength == 0 || text != NULL); 898 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 899 900 // nothing to draw 901 if (text == NULL || byteLength == 0) { 902 return; 903 } 904 905 fCurrStrike = NULL; 906 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); 907 908 // Get GrFontScaler from cache 909 GrFontScaler* fontScaler = GetGrFontScaler(cache); 910 911 const char* stop = text + byteLength; 912 SkTextAlignProc alignProc(skPaint.getTextAlign()); 913 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition); 914 915 if (cache->isSubpixel()) { 916 // maybe we should skip the rounding if linearText is set 917 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); 918 919 SkFixed fxMask = ~0; 920 SkFixed fyMask = ~0; 921 SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound); 922 SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); 923 if (kX_SkAxisAlignment == baseline) { 924 fyMask = 0; 925 halfSampleY = SK_ScalarHalf; 926 } else if (kY_SkAxisAlignment == baseline) { 927 fxMask = 0; 928 halfSampleX = SK_ScalarHalf; 929 } 930 931 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { 932 while (text < stop) { 933 SkPoint tmsLoc; 934 tmsProc(pos, &tmsLoc); 935 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX); 936 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY); 937 938 const SkGlyph& glyph = glyphCacheProc(cache, &text, 939 fx & fxMask, fy & fyMask); 940 941 if (glyph.fWidth) { 942 this->bmpAppendGlyph(blob, 943 runIndex, 944 GrGlyph::Pack(glyph.getGlyphID(), 945 glyph.getSubXFixed(), 946 glyph.getSubYFixed(), 947 GrGlyph::kCoverage_MaskStyle), 948 Sk48Dot16FloorToInt(fx), 949 Sk48Dot16FloorToInt(fy), 950 color, 951 fontScaler, 952 clipRect); 953 } 954 pos += scalarsPerPosition; 955 } 956 } else { 957 while (text < stop) { 958 const char* currentText = text; 959 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0); 960 961 if (metricGlyph.fWidth) { 962 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;) 963 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;) 964 SkPoint tmsLoc; 965 tmsProc(pos, &tmsLoc); 966 SkPoint alignLoc; 967 alignProc(tmsLoc, metricGlyph, &alignLoc); 968 969 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX); 970 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY); 971 972 // have to call again, now that we've been "aligned" 973 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText, 974 fx & fxMask, fy & fyMask); 975 // the assumption is that the metrics haven't changed 976 SkASSERT(prevAdvX == glyph.fAdvanceX); 977 SkASSERT(prevAdvY == glyph.fAdvanceY); 978 SkASSERT(glyph.fWidth); 979 980 this->bmpAppendGlyph(blob, 981 runIndex, 982 GrGlyph::Pack(glyph.getGlyphID(), 983 glyph.getSubXFixed(), 984 glyph.getSubYFixed(), 985 GrGlyph::kCoverage_MaskStyle), 986 Sk48Dot16FloorToInt(fx), 987 Sk48Dot16FloorToInt(fy), 988 color, 989 fontScaler, 990 clipRect); 991 } 992 pos += scalarsPerPosition; 993 } 994 } 995 } else { // not subpixel 996 997 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { 998 while (text < stop) { 999 // the last 2 parameters are ignored 1000 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 1001 1002 if (glyph.fWidth) { 1003 SkPoint tmsLoc; 1004 tmsProc(pos, &tmsLoc); 1005 1006 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX; 1007 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY; 1008 this->bmpAppendGlyph(blob, 1009 runIndex, 1010 GrGlyph::Pack(glyph.getGlyphID(), 1011 glyph.getSubXFixed(), 1012 glyph.getSubYFixed(), 1013 GrGlyph::kCoverage_MaskStyle), 1014 Sk48Dot16FloorToInt(fx), 1015 Sk48Dot16FloorToInt(fy), 1016 color, 1017 fontScaler, 1018 clipRect); 1019 } 1020 pos += scalarsPerPosition; 1021 } 1022 } else { 1023 while (text < stop) { 1024 // the last 2 parameters are ignored 1025 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 1026 1027 if (glyph.fWidth) { 1028 SkPoint tmsLoc; 1029 tmsProc(pos, &tmsLoc); 1030 1031 SkPoint alignLoc; 1032 alignProc(tmsLoc, glyph, &alignLoc); 1033 1034 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX; 1035 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY; 1036 this->bmpAppendGlyph(blob, 1037 runIndex, 1038 GrGlyph::Pack(glyph.getGlyphID(), 1039 glyph.getSubXFixed(), 1040 glyph.getSubYFixed(), 1041 GrGlyph::kCoverage_MaskStyle), 1042 Sk48Dot16FloorToInt(fx), 1043 Sk48Dot16FloorToInt(fy), 1044 color, 1045 fontScaler, 1046 clipRect); 1047 } 1048 pos += scalarsPerPosition; 1049 } 1050 } 1051 } 1052 } 1053 1054 1055 void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex, 1056 SkGlyphCache* cache, const SkPaint& skPaint, 1057 GrColor color, 1058 const SkMatrix& viewMatrix, 1059 const char text[], size_t byteLength, 1060 SkScalar x, SkScalar y, const SkIRect& clipRect, 1061 SkScalar textRatio, 1062 SkTDArray<char>* fallbackTxt, 1063 SkTDArray<SkScalar>* fallbackPos, 1064 SkPoint* offset, 1065 const SkPaint& origPaint) { 1066 SkASSERT(byteLength == 0 || text != NULL); 1067 1068 // nothing to draw 1069 if (text == NULL || byteLength == 0) { 1070 return; 1071 } 1072 1073 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc(); 1074 SkAutoDescriptor desc; 1075 origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true); 1076 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(), 1077 desc.getDesc()); 1078 1079 SkTArray<SkScalar> positions; 1080 1081 const char* textPtr = text; 1082 SkFixed stopX = 0; 1083 SkFixed stopY = 0; 1084 SkFixed origin = 0; 1085 switch (origPaint.getTextAlign()) { 1086 case SkPaint::kRight_Align: origin = SK_Fixed1; break; 1087 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; 1088 case SkPaint::kLeft_Align: origin = 0; break; 1089 } 1090 1091 SkAutoKern autokern; 1092 const char* stop = text + byteLength; 1093 while (textPtr < stop) { 1094 // don't need x, y here, since all subpixel variants will have the 1095 // same advance 1096 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0); 1097 1098 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); 1099 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); 1100 1101 SkFixed height = glyph.fAdvanceY; 1102 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height))); 1103 1104 stopX += width; 1105 stopY += height; 1106 } 1107 SkASSERT(textPtr == stop); 1108 1109 // now adjust starting point depending on alignment 1110 SkScalar alignX = SkFixedToScalar(stopX); 1111 SkScalar alignY = SkFixedToScalar(stopY); 1112 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) { 1113 alignX = SkScalarHalf(alignX); 1114 alignY = SkScalarHalf(alignY); 1115 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) { 1116 alignX = 0; 1117 alignY = 0; 1118 } 1119 x -= alignX; 1120 y -= alignY; 1121 *offset = SkPoint::Make(x, y); 1122 1123 this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength, 1124 positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt, 1125 fallbackPos); 1126 SkGlyphCache::AttachCache(origPaintCache); 1127 } 1128 1129 void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex, 1130 SkGlyphCache* cache, const SkPaint& skPaint, 1131 GrColor color, 1132 const SkMatrix& viewMatrix, 1133 const char text[], size_t byteLength, 1134 const SkScalar pos[], int scalarsPerPosition, 1135 const SkPoint& offset, const SkIRect& clipRect, 1136 SkScalar textRatio, 1137 SkTDArray<char>* fallbackTxt, 1138 SkTDArray<SkScalar>* fallbackPos) { 1139 1140 SkASSERT(byteLength == 0 || text != NULL); 1141 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 1142 1143 // nothing to draw 1144 if (text == NULL || byteLength == 0) { 1145 return; 1146 } 1147 1148 fCurrStrike = NULL; 1149 1150 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); 1151 GrFontScaler* fontScaler = GetGrFontScaler(cache); 1152 1153 const char* stop = text + byteLength; 1154 1155 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { 1156 while (text < stop) { 1157 const char* lastText = text; 1158 // the last 2 parameters are ignored 1159 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 1160 1161 if (glyph.fWidth) { 1162 SkScalar x = offset.x() + pos[0]; 1163 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); 1164 1165 if (!this->dfAppendGlyph(blob, 1166 runIndex, 1167 GrGlyph::Pack(glyph.getGlyphID(), 1168 glyph.getSubXFixed(), 1169 glyph.getSubYFixed(), 1170 GrGlyph::kDistance_MaskStyle), 1171 x, y, color, fontScaler, clipRect, 1172 textRatio, viewMatrix)) { 1173 // couldn't append, send to fallback 1174 fallbackTxt->append(SkToInt(text-lastText), lastText); 1175 *fallbackPos->append() = pos[0]; 1176 if (2 == scalarsPerPosition) { 1177 *fallbackPos->append() = pos[1]; 1178 } 1179 } 1180 } 1181 pos += scalarsPerPosition; 1182 } 1183 } else { 1184 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf 1185 : SK_Scalar1; 1186 while (text < stop) { 1187 const char* lastText = text; 1188 // the last 2 parameters are ignored 1189 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 1190 1191 if (glyph.fWidth) { 1192 SkScalar x = offset.x() + pos[0]; 1193 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); 1194 1195 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio; 1196 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio; 1197 1198 if (!this->dfAppendGlyph(blob, 1199 runIndex, 1200 GrGlyph::Pack(glyph.getGlyphID(), 1201 glyph.getSubXFixed(), 1202 glyph.getSubYFixed(), 1203 GrGlyph::kDistance_MaskStyle), 1204 x - advanceX, y - advanceY, color, 1205 fontScaler, 1206 clipRect, 1207 textRatio, 1208 viewMatrix)) { 1209 // couldn't append, send to fallback 1210 fallbackTxt->append(SkToInt(text-lastText), lastText); 1211 *fallbackPos->append() = pos[0]; 1212 if (2 == scalarsPerPosition) { 1213 *fallbackPos->append() = pos[1]; 1214 } 1215 } 1216 } 1217 pos += scalarsPerPosition; 1218 } 1219 } 1220 } 1221 1222 void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex, 1223 GrGlyph::PackedID packed, 1224 int vx, int vy, GrColor color, GrFontScaler* scaler, 1225 const SkIRect& clipRect) { 1226 Run& run = blob->fRuns[runIndex]; 1227 if (!fCurrStrike) { 1228 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); 1229 run.fStrike.reset(SkRef(fCurrStrike)); 1230 } 1231 1232 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler); 1233 if (!glyph) { 1234 return; 1235 } 1236 1237 int x = vx + glyph->fBounds.fLeft; 1238 int y = vy + glyph->fBounds.fTop; 1239 1240 // keep them as ints until we've done the clip-test 1241 int width = glyph->fBounds.width(); 1242 int height = glyph->fBounds.height(); 1243 1244 #if 0 1245 // Not checking the clip bounds might introduce a performance regression. However, its not 1246 // clear if this is still true today with the larger tiles we use in Chrome. For repositionable 1247 // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal. 1248 // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls 1249 // TODO verify this 1250 // check if we clipped out 1251 if (clipRect.quickReject(x, y, x + width, y + height)) { 1252 return; 1253 } 1254 #endif 1255 1256 // If the glyph is too large we fall back to paths 1257 if (glyph->fTooLargeForAtlas) { 1258 this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy)); 1259 return; 1260 } 1261 1262 GrMaskFormat format = glyph->fMaskFormat; 1263 1264 PerSubRunInfo* subRun = &run.fSubRunInfo.back(); 1265 if (run.fInitialized && subRun->fMaskFormat != format) { 1266 subRun = &run.fSubRunInfo.push_back(); 1267 } 1268 1269 run.fInitialized = true; 1270 1271 size_t vertexStride = get_vertex_stride(format); 1272 1273 SkRect r; 1274 r.fLeft = SkIntToScalar(x); 1275 r.fTop = SkIntToScalar(y); 1276 r.fRight = r.fLeft + SkIntToScalar(width); 1277 r.fBottom = r.fTop + SkIntToScalar(height); 1278 subRun->fMaskFormat = format; 1279 this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format, 1280 glyph); 1281 } 1282 1283 bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex, 1284 GrGlyph::PackedID packed, 1285 SkScalar sx, SkScalar sy, GrColor color, 1286 GrFontScaler* scaler, 1287 const SkIRect& clipRect, 1288 SkScalar textRatio, const SkMatrix& viewMatrix) { 1289 Run& run = blob->fRuns[runIndex]; 1290 if (!fCurrStrike) { 1291 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); 1292 run.fStrike.reset(SkRef(fCurrStrike)); 1293 } 1294 1295 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler); 1296 if (!glyph) { 1297 return true; 1298 } 1299 1300 // fallback to color glyph support 1301 if (kA8_GrMaskFormat != glyph->fMaskFormat) { 1302 return false; 1303 } 1304 1305 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); 1306 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); 1307 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset); 1308 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset); 1309 1310 SkScalar scale = textRatio; 1311 dx *= scale; 1312 dy *= scale; 1313 width *= scale; 1314 height *= scale; 1315 sx += dx; 1316 sy += dy; 1317 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); 1318 1319 #if 0 1320 // check if we clipped out 1321 SkRect dstRect; 1322 viewMatrix.mapRect(&dstRect, glyphRect); 1323 if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()), 1324 SkScalarTruncToInt(dstRect.top()), 1325 SkScalarTruncToInt(dstRect.right()), 1326 SkScalarTruncToInt(dstRect.bottom()))) { 1327 return true; 1328 } 1329 #endif 1330 1331 // TODO combine with the above 1332 // If the glyph is too large we fall back to paths 1333 if (glyph->fTooLargeForAtlas) { 1334 this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy); 1335 return true; 1336 } 1337 1338 PerSubRunInfo* subRun = &run.fSubRunInfo.back(); 1339 SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat); 1340 subRun->fMaskFormat = kA8_GrMaskFormat; 1341 1342 size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText); 1343 1344 bool useColorVerts = !subRun->fUseLCDText; 1345 this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts, 1346 glyph); 1347 return true; 1348 } 1349 1350 inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph, 1351 GrFontScaler* scaler, SkScalar x, SkScalar y) { 1352 if (NULL == glyph->fPath) { 1353 SkPath* path = SkNEW(SkPath); 1354 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 1355 // flag the glyph as being dead? 1356 SkDELETE(path); 1357 return; 1358 } 1359 glyph->fPath = path; 1360 } 1361 SkASSERT(glyph->fPath); 1362 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y)); 1363 } 1364 1365 inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run, 1366 Run::SubRunInfo* subRun, 1367 const SkRect& positions, GrColor color, 1368 size_t vertexStride, bool useVertexColor, 1369 GrGlyph* glyph) { 1370 blob->fGlyphs[subRun->fGlyphEndIndex] = glyph; 1371 run->fVertexBounds.joinNonEmptyArg(positions); 1372 run->fColor = color; 1373 1374 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex); 1375 1376 if (useVertexColor) { 1377 // V0 1378 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); 1379 position->set(positions.fLeft, positions.fTop); 1380 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 1381 *colorPtr = color; 1382 vertex += vertexStride; 1383 1384 // V1 1385 position = reinterpret_cast<SkPoint*>(vertex); 1386 position->set(positions.fLeft, positions.fBottom); 1387 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 1388 *colorPtr = color; 1389 vertex += vertexStride; 1390 1391 // V2 1392 position = reinterpret_cast<SkPoint*>(vertex); 1393 position->set(positions.fRight, positions.fBottom); 1394 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 1395 *colorPtr = color; 1396 vertex += vertexStride; 1397 1398 // V3 1399 position = reinterpret_cast<SkPoint*>(vertex); 1400 position->set(positions.fRight, positions.fTop); 1401 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); 1402 *colorPtr = color; 1403 } else { 1404 // V0 1405 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); 1406 position->set(positions.fLeft, positions.fTop); 1407 vertex += vertexStride; 1408 1409 // V1 1410 position = reinterpret_cast<SkPoint*>(vertex); 1411 position->set(positions.fLeft, positions.fBottom); 1412 vertex += vertexStride; 1413 1414 // V2 1415 position = reinterpret_cast<SkPoint*>(vertex); 1416 position->set(positions.fRight, positions.fBottom); 1417 vertex += vertexStride; 1418 1419 // V3 1420 position = reinterpret_cast<SkPoint*>(vertex); 1421 position->set(positions.fRight, positions.fTop); 1422 } 1423 1424 subRun->fGlyphEndIndex++; 1425 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph; 1426 } 1427 1428 class BitmapTextBatch : public GrBatch { 1429 public: 1430 typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable; 1431 typedef GrAtlasTextContext::BitmapTextBlob Blob; 1432 typedef Blob::Run Run; 1433 typedef Run::SubRunInfo TextInfo; 1434 struct Geometry { 1435 Blob* fBlob; 1436 int fRun; 1437 int fSubRun; 1438 GrColor fColor; 1439 SkScalar fTransX; 1440 SkScalar fTransY; 1441 }; 1442 1443 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount, 1444 GrBatchFontCache* fontCache) { 1445 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache)); 1446 } 1447 1448 static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount, 1449 GrBatchFontCache* fontCache, 1450 DistanceAdjustTable* distanceAdjustTable, 1451 SkColor filteredColor, bool useLCDText, 1452 bool useBGR, float gamma) { 1453 return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable, 1454 filteredColor, useLCDText, useBGR, gamma)); 1455 } 1456 1457 const char* name() const override { return "BitmapTextBatch"; } 1458 1459 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 1460 if (kARGB_GrMaskFormat == fMaskFormat) { 1461 out->setUnknownFourComponents(); 1462 } else { 1463 out->setKnownFourComponents(fBatch.fColor); 1464 } 1465 } 1466 1467 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 1468 if (!fUseDistanceFields) { 1469 // Bitmap Text 1470 if (kARGB_GrMaskFormat != fMaskFormat) { 1471 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) { 1472 out->setUnknownSingleComponent(); 1473 } else if (GrPixelConfigIsOpaque(fPixelConfig)) { 1474 out->setUnknownOpaqueFourComponents(); 1475 out->setUsingLCDCoverage(); 1476 } else { 1477 out->setUnknownFourComponents(); 1478 out->setUsingLCDCoverage(); 1479 } 1480 } else { 1481 out->setKnownSingleComponent(0xff); 1482 } 1483 } else { 1484 // Distance fields 1485 if (!fUseLCDText) { 1486 out->setUnknownSingleComponent(); 1487 } else { 1488 out->setUnknownFourComponents(); 1489 out->setUsingLCDCoverage(); 1490 } 1491 } 1492 } 1493 1494 void initBatchTracker(const GrPipelineInfo& init) override { 1495 // Handle any color overrides 1496 if (init.fColorIgnored) { 1497 fBatch.fColor = GrColor_ILLEGAL; 1498 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 1499 fBatch.fColor = init.fOverrideColor; 1500 } 1501 1502 // setup batch properties 1503 fBatch.fColorIgnored = init.fColorIgnored; 1504 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 1505 fBatch.fCoverageIgnored = init.fCoverageIgnored; 1506 } 1507 1508 struct FlushInfo { 1509 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer; 1510 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer; 1511 int fGlyphsToFlush; 1512 int fVertexOffset; 1513 }; 1514 1515 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 1516 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix. 1517 // TODO actually only invert if we don't have RGBA 1518 SkMatrix localMatrix; 1519 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { 1520 SkDebugf("Cannot invert viewmatrix\n"); 1521 return; 1522 } 1523 1524 GrTexture* texture = fFontCache->getTexture(fMaskFormat); 1525 if (!texture) { 1526 SkDebugf("Could not allocate backing texture for atlas\n"); 1527 return; 1528 } 1529 1530 SkAutoTUnref<const GrGeometryProcessor> gp; 1531 if (fUseDistanceFields) { 1532 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), 1533 texture)); 1534 } else { 1535 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); 1536 gp.reset(GrBitmapTextGeoProc::Create(this->color(), 1537 texture, 1538 params, 1539 fMaskFormat, 1540 localMatrix)); 1541 } 1542 1543 FlushInfo flushInfo; 1544 flushInfo.fGlyphsToFlush = 0; 1545 size_t vertexStride = gp->getVertexStride(); 1546 SkASSERT(vertexStride == (fUseDistanceFields ? 1547 get_vertex_stride_df(fMaskFormat, fUseLCDText) : 1548 get_vertex_stride(fMaskFormat))); 1549 1550 this->initDraw(batchTarget, gp, pipeline); 1551 1552 int glyphCount = this->numGlyphs(); 1553 int instanceCount = fInstanceCount; 1554 const GrVertexBuffer* vertexBuffer; 1555 1556 void* vertices = batchTarget->makeVertSpace(vertexStride, 1557 glyphCount * kVerticesPerGlyph, 1558 &vertexBuffer, 1559 &flushInfo.fVertexOffset); 1560 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer)); 1561 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer()); 1562 if (!vertices || !flushInfo.fVertexBuffer) { 1563 SkDebugf("Could not allocate vertices\n"); 1564 return; 1565 } 1566 1567 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); 1568 1569 // We cache some values to avoid going to the glyphcache for the same fontScaler twice 1570 // in a row 1571 const SkDescriptor* desc = NULL; 1572 SkGlyphCache* cache = NULL; 1573 GrFontScaler* scaler = NULL; 1574 SkTypeface* typeface = NULL; 1575 1576 for (int i = 0; i < instanceCount; i++) { 1577 Geometry& args = fGeoData[i]; 1578 Blob* blob = args.fBlob; 1579 Run& run = blob->fRuns[args.fRun]; 1580 TextInfo& info = run.fSubRunInfo[args.fSubRun]; 1581 1582 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat); 1583 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen; 1584 bool regenerateColors; 1585 if (fUseDistanceFields) { 1586 regenerateColors = !fUseLCDText && run.fColor != args.fColor; 1587 } else { 1588 regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor; 1589 } 1590 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f; 1591 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; 1592 1593 // We regenerate both texture coords and colors in the blob itself, and update the 1594 // atlas generation. If we don't end up purging any unused plots, we can avoid 1595 // regenerating the coords. We could take a finer grained approach to updating texture 1596 // coords but its not clear if the extra bookkeeping would offset any gains. 1597 // To avoid looping over the glyphs twice, we do one loop and conditionally update color 1598 // or coords as needed. One final note, if we have to break a run for an atlas eviction 1599 // then we can't really trust the atlas has all of the correct data. Atlas evictions 1600 // should be pretty rare, so we just always regenerate in those cases 1601 if (regenerateTextureCoords || regenerateColors || regeneratePositions) { 1602 // first regenerate texture coordinates / colors if need be 1603 bool brokenRun = false; 1604 1605 // Because the GrBatchFontCache may evict the strike a blob depends on using for 1606 // generating its texture coords, we have to track whether or not the strike has 1607 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is 1608 // otherwise we have to get the new strike, and use that to get the correct glyphs. 1609 // Because we do not have the packed ids, and thus can't look up our glyphs in the 1610 // new strike, we instead keep our ref to the old strike and use the packed ids from 1611 // it. These ids will still be valid as long as we hold the ref. When we are done 1612 // updating our cache of the GrGlyph*s, we drop our ref on the old strike 1613 bool regenerateGlyphs = false; 1614 GrBatchTextStrike* strike = NULL; 1615 if (regenerateTextureCoords) { 1616 info.fBulkUseToken.reset(); 1617 1618 // We can reuse if we have a valid strike and our descriptors / typeface are the 1619 // same 1620 const SkDescriptor* newDesc = run.fOverrideDescriptor ? 1621 run.fOverrideDescriptor->getDesc() : 1622 run.fDescriptor.getDesc(); 1623 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) || 1624 !(desc->equals(*newDesc))) { 1625 if (cache) { 1626 SkGlyphCache::AttachCache(cache); 1627 } 1628 desc = newDesc; 1629 cache = SkGlyphCache::DetachCache(run.fTypeface, desc); 1630 scaler = GrTextContext::GetGrFontScaler(cache); 1631 strike = run.fStrike; 1632 typeface = run.fTypeface; 1633 } 1634 1635 if (run.fStrike->isAbandoned()) { 1636 regenerateGlyphs = true; 1637 strike = fFontCache->getStrike(scaler); 1638 } else { 1639 strike = run.fStrike; 1640 } 1641 } 1642 1643 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { 1644 if (regenerateTextureCoords) { 1645 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex; 1646 GrGlyph* glyph; 1647 if (regenerateGlyphs) { 1648 // Get the id from the old glyph, and use the new strike to lookup 1649 // the glyph. 1650 glyph = blob->fGlyphs[glyphOffset]; 1651 blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID, 1652 scaler); 1653 } 1654 glyph = blob->fGlyphs[glyphOffset]; 1655 SkASSERT(glyph); 1656 1657 if (!fFontCache->hasGlyph(glyph) && 1658 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) { 1659 this->flush(batchTarget, &flushInfo); 1660 this->initDraw(batchTarget, gp, pipeline); 1661 brokenRun = glyphIdx > 0; 1662 1663 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget, 1664 glyph, 1665 scaler); 1666 SkASSERT(success); 1667 } 1668 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph, 1669 batchTarget->currentToken()); 1670 1671 // Texture coords are the last vertex attribute so we get a pointer to the 1672 // first one and then map with stride in regenerateTextureCoords 1673 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices); 1674 vertex += info.fVertexStartIndex; 1675 vertex += vertexStride * glyphIdx * kVerticesPerGlyph; 1676 vertex += vertexStride - sizeof(SkIPoint16); 1677 1678 this->regenerateTextureCoords(glyph, vertex, vertexStride); 1679 } 1680 1681 if (regenerateColors) { 1682 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices); 1683 vertex += info.fVertexStartIndex; 1684 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint); 1685 this->regenerateColors(vertex, vertexStride, args.fColor); 1686 } 1687 1688 if (regeneratePositions) { 1689 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices); 1690 vertex += info.fVertexStartIndex; 1691 vertex += vertexStride * glyphIdx * kVerticesPerGlyph; 1692 SkScalar transX = args.fTransX; 1693 SkScalar transY = args.fTransY; 1694 this->regeneratePositions(vertex, vertexStride, transX, transY); 1695 } 1696 flushInfo.fGlyphsToFlush++; 1697 } 1698 1699 // We my have changed the color so update it here 1700 run.fColor = args.fColor; 1701 if (regenerateTextureCoords) { 1702 if (regenerateGlyphs) { 1703 run.fStrike.reset(SkRef(strike)); 1704 } 1705 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration : 1706 fFontCache->atlasGeneration(fMaskFormat); 1707 } 1708 } else { 1709 flushInfo.fGlyphsToFlush += glyphCount; 1710 1711 // set use tokens for all of the glyphs in our subrun. This is only valid if we 1712 // have a valid atlas generation 1713 fFontCache->setUseTokenBulk(info.fBulkUseToken, 1714 batchTarget->currentToken(), 1715 fMaskFormat); 1716 } 1717 1718 // now copy all vertices 1719 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex; 1720 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount); 1721 1722 currVertex += byteCount; 1723 } 1724 // Make sure to attach the last cache if applicable 1725 if (cache) { 1726 SkGlyphCache::AttachCache(cache); 1727 } 1728 this->flush(batchTarget, &flushInfo); 1729 } 1730 1731 // The minimum number of Geometry we will try to allocate. 1732 static const int kMinAllocated = 32; 1733 1734 // Total number of Geometry this Batch owns 1735 int instanceCount() const { return fInstanceCount; } 1736 SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; } 1737 1738 // to avoid even the initial copy of the struct, we have a getter for the first item which 1739 // is used to seed the batch with its initial geometry. After seeding, the client should call 1740 // init() so the Batch can initialize itself 1741 Geometry& geometry() { return fGeoData[0]; } 1742 void init() { 1743 const Geometry& geo = fGeoData[0]; 1744 fBatch.fColor = geo.fColor; 1745 fBatch.fViewMatrix = geo.fBlob->fViewMatrix; 1746 1747 // We don't yet position distance field text on the cpu, so we have to map the vertex bounds 1748 // into device space 1749 const Run& run = geo.fBlob->fRuns[geo.fRun]; 1750 if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) { 1751 SkRect bounds = run.fVertexBounds; 1752 fBatch.fViewMatrix.mapRect(&bounds); 1753 this->setBounds(bounds); 1754 } else { 1755 this->setBounds(run.fVertexBounds); 1756 } 1757 } 1758 1759 private: 1760 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache) 1761 : fMaskFormat(maskFormat) 1762 , fPixelConfig(fontCache->getPixelConfig(maskFormat)) 1763 , fFontCache(fontCache) 1764 , fUseDistanceFields(false) { 1765 this->initClassID<BitmapTextBatch>(); 1766 fBatch.fNumGlyphs = glyphCount; 1767 fInstanceCount = 1; 1768 fAllocatedCount = kMinAllocated; 1769 } 1770 1771 BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache, 1772 DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor, 1773 bool useLCDText, bool useBGR, float gamma) 1774 : fMaskFormat(maskFormat) 1775 , fPixelConfig(fontCache->getPixelConfig(maskFormat)) 1776 , fFontCache(fontCache) 1777 , fDistanceAdjustTable(SkRef(distanceAdjustTable)) 1778 , fFilteredColor(filteredColor) 1779 , fUseDistanceFields(true) 1780 , fUseLCDText(useLCDText) 1781 , fUseBGR(useBGR) 1782 , fGamma(gamma) { 1783 this->initClassID<BitmapTextBatch>(); 1784 fBatch.fNumGlyphs = glyphCount; 1785 fInstanceCount = 1; 1786 fAllocatedCount = kMinAllocated; 1787 SkASSERT(fMaskFormat == kA8_GrMaskFormat); 1788 } 1789 1790 ~BitmapTextBatch() { 1791 for (int i = 0; i < fInstanceCount; i++) { 1792 fGeoData[i].fBlob->unref(); 1793 } 1794 } 1795 1796 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) { 1797 int width = glyph->fBounds.width(); 1798 int height = glyph->fBounds.height(); 1799 1800 int u0, v0, u1, v1; 1801 if (fUseDistanceFields) { 1802 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset; 1803 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset; 1804 u1 = u0 + width - 2 * SK_DistanceFieldInset; 1805 v1 = v0 + height - 2 * SK_DistanceFieldInset; 1806 } else { 1807 u0 = glyph->fAtlasLocation.fX; 1808 v0 = glyph->fAtlasLocation.fY; 1809 u1 = u0 + width; 1810 v1 = v0 + height; 1811 } 1812 1813 SkIPoint16* textureCoords; 1814 // V0 1815 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); 1816 textureCoords->set(u0, v0); 1817 vertex += vertexStride; 1818 1819 // V1 1820 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); 1821 textureCoords->set(u0, v1); 1822 vertex += vertexStride; 1823 1824 // V2 1825 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); 1826 textureCoords->set(u1, v1); 1827 vertex += vertexStride; 1828 1829 // V3 1830 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); 1831 textureCoords->set(u1, v0); 1832 } 1833 1834 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) { 1835 for (int i = 0; i < kVerticesPerGlyph; i++) { 1836 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex); 1837 *vcolor = color; 1838 vertex += vertexStride; 1839 } 1840 } 1841 1842 void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX, 1843 SkScalar transY) { 1844 for (int i = 0; i < kVerticesPerGlyph; i++) { 1845 SkPoint* point = reinterpret_cast<SkPoint*>(vertex); 1846 point->fX += transX; 1847 point->fY += transY; 1848 vertex += vertexStride; 1849 } 1850 } 1851 1852 void initDraw(GrBatchTarget* batchTarget, 1853 const GrGeometryProcessor* gp, 1854 const GrPipeline* pipeline) { 1855 batchTarget->initDraw(gp, pipeline); 1856 1857 // TODO remove this when batch is everywhere 1858 GrPipelineInfo init; 1859 init.fColorIgnored = fBatch.fColorIgnored; 1860 init.fOverrideColor = GrColor_ILLEGAL; 1861 init.fCoverageIgnored = fBatch.fCoverageIgnored; 1862 init.fUsesLocalCoords = this->usesLocalCoords(); 1863 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 1864 } 1865 1866 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) { 1867 GrVertices vertices; 1868 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads(); 1869 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer, 1870 flushInfo->fIndexBuffer, flushInfo->fVertexOffset, 1871 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush, 1872 maxGlyphsPerDraw); 1873 batchTarget->draw(vertices); 1874 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush; 1875 flushInfo->fGlyphsToFlush = 0; 1876 } 1877 1878 GrColor color() const { return fBatch.fColor; } 1879 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } 1880 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 1881 int numGlyphs() const { return fBatch.fNumGlyphs; } 1882 1883 bool onCombineIfPossible(GrBatch* t) override { 1884 BitmapTextBatch* that = t->cast<BitmapTextBatch>(); 1885 1886 if (fUseDistanceFields != that->fUseDistanceFields) { 1887 return false; 1888 } 1889 1890 if (!fUseDistanceFields) { 1891 // Bitmap Text 1892 if (fMaskFormat != that->fMaskFormat) { 1893 return false; 1894 } 1895 1896 // TODO we can often batch across LCD text if we have dual source blending and don't 1897 // have to use the blend constant 1898 if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) { 1899 return false; 1900 } 1901 1902 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1903 return false; 1904 } 1905 } else { 1906 // Distance Fields 1907 SkASSERT(this->fMaskFormat == that->fMaskFormat && 1908 this->fMaskFormat == kA8_GrMaskFormat); 1909 1910 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1911 return false; 1912 } 1913 1914 if (fFilteredColor != that->fFilteredColor) { 1915 return false; 1916 } 1917 1918 if (fUseLCDText != that->fUseLCDText) { 1919 return false; 1920 } 1921 1922 if (fUseBGR != that->fUseBGR) { 1923 return false; 1924 } 1925 1926 if (fGamma != that->fGamma) { 1927 return false; 1928 } 1929 1930 // TODO see note above 1931 if (fUseLCDText && this->color() != that->color()) { 1932 return false; 1933 } 1934 } 1935 1936 fBatch.fNumGlyphs += that->numGlyphs(); 1937 1938 // copy that->geoData(). We do this manually for performance reasons 1939 SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData(); 1940 int otherInstanceCount = that->instanceCount(); 1941 int allocSize = otherInstanceCount + fInstanceCount; 1942 if (allocSize > fAllocatedCount) { 1943 while (allocSize > fAllocatedCount) { 1944 fAllocatedCount = fAllocatedCount << 1; 1945 } 1946 fGeoData.realloc(fAllocatedCount); 1947 } 1948 1949 memcpy(&fGeoData[fInstanceCount], otherGeoData->get(), 1950 otherInstanceCount * sizeof(Geometry)); 1951 int total = fInstanceCount + otherInstanceCount; 1952 for (int i = fInstanceCount; i < total; i++) { 1953 fGeoData[i].fBlob->ref(); 1954 } 1955 fInstanceCount = total; 1956 1957 this->joinBounds(that->bounds()); 1958 return true; 1959 } 1960 1961 // TODO just use class params 1962 // TODO trying to figure out why lcd is so whack 1963 GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor, 1964 GrColor color, GrTexture* texture) { 1965 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); 1966 1967 // set up any flags 1968 uint32_t flags = 0; 1969 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; 1970 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0; 1971 flags |= fUseLCDText && viewMatrix.rectStaysRect() ? 1972 kRectToRect_DistanceFieldEffectFlag : 0; 1973 flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0; 1974 1975 // see if we need to create a new effect 1976 if (fUseLCDText) { 1977 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor); 1978 1979 float redCorrection = 1980 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift]; 1981 float greenCorrection = 1982 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift]; 1983 float blueCorrection = 1984 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift]; 1985 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust = 1986 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection, 1987 greenCorrection, 1988 blueCorrection); 1989 1990 return GrDistanceFieldLCDTextGeoProc::Create(color, 1991 viewMatrix, 1992 texture, 1993 params, 1994 widthAdjust, 1995 flags); 1996 } else { 1997 flags |= kColorAttr_DistanceFieldEffectFlag; 1998 #ifdef SK_GAMMA_APPLY_TO_A8 1999 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor); 2000 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift]; 2001 return GrDistanceFieldA8TextGeoProc::Create(color, 2002 viewMatrix, 2003 texture, 2004 params, 2005 correction, 2006 flags); 2007 #else 2008 return GrDistanceFieldA8TextGeoProc::Create(color, 2009 viewMatrix, 2010 texture, 2011 params, 2012 flags); 2013 #endif 2014 } 2015 2016 } 2017 2018 struct BatchTracker { 2019 GrColor fColor; 2020 SkMatrix fViewMatrix; 2021 bool fUsesLocalCoords; 2022 bool fColorIgnored; 2023 bool fCoverageIgnored; 2024 int fNumGlyphs; 2025 }; 2026 2027 BatchTracker fBatch; 2028 SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData; 2029 int fInstanceCount; 2030 int fAllocatedCount; 2031 GrMaskFormat fMaskFormat; 2032 GrPixelConfig fPixelConfig; 2033 GrBatchFontCache* fFontCache; 2034 2035 // Distance field properties 2036 SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable; 2037 SkColor fFilteredColor; 2038 bool fUseDistanceFields; 2039 bool fUseLCDText; 2040 bool fUseBGR; 2041 float fGamma; 2042 }; 2043 2044 void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint, 2045 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix, 2046 const SkIRect& clipBounds, SkScalar x, SkScalar y) { 2047 SkPaint runPaint = skPaint; 2048 2049 size_t textLen = it.glyphCount() * sizeof(uint16_t); 2050 const SkPoint& offset = it.offset(); 2051 2052 it.applyFontToPaint(&runPaint); 2053 2054 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) { 2055 return; 2056 } 2057 2058 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint)); 2059 2060 switch (it.positioning()) { 2061 case SkTextBlob::kDefault_Positioning: 2062 this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(), 2063 textLen, x + offset.x(), y + offset.y(), clipBounds); 2064 break; 2065 case SkTextBlob::kHorizontal_Positioning: 2066 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(), 2067 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), 2068 clipBounds); 2069 break; 2070 case SkTextBlob::kFull_Positioning: 2071 this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(), 2072 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds); 2073 break; 2074 } 2075 } 2076 2077 2078 inline BitmapTextBatch* 2079 GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info, 2080 int glyphCount, int run, int subRun, 2081 GrColor color, SkScalar transX, SkScalar transY, 2082 const SkPaint& skPaint) { 2083 GrMaskFormat format = info.fMaskFormat; 2084 GrColor subRunColor; 2085 if (kARGB_GrMaskFormat == format) { 2086 uint8_t paintAlpha = skPaint.getAlpha(); 2087 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha); 2088 } else { 2089 subRunColor = color; 2090 } 2091 2092 BitmapTextBatch* batch; 2093 if (info.fDrawAsDistanceFields) { 2094 SkColor filteredColor; 2095 SkColorFilter* colorFilter = skPaint.getColorFilter(); 2096 if (colorFilter) { 2097 filteredColor = colorFilter->filterColor(skPaint.getColor()); 2098 } else { 2099 filteredColor = skPaint.getColor(); 2100 } 2101 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry()); 2102 float gamma = fDeviceProperties.gamma(); 2103 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(), 2104 fDistanceAdjustTable, filteredColor, 2105 info.fUseLCDText, useBGR, 2106 gamma); 2107 } else { 2108 batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache()); 2109 } 2110 BitmapTextBatch::Geometry& geometry = batch->geometry(); 2111 geometry.fBlob = SkRef(cacheBlob); 2112 geometry.fRun = run; 2113 geometry.fSubRun = subRun; 2114 geometry.fColor = subRunColor; 2115 geometry.fTransX = transX; 2116 geometry.fTransY = transY; 2117 batch->init(); 2118 2119 return batch; 2120 } 2121 2122 inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, 2123 BitmapTextBlob* cacheBlob, int run, GrColor color, 2124 SkScalar transX, SkScalar transY, const SkPaint& skPaint) { 2125 for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) { 2126 const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun]; 2127 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; 2128 if (0 == glyphCount) { 2129 continue; 2130 } 2131 2132 SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run, 2133 subRun, color, transX, transY, 2134 skPaint)); 2135 target->drawBatch(pipelineBuilder, batch); 2136 } 2137 } 2138 2139 inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt, 2140 const SkPaint& skPaint, 2141 SkScalar transX, SkScalar transY, 2142 const SkIRect& clipBounds) { 2143 if (!cacheBlob->fBigGlyphs.count()) { 2144 return; 2145 } 2146 2147 SkMatrix pathMatrix; 2148 if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) { 2149 SkDebugf("could not invert viewmatrix\n"); 2150 return; 2151 } 2152 2153 for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) { 2154 BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i]; 2155 bigGlyph.fVx += transX; 2156 bigGlyph.fVy += transY; 2157 SkMatrix translate = cacheBlob->fViewMatrix; 2158 translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy); 2159 2160 fGpuDevice->internalDrawPath(bigGlyph.fPath, skPaint, translate, &pathMatrix, clipBounds, 2161 false); 2162 } 2163 } 2164 2165 void GrAtlasTextContext::flush(GrDrawTarget* target, 2166 const SkTextBlob* blob, 2167 BitmapTextBlob* cacheBlob, 2168 GrRenderTarget* rt, 2169 const SkPaint& skPaint, 2170 const GrPaint& grPaint, 2171 SkDrawFilter* drawFilter, 2172 const GrClip& clip, 2173 const SkMatrix& viewMatrix, 2174 const SkIRect& clipBounds, 2175 SkScalar x, SkScalar y, 2176 SkScalar transX, SkScalar transY) { 2177 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush 2178 // it as paths 2179 GrPipelineBuilder pipelineBuilder; 2180 pipelineBuilder.setFromPaint(grPaint, rt, clip); 2181 2182 GrColor color = grPaint.getColor(); 2183 2184 SkTextBlob::RunIterator it(blob); 2185 for (int run = 0; !it.done(); it.next(), run++) { 2186 if (cacheBlob->fRuns[run].fDrawAsPaths) { 2187 this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y); 2188 continue; 2189 } 2190 cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY); 2191 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint); 2192 } 2193 2194 // Now flush big glyphs 2195 this->flushBigGlyphs(cacheBlob, rt, skPaint, transX, transY, clipBounds); 2196 } 2197 2198 void GrAtlasTextContext::flush(GrDrawTarget* target, 2199 BitmapTextBlob* cacheBlob, 2200 GrRenderTarget* rt, 2201 const SkPaint& skPaint, 2202 const GrPaint& grPaint, 2203 const GrClip& clip, 2204 const SkIRect& clipBounds) { 2205 GrPipelineBuilder pipelineBuilder; 2206 pipelineBuilder.setFromPaint(grPaint, rt, clip); 2207 2208 GrColor color = grPaint.getColor(); 2209 for (int run = 0; run < cacheBlob->fRunCount; run++) { 2210 this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint); 2211 } 2212 2213 // Now flush big glyphs 2214 this->flushBigGlyphs(cacheBlob, rt, skPaint, 0, 0, clipBounds); 2215 } 2216 2217 /////////////////////////////////////////////////////////////////////////////////////////////////// 2218 2219 #ifdef GR_TEST_UTILS 2220 2221 BATCH_TEST_DEFINE(TextBlobBatch) { 2222 static uint32_t gContextID = SK_InvalidGenID; 2223 static GrAtlasTextContext* gTextContext = NULL; 2224 static SkDeviceProperties gDeviceProperties(SkDeviceProperties::kLegacyLCD_InitType); 2225 2226 if (context->uniqueID() != gContextID) { 2227 gContextID = context->uniqueID(); 2228 SkDELETE(gTextContext); 2229 // We don't yet test the fall back to paths in the GrTextContext base class. This is mostly 2230 // because we don't really want to have a gpu device here. 2231 // We enable distance fields by twiddling a knob on the paint 2232 gTextContext = GrAtlasTextContext::Create(context, NULL, gDeviceProperties, false); 2233 } 2234 2235 // create dummy render target 2236 GrSurfaceDesc desc; 2237 desc.fFlags = kRenderTarget_GrSurfaceFlag; 2238 desc.fWidth = 1024; 2239 desc.fHeight = 1024; 2240 desc.fConfig = kRGBA_8888_GrPixelConfig; 2241 desc.fSampleCnt = 0; 2242 SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0)); 2243 SkASSERT(texture); 2244 SkASSERT(NULL != texture->asRenderTarget()); 2245 GrRenderTarget* rt = texture->asRenderTarget(); 2246 2247 // Setup dummy SkPaint / GrPaint 2248 GrColor color = GrRandomColor(random); 2249 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); 2250 SkPaint skPaint; 2251 skPaint.setDistanceFieldTextTEMP(random->nextBool()); 2252 skPaint.setColor(color); 2253 skPaint.setLCDRenderText(random->nextBool()); 2254 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool()); 2255 skPaint.setSubpixelText(random->nextBool()); 2256 2257 GrPaint grPaint; 2258 if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) { 2259 SkFAIL("couldn't convert paint\n"); 2260 } 2261 2262 const char* text = "The quick brown fox jumps over the lazy dog."; 2263 int textLen = (int)strlen(text); 2264 2265 // Setup clip 2266 GrClip clip; 2267 SkIRect noClip = SkIRect::MakeLargest(); 2268 2269 // right now we don't handle textblobs, nor do we handle drawPosText. Since we only 2270 // intend to test the batch with this unit test, that is okay. 2271 SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob( 2272 gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text, 2273 static_cast<size_t>(textLen), 0, 0, noClip)); 2274 2275 SkScalar transX = static_cast<SkScalar>(random->nextU()); 2276 SkScalar transY = static_cast<SkScalar>(random->nextU()); 2277 const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0]; 2278 return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint); 2279 } 2280 2281 #endif 2282