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