1 /* 2 * Copyright 2013 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 "GrDistanceFieldTextContext.h" 9 #include "GrAtlas.h" 10 #include "SkColorFilter.h" 11 #include "GrDrawTarget.h" 12 #include "GrDrawTargetCaps.h" 13 #include "GrFontScaler.h" 14 #include "SkGlyphCache.h" 15 #include "GrGpu.h" 16 #include "GrIndexBuffer.h" 17 #include "GrStrokeInfo.h" 18 #include "GrTextStrike.h" 19 #include "GrTextStrike_impl.h" 20 #include "SkDistanceFieldGen.h" 21 #include "SkDraw.h" 22 #include "SkGpuDevice.h" 23 #include "SkPath.h" 24 #include "SkRTConf.h" 25 #include "SkStrokeRec.h" 26 #include "effects/GrDistanceFieldTextureEffect.h" 27 28 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, 29 "Dump the contents of the font cache before every purge."); 30 31 static const int kSmallDFFontSize = 32; 32 static const int kSmallDFFontLimit = 32; 33 static const int kMediumDFFontSize = 64; 34 static const int kMediumDFFontLimit = 64; 35 static const int kLargeDFFontSize = 128; 36 37 namespace { 38 // position + texture coord 39 extern const GrVertexAttrib gTextVertexAttribs[] = { 40 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 41 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding} 42 }; 43 44 static const size_t kTextVASize = 2 * sizeof(SkPoint); 45 46 // position + color + texture coord 47 extern const GrVertexAttrib gTextVertexWithColorAttribs[] = { 48 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 49 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding}, 50 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding} 51 }; 52 53 static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor); 54 55 }; 56 57 GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, 58 const SkDeviceProperties& properties, 59 bool enable) 60 : GrTextContext(context, properties) { 61 #if SK_FORCE_DISTANCEFIELD_FONTS 62 fEnableDFRendering = true; 63 #else 64 fEnableDFRendering = enable; 65 #endif 66 fStrike = NULL; 67 fGammaTexture = NULL; 68 69 fCurrTexture = NULL; 70 fCurrVertex = 0; 71 fEffectTextureUniqueID = SK_InvalidUniqueID; 72 fEffectColor = GrColor_ILLEGAL; 73 fEffectFlags = 0; 74 75 fVertices = NULL; 76 fMaxVertices = 0; 77 78 fVertexBounds.setLargestInverted(); 79 } 80 81 GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { 82 this->flushGlyphs(); 83 SkSafeSetNull(fGammaTexture); 84 } 85 86 bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) { 87 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) { 88 return false; 89 } 90 91 // rasterizers and mask filters modify alpha, which doesn't 92 // translate well to distance 93 if (paint.getRasterizer() || paint.getMaskFilter() || 94 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) { 95 return false; 96 } 97 98 // TODO: add some stroking support 99 if (paint.getStyle() != SkPaint::kFill_Style) { 100 return false; 101 } 102 103 // TODO: choose an appropriate maximum scale for distance fields and 104 // enable perspective 105 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) { 106 return false; 107 } 108 109 // distance fields cannot represent color fonts 110 SkScalerContext::Rec rec; 111 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); 112 return rec.getFormat() != SkMask::kARGB32_Format; 113 } 114 115 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { 116 unsigned r = SkColorGetR(c); 117 unsigned g = SkColorGetG(c); 118 unsigned b = SkColorGetB(c); 119 return GrColorPackRGBA(r, g, b, 0xff); 120 } 121 122 void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) { 123 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); 124 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); 125 126 uint32_t textureUniqueID = fCurrTexture->getUniqueID(); 127 const SkMatrix& ctm = fContext->getMatrix(); 128 129 // set up any flags 130 uint32_t flags = 0; 131 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; 132 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0; 133 flags |= fUseLCDText && ctm.rectStaysRect() ? 134 kRectToRect_DistanceFieldEffectFlag : 0; 135 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.fPixelGeometry); 136 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0; 137 138 // see if we need to create a new effect 139 if (textureUniqueID != fEffectTextureUniqueID || 140 filteredColor != fEffectColor || 141 flags != fEffectFlags) { 142 if (fUseLCDText) { 143 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor); 144 fCachedGeometryProcessor.reset( 145 GrDistanceFieldLCDTextureEffect::Create(fCurrTexture, 146 params, 147 fGammaTexture, 148 gammaParams, 149 colorNoPreMul, 150 flags)); 151 } else { 152 #ifdef SK_GAMMA_APPLY_TO_A8 153 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.getGamma(), 154 filteredColor); 155 fCachedGeometryProcessor.reset( 156 GrDistanceFieldTextureEffect::Create(fCurrTexture, 157 params, 158 fGammaTexture, 159 gammaParams, 160 lum/255.f, 161 flags)); 162 #else 163 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture, 164 params, flags)); 165 #endif 166 } 167 fEffectTextureUniqueID = textureUniqueID; 168 fEffectColor = filteredColor; 169 fEffectFlags = flags; 170 } 171 172 } 173 174 void GrDistanceFieldTextContext::flushGlyphs() { 175 if (NULL == fDrawTarget) { 176 return; 177 } 178 179 GrDrawState* drawState = fDrawTarget->drawState(); 180 GrDrawState::AutoRestoreEffects are(drawState); 181 182 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget()); 183 184 if (fCurrVertex > 0) { 185 // setup our sampler state for our text texture/atlas 186 SkASSERT(SkIsAlign4(fCurrVertex)); 187 188 // get our current color 189 SkColor filteredColor; 190 SkColorFilter* colorFilter = fSkPaint.getColorFilter(); 191 if (colorFilter) { 192 filteredColor = colorFilter->filterColor(fSkPaint.getColor()); 193 } else { 194 filteredColor = fSkPaint.getColor(); 195 } 196 this->setupCoverageEffect(filteredColor); 197 198 // Effects could be stored with one of the cache objects (atlas?) 199 drawState->setGeometryProcessor(fCachedGeometryProcessor.get()); 200 201 // Set draw state 202 if (fUseLCDText) { 203 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor); 204 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || 205 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || 206 fPaint.numColorStages()) { 207 GrPrintf("LCD Text will not draw correctly.\n"); 208 } 209 SkASSERT(!drawState->hasColorVertexAttribute()); 210 // We don't use the GrPaint's color in this case because it's been premultiplied by 211 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by 212 // the mask texture color. The end result is that we get 213 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor 214 int a = SkColorGetA(fSkPaint.getColor()); 215 // paintAlpha 216 drawState->setColor(SkColorSetARGB(a, a, a, a)); 217 // paintColor 218 drawState->setBlendConstant(colorNoPreMul); 219 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 220 } else { 221 // set back to normal in case we took LCD path previously. 222 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); 223 // We're using per-vertex color. 224 SkASSERT(drawState->hasColorVertexAttribute()); 225 } 226 int nGlyphs = fCurrVertex / 4; 227 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 228 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 229 nGlyphs, 230 4, 6, &fVertexBounds); 231 fDrawTarget->resetVertexSource(); 232 fVertices = NULL; 233 fMaxVertices = 0; 234 fCurrVertex = 0; 235 SkSafeSetNull(fCurrTexture); 236 fVertexBounds.setLargestInverted(); 237 } 238 } 239 240 void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 241 SkFixed vx, SkFixed vy, 242 GrFontScaler* scaler) { 243 if (NULL == fDrawTarget) { 244 return; 245 } 246 247 if (NULL == fStrike) { 248 fStrike = fContext->getFontCache()->getStrike(scaler, true); 249 } 250 251 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 252 if (NULL == glyph || glyph->fBounds.isEmpty()) { 253 return; 254 } 255 256 SkScalar sx = SkFixedToScalar(vx); 257 SkScalar sy = SkFixedToScalar(vy); 258 /* 259 // not valid, need to find a different solution for this 260 vx += SkIntToFixed(glyph->fBounds.fLeft); 261 vy += SkIntToFixed(glyph->fBounds.fTop); 262 263 // keep them as ints until we've done the clip-test 264 GrFixed width = glyph->fBounds.width(); 265 GrFixed height = glyph->fBounds.height(); 266 267 // check if we clipped out 268 if (true || NULL == glyph->fPlot) { 269 int x = vx >> 16; 270 int y = vy >> 16; 271 if (fClipRect.quickReject(x, y, x + width, y + height)) { 272 // SkCLZ(3); // so we can set a break-point in the debugger 273 return; 274 } 275 } 276 */ 277 if (NULL == glyph->fPlot) { 278 if (!fStrike->glyphTooLargeForAtlas(glyph)) { 279 if (fStrike->addGlyphToAtlas(glyph, scaler)) { 280 goto HAS_ATLAS; 281 } 282 283 // try to clear out an unused plot before we flush 284 if (fContext->getFontCache()->freeUnusedPlot(fStrike) && 285 fStrike->addGlyphToAtlas(glyph, scaler)) { 286 goto HAS_ATLAS; 287 } 288 289 if (c_DumpFontCache) { 290 #ifdef SK_DEVELOPER 291 fContext->getFontCache()->dump(); 292 #endif 293 } 294 295 // before we purge the cache, we must flush any accumulated draws 296 this->flushGlyphs(); 297 fContext->flush(); 298 299 // we should have an unused plot now 300 if (fContext->getFontCache()->freeUnusedPlot(fStrike) && 301 fStrike->addGlyphToAtlas(glyph, scaler)) { 302 goto HAS_ATLAS; 303 } 304 } 305 306 if (NULL == glyph->fPath) { 307 SkPath* path = SkNEW(SkPath); 308 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 309 // flag the glyph as being dead? 310 delete path; 311 return; 312 } 313 glyph->fPath = path; 314 } 315 316 GrContext::AutoMatrix am; 317 SkMatrix ctm; 318 ctm.setScale(fTextRatio, fTextRatio); 319 ctm.postTranslate(sx, sy); 320 GrPaint tmpPaint(fPaint); 321 am.setPreConcat(fContext, ctm, &tmpPaint); 322 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); 323 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo); 324 return; 325 } 326 327 HAS_ATLAS: 328 SkASSERT(glyph->fPlot); 329 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); 330 glyph->fPlot->setDrawToken(drawToken); 331 332 GrTexture* texture = glyph->fPlot->texture(); 333 SkASSERT(texture); 334 335 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 336 this->flushGlyphs(); 337 fCurrTexture = texture; 338 fCurrTexture->ref(); 339 } 340 341 bool useColorVerts = !fUseLCDText; 342 343 if (NULL == fVertices) { 344 // If we need to reserve vertices allow the draw target to suggest 345 // a number of verts to reserve and whether to perform a flush. 346 fMaxVertices = kMinRequestedVerts; 347 if (useColorVerts) { 348 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>( 349 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), 350 kTextVAColorSize); 351 } else { 352 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 353 SK_ARRAY_COUNT(gTextVertexAttribs), 354 kTextVASize); 355 } 356 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); 357 if (flush) { 358 this->flushGlyphs(); 359 fContext->flush(); 360 if (useColorVerts) { 361 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>( 362 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), 363 kTextVAColorSize); 364 } else { 365 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 366 SK_ARRAY_COUNT(gTextVertexAttribs), 367 kTextVASize); 368 } 369 } 370 fMaxVertices = kDefaultRequestedVerts; 371 // ignore return, no point in flushing again. 372 fDrawTarget->geometryHints(&fMaxVertices, NULL); 373 374 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 375 if (fMaxVertices < kMinRequestedVerts) { 376 fMaxVertices = kDefaultRequestedVerts; 377 } else if (fMaxVertices > maxQuadVertices) { 378 // don't exceed the limit of the index buffer 379 fMaxVertices = maxQuadVertices; 380 } 381 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices, 382 0, 383 &fVertices, 384 NULL); 385 GrAlwaysAssert(success); 386 } 387 388 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); 389 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); 390 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset); 391 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset); 392 393 SkScalar scale = fTextRatio; 394 dx *= scale; 395 dy *= scale; 396 sx += dx; 397 sy += dy; 398 width *= scale; 399 height *= scale; 400 401 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset); 402 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset); 403 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset); 404 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset); 405 406 SkRect r; 407 r.fLeft = sx; 408 r.fTop = sy; 409 r.fRight = sx + width; 410 r.fBottom = sy + height; 411 412 fVertexBounds.growToInclude(r); 413 414 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint)) 415 : (2 * sizeof(SkPoint) + sizeof(GrColor)); 416 417 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride()); 418 419 SkPoint* positions = reinterpret_cast<SkPoint*>( 420 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex); 421 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize); 422 423 // The texture coords are last in both the with and without color vertex layouts. 424 SkPoint* textureCoords = reinterpret_cast<SkPoint*>( 425 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint)); 426 textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), 427 SkFixedToFloat(texture->normalizeFixedY(ty)), 428 SkFixedToFloat(texture->normalizeFixedX(tx + tw)), 429 SkFixedToFloat(texture->normalizeFixedY(ty + th)), 430 vertSize); 431 if (useColorVerts) { 432 if (0xFF == GrColorUnpackA(fPaint.getColor())) { 433 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true); 434 } 435 // color comes after position. 436 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1); 437 for (int i = 0; i < 4; ++i) { 438 *colors = fPaint.getColor(); 439 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize); 440 } 441 } 442 443 fCurrVertex += 4; 444 } 445 446 inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) { 447 GrTextContext::init(paint, skPaint); 448 449 fStrike = NULL; 450 451 const SkMatrix& ctm = fContext->getMatrix(); 452 453 // getMaxScale doesn't support perspective, so neither do we at the moment 454 SkASSERT(!ctm.hasPerspective()); 455 SkScalar maxScale = ctm.getMaxScale(); 456 SkScalar textSize = fSkPaint.getTextSize(); 457 SkScalar scaledTextSize = textSize; 458 // if we have non-unity scale, we need to choose our base text size 459 // based on the SkPaint's text size multiplied by the max scale factor 460 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? 461 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { 462 scaledTextSize *= maxScale; 463 } 464 465 fCurrVertex = 0; 466 467 fVertices = NULL; 468 469 if (scaledTextSize <= kSmallDFFontLimit) { 470 fTextRatio = textSize / kSmallDFFontSize; 471 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize)); 472 } else if (scaledTextSize <= kMediumDFFontLimit) { 473 fTextRatio = textSize / kMediumDFFontSize; 474 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize)); 475 } else { 476 fTextRatio = textSize / kLargeDFFontSize; 477 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize)); 478 } 479 480 fUseLCDText = fSkPaint.isLCDRenderText(); 481 482 fSkPaint.setLCDRenderText(false); 483 fSkPaint.setAutohinted(false); 484 fSkPaint.setHinting(SkPaint::kNormal_Hinting); 485 fSkPaint.setSubpixelText(true); 486 487 } 488 489 inline void GrDistanceFieldTextContext::finish() { 490 this->flushGlyphs(); 491 492 GrTextContext::finish(); 493 } 494 495 static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache, 496 const SkDeviceProperties& deviceProperties, 497 GrTexture** gammaTexture) { 498 if (NULL == *gammaTexture) { 499 int width, height; 500 size_t size; 501 502 #ifdef SK_GAMMA_CONTRAST 503 SkScalar contrast = SK_GAMMA_CONTRAST; 504 #else 505 SkScalar contrast = 0.5f; 506 #endif 507 SkScalar paintGamma = deviceProperties.getGamma(); 508 SkScalar deviceGamma = deviceProperties.getGamma(); 509 510 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, 511 &width, &height); 512 513 SkAutoTArray<uint8_t> data((int)size); 514 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); 515 516 // TODO: Update this to use the cache rather than directly creating a texture. 517 GrTextureDesc desc; 518 desc.fFlags = kDynamicUpdate_GrTextureFlagBit; 519 desc.fWidth = width; 520 desc.fHeight = height; 521 desc.fConfig = kAlpha_8_GrPixelConfig; 522 523 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0); 524 if (NULL == *gammaTexture) { 525 return; 526 } 527 528 context->writeTexturePixels(*gammaTexture, 529 0, 0, width, height, 530 (*gammaTexture)->config(), data.get(), 0, 531 GrContext::kDontFlush_PixelOpsFlag); 532 } 533 } 534 535 void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint, 536 const char text[], size_t byteLength, 537 SkScalar x, SkScalar y) { 538 SkASSERT(byteLength == 0 || text != NULL); 539 540 // nothing to draw or can't draw 541 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/ 542 || fSkPaint.getRasterizer()) { 543 return; 544 } 545 546 this->init(paint, skPaint); 547 548 SkScalar sizeRatio = fTextRatio; 549 550 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); 551 552 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL); 553 SkGlyphCache* cache = autoCache.getCache(); 554 GrFontScaler* fontScaler = GetGrFontScaler(cache); 555 556 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture); 557 558 // need to measure first 559 // TODO - generate positions and pre-load cache as well? 560 const char* stop = text + byteLength; 561 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { 562 SkFixed stopX = 0; 563 SkFixed stopY = 0; 564 565 const char* textPtr = text; 566 while (textPtr < stop) { 567 // don't need x, y here, since all subpixel variants will have the 568 // same advance 569 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); 570 571 stopX += glyph.fAdvanceX; 572 stopY += glyph.fAdvanceY; 573 } 574 SkASSERT(textPtr == stop); 575 576 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio; 577 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio; 578 579 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { 580 alignX = SkScalarHalf(alignX); 581 alignY = SkScalarHalf(alignY); 582 } 583 584 x -= alignX; 585 y -= alignY; 586 } 587 588 SkFixed fx = SkScalarToFixed(x); 589 SkFixed fy = SkScalarToFixed(y); 590 SkFixed fixedScale = SkScalarToFixed(sizeRatio); 591 while (text < stop) { 592 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 593 594 if (glyph.fWidth) { 595 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 596 glyph.getSubXFixed(), 597 glyph.getSubYFixed()), 598 fx, 599 fy, 600 fontScaler); 601 } 602 603 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale); 604 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale); 605 } 606 607 this->finish(); 608 } 609 610 void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint, 611 const char text[], size_t byteLength, 612 const SkScalar pos[], SkScalar constY, 613 int scalarsPerPosition) { 614 615 SkASSERT(byteLength == 0 || text != NULL); 616 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 617 618 // nothing to draw 619 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) { 620 return; 621 } 622 623 this->init(paint, skPaint); 624 625 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); 626 627 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL); 628 SkGlyphCache* cache = autoCache.getCache(); 629 GrFontScaler* fontScaler = GetGrFontScaler(cache); 630 631 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture); 632 633 const char* stop = text + byteLength; 634 635 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { 636 while (text < stop) { 637 // the last 2 parameters are ignored 638 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 639 640 if (glyph.fWidth) { 641 SkScalar x = pos[0]; 642 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; 643 644 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 645 glyph.getSubXFixed(), 646 glyph.getSubYFixed()), 647 SkScalarToFixed(x), 648 SkScalarToFixed(y), 649 fontScaler); 650 } 651 pos += scalarsPerPosition; 652 } 653 } else { 654 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0; 655 while (text < stop) { 656 // the last 2 parameters are ignored 657 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 658 659 if (glyph.fWidth) { 660 SkScalar x = pos[0]; 661 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; 662 663 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 664 glyph.getSubXFixed(), 665 glyph.getSubYFixed()), 666 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift), 667 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift), 668 fontScaler); 669 } 670 pos += scalarsPerPosition; 671 } 672 } 673 674 this->finish(); 675 } 676