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 8 #include "GrTextUtils.h" 9 #include "GrAtlasGlyphCache.h" 10 #include "GrAtlasTextBlob.h" 11 #include "GrBlurUtils.h" 12 #include "GrCaps.h" 13 #include "GrContext.h" 14 #include "GrRenderTargetContext.h" 15 #include "GrSurfaceContextPriv.h" 16 #include "SkDistanceFieldGen.h" 17 #include "SkDrawFilter.h" 18 #include "SkDrawProcs.h" 19 #include "SkFindAndPlaceGlyph.h" 20 #include "SkGlyphCache.h" 21 #include "SkGr.h" 22 #include "SkPaint.h" 23 #include "SkRect.h" 24 #include "SkTextBlobRunIterator.h" 25 #include "SkTextMapStateProc.h" 26 #include "SkTextToPathIter.h" 27 28 namespace { 29 static const int kMinDFFontSize = 18; 30 static const int kSmallDFFontSize = 32; 31 static const int kSmallDFFontLimit = 32; 32 static const int kMediumDFFontSize = 72; 33 static const int kMediumDFFontLimit = 72; 34 static const int kLargeDFFontSize = 162; 35 #ifdef SK_BUILD_FOR_ANDROID 36 static const int kLargeDFFontLimit = 384; 37 #else 38 static const int kLargeDFFontLimit = 2 * kLargeDFFontSize; 39 #endif 40 }; 41 42 bool GrTextUtils::Paint::toGrPaint(GrMaskFormat maskFormat, GrRenderTargetContext* rtc, 43 const SkMatrix& viewMatrix, GrPaint* grPaint) const { 44 // TODO: this is the last use of GrSurfaceContextPriv 45 GrContext* context = rtc->surfPriv().getContext(); 46 if (kARGB_GrMaskFormat == maskFormat) { 47 return SkPaintToGrPaintWithPrimitiveColor(context, rtc, this->skPaint(), grPaint); 48 } else { 49 return SkPaintToGrPaint(context, rtc, this->skPaint(), viewMatrix, grPaint); 50 } 51 } 52 53 void GrTextUtils::Paint::initFilteredColor() { 54 // This mirrors the logic in skpaint_to_grpaint_impl for handling paint colors 55 if (fDstColorSpace) { 56 GrColor4f filteredColor = SkColorToUnpremulGrColor4f(fPaint->getColor(), fDstColorSpace, 57 fColorXformFromSRGB); 58 if (fPaint->getColorFilter()) { 59 filteredColor = GrColor4f::FromSkColor4f( 60 fPaint->getColorFilter()->filterColor4f(filteredColor.toSkColor4f())); 61 } 62 fFilteredPremulColor = filteredColor.premul().toGrColor(); 63 } else { 64 SkColor filteredSkColor = fPaint->getColor(); 65 if (fPaint->getColorFilter()) { 66 filteredSkColor = fPaint->getColorFilter()->filterColor(filteredSkColor); 67 } 68 fFilteredPremulColor = SkColorToPremulGrColor(filteredSkColor); 69 } 70 } 71 72 bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) { 73 if (!fModifiedPaint.isValid()) { 74 fModifiedPaint.init(fOriginalPaint->skPaint()); 75 fPaint = fModifiedPaint.get(); 76 } else if (fFilter) { 77 // We have to reset before applying the run because the filter could have arbitrary 78 // changed the paint. 79 *fModifiedPaint.get() = fOriginalPaint->skPaint(); 80 } 81 run.applyFontToPaint(fModifiedPaint.get()); 82 83 if (fFilter) { 84 if (!fFilter->filter(fModifiedPaint.get(), SkDrawFilter::kText_Type)) { 85 // A false return from filter() means we should abort the current draw. 86 return false; 87 } 88 // The draw filter could have changed either the paint color or color filter. 89 this->initFilteredColor(); 90 } 91 fModifiedPaint.get()->setFlags(FilterTextFlags(fProps, *fModifiedPaint.get())); 92 return true; 93 } 94 95 void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache, 96 const SkSurfaceProps& props, const GrTextUtils::Paint& paint, 97 uint32_t scalerContextFlags, const SkMatrix& viewMatrix, 98 const char text[], size_t byteLength, SkScalar x, SkScalar y) { 99 SkASSERT(byteLength == 0 || text != nullptr); 100 101 // nothing to draw 102 if (text == nullptr || byteLength == 0) { 103 return; 104 } 105 106 // Ensure the blob is set for bitmaptext 107 blob->setHasBitmap(); 108 109 GrAtlasTextStrike* currStrike = nullptr; 110 111 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix); 112 SkFindAndPlaceGlyph::ProcessText( 113 paint.skPaint().getTextEncoding(), text, byteLength, 114 {x, y}, viewMatrix, paint.skPaint().getTextAlign(), 115 cache, 116 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { 117 position += rounding; 118 BmpAppendGlyph( 119 blob, runIndex, fontCache, &currStrike, glyph, 120 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), 121 paint.filteredPremulColor(), cache); 122 } 123 ); 124 125 SkGlyphCache::AttachCache(cache); 126 } 127 128 void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache, 129 const SkSurfaceProps& props, const GrTextUtils::Paint& paint, 130 uint32_t scalerContextFlags, const SkMatrix& viewMatrix, 131 const char text[], size_t byteLength, const SkScalar pos[], 132 int scalarsPerPosition, const SkPoint& offset) { 133 SkASSERT(byteLength == 0 || text != nullptr); 134 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 135 136 // nothing to draw 137 if (text == nullptr || byteLength == 0) { 138 return; 139 } 140 141 // Ensure the blob is set for bitmaptext 142 blob->setHasBitmap(); 143 144 GrAtlasTextStrike* currStrike = nullptr; 145 146 SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix); 147 148 SkFindAndPlaceGlyph::ProcessPosText( 149 paint.skPaint().getTextEncoding(), text, byteLength, 150 offset, viewMatrix, pos, scalarsPerPosition, 151 paint.skPaint().getTextAlign(), cache, 152 [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { 153 position += rounding; 154 BmpAppendGlyph( 155 blob, runIndex, fontCache, &currStrike, glyph, 156 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), 157 paint.filteredPremulColor(), cache); 158 } 159 ); 160 161 SkGlyphCache::AttachCache(cache); 162 } 163 164 void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex, 165 GrAtlasGlyphCache* fontCache, 166 GrAtlasTextStrike** strike, const SkGlyph& skGlyph, 167 int vx, int vy, GrColor color, SkGlyphCache* cache) { 168 if (!*strike) { 169 *strike = fontCache->getStrike(cache); 170 } 171 172 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), 173 skGlyph.getSubXFixed(), 174 skGlyph.getSubYFixed(), 175 GrGlyph::kCoverage_MaskStyle); 176 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache); 177 if (!glyph) { 178 return; 179 } 180 181 int x = vx + glyph->fBounds.fLeft; 182 int y = vy + glyph->fBounds.fTop; 183 184 // keep them as ints until we've done the clip-test 185 int width = glyph->fBounds.width(); 186 int height = glyph->fBounds.height(); 187 188 SkRect r; 189 r.fLeft = SkIntToScalar(x); 190 r.fTop = SkIntToScalar(y); 191 r.fRight = r.fLeft + SkIntToScalar(width); 192 r.fBottom = r.fTop + SkIntToScalar(height); 193 194 blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph, 195 SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, true); 196 } 197 198 bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix, 199 const SkSurfaceProps& props, const GrShaderCaps& caps) { 200 if (!viewMatrix.hasPerspective()) { 201 SkScalar maxScale = viewMatrix.getMaxScale(); 202 SkScalar scaledTextSize = maxScale * skPaint.getTextSize(); 203 // Hinted text looks far better at small resolutions 204 // Scaling up beyond 2x yields undesireable artifacts 205 if (scaledTextSize < kMinDFFontSize || 206 scaledTextSize > kLargeDFFontLimit) { 207 return false; 208 } 209 210 bool useDFT = props.isUseDeviceIndependentFonts(); 211 #if SK_FORCE_DISTANCE_FIELD_TEXT 212 useDFT = true; 213 #endif 214 215 if (!useDFT && scaledTextSize < kLargeDFFontSize) { 216 return false; 217 } 218 } 219 220 // rasterizers and mask filters modify alpha, which doesn't 221 // translate well to distance 222 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) { 223 return false; 224 } 225 226 // TODO: add some stroking support 227 if (skPaint.getStyle() != SkPaint::kFill_Style) { 228 return false; 229 } 230 231 return true; 232 } 233 234 void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob, 235 SkPaint* skPaint, 236 SkScalar* textRatio, 237 const SkMatrix& viewMatrix) { 238 SkScalar textSize = skPaint->getTextSize(); 239 SkScalar scaledTextSize = textSize; 240 241 if (viewMatrix.hasPerspective()) { 242 // for perspective, we simply force to the medium size 243 // TODO: compute a size based on approximate screen area 244 scaledTextSize = kMediumDFFontLimit; 245 } else { 246 SkScalar maxScale = viewMatrix.getMaxScale(); 247 // if we have non-unity scale, we need to choose our base text size 248 // based on the SkPaint's text size multiplied by the max scale factor 249 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? 250 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { 251 scaledTextSize *= maxScale; 252 } 253 } 254 255 // We have three sizes of distance field text, and within each size 'bucket' there is a floor 256 // and ceiling. A scale outside of this range would require regenerating the distance fields 257 SkScalar dfMaskScaleFloor; 258 SkScalar dfMaskScaleCeil; 259 if (scaledTextSize <= kSmallDFFontLimit) { 260 dfMaskScaleFloor = kMinDFFontSize; 261 dfMaskScaleCeil = kSmallDFFontLimit; 262 *textRatio = textSize / kSmallDFFontSize; 263 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize)); 264 } else if (scaledTextSize <= kMediumDFFontLimit) { 265 dfMaskScaleFloor = kSmallDFFontLimit; 266 dfMaskScaleCeil = kMediumDFFontLimit; 267 *textRatio = textSize / kMediumDFFontSize; 268 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize)); 269 } else { 270 dfMaskScaleFloor = kMediumDFFontLimit; 271 dfMaskScaleCeil = kLargeDFFontLimit; 272 *textRatio = textSize / kLargeDFFontSize; 273 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize)); 274 } 275 276 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and 277 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale 278 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can 279 // tolerate before we'd have to move to a large mip size. When we actually test these values 280 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test 281 // against these values to decide if we can reuse or not(ie, will a given scale change our mip 282 // level) 283 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil); 284 blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize); 285 286 skPaint->setAntiAlias(true); 287 skPaint->setLCDRenderText(false); 288 skPaint->setAutohinted(false); 289 skPaint->setHinting(SkPaint::kNormal_Hinting); 290 skPaint->setSubpixelText(true); 291 } 292 293 void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex, 294 GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, 295 const GrTextUtils::Paint& paint, uint32_t scalerContextFlags, 296 const SkMatrix& viewMatrix, 297 const char text[], size_t byteLength, 298 SkScalar x, SkScalar y) { 299 SkASSERT(byteLength == 0 || text != nullptr); 300 301 // nothing to draw 302 if (text == nullptr || byteLength == 0) { 303 return; 304 } 305 306 const SkPaint& skPaint = paint.skPaint(); 307 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), 308 skPaint.isDevKernText(), 309 true); 310 SkAutoDescriptor desc; 311 SkScalerContextEffects effects; 312 // We apply the fake-gamma by altering the distance in the shader, so we ignore the 313 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). 314 skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags, 315 nullptr); 316 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), effects, 317 desc.getDesc()); 318 319 SkTArray<SkScalar> positions; 320 321 const char* textPtr = text; 322 SkScalar stopX = 0; 323 SkScalar stopY = 0; 324 SkScalar origin = 0; 325 switch (skPaint.getTextAlign()) { 326 case SkPaint::kRight_Align: origin = SK_Scalar1; break; 327 case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break; 328 case SkPaint::kLeft_Align: origin = 0; break; 329 } 330 331 SkAutoKern autokern; 332 const char* stop = text + byteLength; 333 while (textPtr < stop) { 334 // don't need x, y here, since all subpixel variants will have the 335 // same advance 336 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr); 337 338 SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph); 339 positions.push_back(stopX + origin * width); 340 341 SkScalar height = SkFloatToScalar(glyph.fAdvanceY); 342 positions.push_back(stopY + origin * height); 343 344 stopX += width; 345 stopY += height; 346 } 347 SkASSERT(textPtr == stop); 348 349 SkGlyphCache::AttachCache(origPaintCache); 350 351 // now adjust starting point depending on alignment 352 SkScalar alignX = stopX; 353 SkScalar alignY = stopY; 354 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { 355 alignX = SkScalarHalf(alignX); 356 alignY = SkScalarHalf(alignY); 357 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) { 358 alignX = 0; 359 alignY = 0; 360 } 361 x -= alignX; 362 y -= alignY; 363 SkPoint offset = SkPoint::Make(x, y); 364 365 DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text, 366 byteLength, positions.begin(), 2, offset); 367 } 368 369 void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache, 370 const SkSurfaceProps& props, const GrTextUtils::Paint& paint, 371 uint32_t scalerContextFlags, const SkMatrix& viewMatrix, 372 const char text[], size_t byteLength, const SkScalar pos[], 373 int scalarsPerPosition, const SkPoint& offset) { 374 SkASSERT(byteLength == 0 || text != nullptr); 375 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 376 377 // nothing to draw 378 if (text == nullptr || byteLength == 0) { 379 return; 380 } 381 382 SkTDArray<char> fallbackTxt; 383 SkTDArray<SkScalar> fallbackPos; 384 385 // Setup distance field paint and text ratio 386 SkScalar textRatio; 387 SkPaint dfPaint(paint); 388 GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix); 389 blob->setHasDistanceField(); 390 blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(), 391 paint.skPaint().isAntiAlias()); 392 393 GrAtlasTextStrike* currStrike = nullptr; 394 395 // We apply the fake-gamma by altering the distance in the shader, so we ignore the 396 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). 397 SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags, 398 dfPaint, nullptr); 399 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), 400 dfPaint.isDevKernText(), 401 true); 402 403 const char* stop = text + byteLength; 404 405 if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) { 406 while (text < stop) { 407 const char* lastText = text; 408 // the last 2 parameters are ignored 409 const SkGlyph& glyph = glyphCacheProc(cache, &text); 410 411 if (glyph.fWidth) { 412 SkScalar x = offset.x() + pos[0]; 413 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); 414 415 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y, 416 paint.filteredPremulColor(), cache, textRatio, viewMatrix)) { 417 // couldn't append, send to fallback 418 fallbackTxt.append(SkToInt(text-lastText), lastText); 419 *fallbackPos.append() = pos[0]; 420 if (2 == scalarsPerPosition) { 421 *fallbackPos.append() = pos[1]; 422 } 423 } 424 } 425 pos += scalarsPerPosition; 426 } 427 } else { 428 SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf 429 : SK_Scalar1; 430 while (text < stop) { 431 const char* lastText = text; 432 // the last 2 parameters are ignored 433 const SkGlyph& glyph = glyphCacheProc(cache, &text); 434 435 if (glyph.fWidth) { 436 SkScalar x = offset.x() + pos[0]; 437 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); 438 439 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio; 440 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio; 441 442 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX, 443 y - advanceY, paint.filteredPremulColor(), cache, textRatio, 444 viewMatrix)) { 445 // couldn't append, send to fallback 446 fallbackTxt.append(SkToInt(text-lastText), lastText); 447 *fallbackPos.append() = pos[0]; 448 if (2 == scalarsPerPosition) { 449 *fallbackPos.append() = pos[1]; 450 } 451 } 452 } 453 pos += scalarsPerPosition; 454 } 455 } 456 457 SkGlyphCache::AttachCache(cache); 458 if (fallbackTxt.count()) { 459 blob->initOverride(runIndex); 460 GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, 461 viewMatrix, fallbackTxt.begin(), fallbackTxt.count(), 462 fallbackPos.begin(), scalarsPerPosition, offset); 463 } 464 } 465 466 bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* cache, 467 GrAtlasTextStrike** strike, const SkGlyph& skGlyph, 468 SkScalar sx, SkScalar sy, GrColor color, 469 SkGlyphCache* glyphCache, 470 SkScalar textRatio, const SkMatrix& viewMatrix) { 471 if (!*strike) { 472 *strike = cache->getStrike(glyphCache); 473 } 474 475 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), 476 skGlyph.getSubXFixed(), 477 skGlyph.getSubYFixed(), 478 GrGlyph::kDistance_MaskStyle); 479 GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache); 480 if (!glyph) { 481 return true; 482 } 483 484 // fallback to color glyph support 485 if (kA8_GrMaskFormat != glyph->fMaskFormat) { 486 return false; 487 } 488 489 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); 490 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); 491 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset); 492 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset); 493 494 SkScalar scale = textRatio; 495 dx *= scale; 496 dy *= scale; 497 width *= scale; 498 height *= scale; 499 sx += dx; 500 sy += dy; 501 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); 502 503 blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, 504 sx - dx, sy - dy, scale, false); 505 return true; 506 } 507 508 void GrTextUtils::DrawTextAsPath(GrContext* context, GrRenderTargetContext* rtc, const GrClip& clip, 509 const SkPaint& paint, const SkMatrix& viewMatrix, 510 const char text[], size_t byteLength, SkScalar x, SkScalar y, 511 const SkIRect& clipBounds) { 512 SkTextToPathIter iter(text, byteLength, paint, true); 513 514 SkMatrix matrix; 515 matrix.setScale(iter.getPathScale(), iter.getPathScale()); 516 matrix.postTranslate(x, y); 517 518 const SkPath* iterPath; 519 SkScalar xpos, prevXPos = 0; 520 521 while (iter.next(&iterPath, &xpos)) { 522 matrix.postTranslate(xpos - prevXPos, 0); 523 if (iterPath) { 524 const SkPaint& pnt = iter.getPaint(); 525 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *iterPath, 526 pnt, viewMatrix, &matrix, clipBounds, false); 527 } 528 prevXPos = xpos; 529 } 530 } 531 532 void GrTextUtils::DrawPosTextAsPath(GrContext* context, 533 GrRenderTargetContext* rtc, 534 const SkSurfaceProps& props, 535 const GrClip& clip, 536 const SkPaint& origPaint, const SkMatrix& viewMatrix, 537 const char text[], size_t byteLength, 538 const SkScalar pos[], int scalarsPerPosition, 539 const SkPoint& offset, const SkIRect& clipBounds) { 540 // setup our std paint, in hopes of getting hits in the cache 541 SkPaint paint(origPaint); 542 SkScalar matrixScale = paint.setupForAsPaths(); 543 544 SkMatrix matrix; 545 matrix.setScale(matrixScale, matrixScale); 546 547 // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache. 548 paint.setStyle(SkPaint::kFill_Style); 549 paint.setPathEffect(nullptr); 550 551 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(), 552 paint.isDevKernText(), 553 true); 554 SkAutoGlyphCache autoCache(paint, &props, nullptr); 555 SkGlyphCache* cache = autoCache.getCache(); 556 557 const char* stop = text + byteLength; 558 SkTextAlignProc alignProc(paint.getTextAlign()); 559 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition); 560 561 // Now restore the original settings, so we "draw" with whatever style/stroking. 562 paint.setStyle(origPaint.getStyle()); 563 paint.setPathEffect(origPaint.refPathEffect()); 564 565 while (text < stop) { 566 const SkGlyph& glyph = glyphCacheProc(cache, &text); 567 if (glyph.fWidth) { 568 const SkPath* path = cache->findPath(glyph); 569 if (path) { 570 SkPoint tmsLoc; 571 tmsProc(pos, &tmsLoc); 572 SkPoint loc; 573 alignProc(tmsLoc, glyph, &loc); 574 575 matrix[SkMatrix::kMTransX] = loc.fX; 576 matrix[SkMatrix::kMTransY] = loc.fY; 577 GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, *path, paint, 578 viewMatrix, &matrix, clipBounds, false); 579 } 580 } 581 pos += scalarsPerPosition; 582 } 583 } 584 585 bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) { 586 return paint.getMaskFilter() || 587 paint.getRasterizer() || 588 paint.getPathEffect() || 589 paint.isFakeBoldText() || 590 paint.getStyle() != SkPaint::kFill_Style; 591 } 592 593 uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) { 594 uint32_t flags = paint.getFlags(); 595 596 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) { 597 return flags; 598 } 599 600 if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) { 601 flags &= ~SkPaint::kLCDRenderText_Flag; 602 flags |= SkPaint::kGenA8FromLCD_Flag; 603 } 604 605 return flags; 606 } 607