GrDistanceFieldTextContext.cpp revision 4362a38563a958083aca2b456aaaa9f756f6f4e1
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 "GrDrawTarget.h" 11#include "GrDrawTargetCaps.h" 12#include "GrFontScaler.h" 13#include "SkGlyphCache.h" 14#include "GrIndexBuffer.h" 15#include "GrTextStrike.h" 16#include "GrTextStrike_impl.h" 17#include "SkDraw.h" 18#include "SkGpuDevice.h" 19#include "SkPath.h" 20#include "SkRTConf.h" 21#include "SkStrokeRec.h" 22#include "effects/GrDistanceFieldTextureEffect.h" 23 24static const int kGlyphCoordsAttributeIndex = 1; 25 26static const int kBaseDFFontSize = 32; 27 28SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, 29 "Dump the contents of the font cache before every purge."); 30 31#if SK_FORCE_DISTANCEFIELD_FONTS 32static const bool kForceDistanceFieldFonts = true; 33#else 34static const bool kForceDistanceFieldFonts = false; 35#endif 36 37GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, 38 const SkDeviceProperties& properties) 39 : GrTextContext(context, properties) { 40 fStrike = NULL; 41 42 fCurrTexture = NULL; 43 fCurrVertex = 0; 44 45 fVertices = NULL; 46 fMaxVertices = 0; 47} 48 49GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { 50 this->flushGlyphs(); 51} 52 53bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) { 54 return (kForceDistanceFieldFonts || paint.isDistanceFieldTextTEMP()) && 55 !paint.getRasterizer() && !paint.getMaskFilter() && 56 paint.getStyle() == SkPaint::kFill_Style && 57 fContext->getTextTarget()->caps()->shaderDerivativeSupport() && 58 !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix()); 59} 60 61static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { 62 unsigned r = SkColorGetR(c); 63 unsigned g = SkColorGetG(c); 64 unsigned b = SkColorGetB(c); 65 return GrColorPackRGBA(r, g, b, 0xff); 66} 67 68void GrDistanceFieldTextContext::flushGlyphs() { 69 if (NULL == fDrawTarget) { 70 return; 71 } 72 73 GrDrawState* drawState = fDrawTarget->drawState(); 74 GrDrawState::AutoRestoreEffects are(drawState); 75 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget()); 76 77 if (fCurrVertex > 0) { 78 // setup our sampler state for our text texture/atlas 79 SkASSERT(GrIsALIGN4(fCurrVertex)); 80 SkASSERT(fCurrTexture); 81 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); 82 83 // This effect could be stored with one of the cache objects (atlas?) 84 drawState->addCoverageEffect( 85 GrDistanceFieldTextureEffect::Create(fCurrTexture, params, 86 fContext->getMatrix().isSimilarity()), 87 kGlyphCoordsAttributeIndex)->unref(); 88 89 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 90 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || 91 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || 92 fPaint.numColorStages()) { 93 GrPrintf("LCD Text will not draw correctly.\n"); 94 } 95 // We don't use the GrPaint's color in this case because it's been premultiplied by 96 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by 97 // the mask texture color. The end result is that we get 98 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor 99 int a = SkColorGetA(fSkPaint.getColor()); 100 // paintAlpha 101 drawState->setColor(SkColorSetARGB(a, a, a, a)); 102 // paintColor 103 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor())); 104 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 105 } else { 106 // set back to normal in case we took LCD path previously. 107 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); 108 drawState->setColor(fPaint.getColor()); 109 } 110 111 int nGlyphs = fCurrVertex / 4; 112 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 113 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 114 nGlyphs, 115 4, 6); 116 fDrawTarget->resetVertexSource(); 117 fVertices = NULL; 118 fMaxVertices = 0; 119 fCurrVertex = 0; 120 SkSafeSetNull(fCurrTexture); 121 } 122} 123 124namespace { 125 126// position + texture coord 127extern const GrVertexAttrib gTextVertexAttribs[] = { 128 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 129 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} 130}; 131 132}; 133 134void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 135 GrFixed vx, GrFixed vy, 136 GrFontScaler* scaler) { 137 if (NULL == fDrawTarget) { 138 return; 139 } 140 if (NULL == fStrike) { 141 fStrike = fContext->getFontCache()->getStrike(scaler, true); 142 } 143 144 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 145 if (NULL == glyph || glyph->fBounds.isEmpty()) { 146 return; 147 } 148 149 SkScalar sx = SkFixedToScalar(vx); 150 SkScalar sy = SkFixedToScalar(vy); 151/* 152 // not valid, need to find a different solution for this 153 vx += SkIntToFixed(glyph->fBounds.fLeft); 154 vy += SkIntToFixed(glyph->fBounds.fTop); 155 156 // keep them as ints until we've done the clip-test 157 GrFixed width = glyph->fBounds.width(); 158 GrFixed height = glyph->fBounds.height(); 159 160 // check if we clipped out 161 if (true || NULL == glyph->fPlot) { 162 int x = vx >> 16; 163 int y = vy >> 16; 164 if (fClipRect.quickReject(x, y, x + width, y + height)) { 165// SkCLZ(3); // so we can set a break-point in the debugger 166 return; 167 } 168 } 169*/ 170 if (NULL == glyph->fPlot) { 171 if (fStrike->addGlyphToAtlas(glyph, scaler)) { 172 goto HAS_ATLAS; 173 } 174 175 // try to clear out an unused plot before we flush 176 if (fContext->getFontCache()->freeUnusedPlot(fStrike) && 177 fStrike->addGlyphToAtlas(glyph, scaler)) { 178 goto HAS_ATLAS; 179 } 180 181 if (c_DumpFontCache) { 182#ifdef SK_DEVELOPER 183 fContext->getFontCache()->dump(); 184#endif 185 } 186 187 // before we purge the cache, we must flush any accumulated draws 188 this->flushGlyphs(); 189 fContext->flush(); 190 191 // we should have an unused plot now 192 if (fContext->getFontCache()->freeUnusedPlot(fStrike) && 193 fStrike->addGlyphToAtlas(glyph, scaler)) { 194 goto HAS_ATLAS; 195 } 196 197 if (NULL == glyph->fPath) { 198 SkPath* path = SkNEW(SkPath); 199 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 200 // flag the glyph as being dead? 201 delete path; 202 return; 203 } 204 glyph->fPath = path; 205 } 206 207 GrContext::AutoMatrix am; 208 SkMatrix translate; 209 translate.setTranslate(sx, sy); 210 GrPaint tmpPaint(fPaint); 211 am.setPreConcat(fContext, translate, &tmpPaint); 212 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 213 fContext->drawPath(tmpPaint, *glyph->fPath, stroke); 214 return; 215 } 216 217HAS_ATLAS: 218 SkASSERT(glyph->fPlot); 219 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); 220 glyph->fPlot->setDrawToken(drawToken); 221 222 GrTexture* texture = glyph->fPlot->texture(); 223 SkASSERT(texture); 224 225 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 226 this->flushGlyphs(); 227 fCurrTexture = texture; 228 fCurrTexture->ref(); 229 } 230 231 if (NULL == fVertices) { 232 // If we need to reserve vertices allow the draw target to suggest 233 // a number of verts to reserve and whether to perform a flush. 234 fMaxVertices = kMinRequestedVerts; 235 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 236 SK_ARRAY_COUNT(gTextVertexAttribs)); 237 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); 238 if (flush) { 239 this->flushGlyphs(); 240 fContext->flush(); 241 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 242 SK_ARRAY_COUNT(gTextVertexAttribs)); 243 } 244 fMaxVertices = kDefaultRequestedVerts; 245 // ignore return, no point in flushing again. 246 fDrawTarget->geometryHints(&fMaxVertices, NULL); 247 248 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 249 if (fMaxVertices < kMinRequestedVerts) { 250 fMaxVertices = kDefaultRequestedVerts; 251 } else if (fMaxVertices > maxQuadVertices) { 252 // don't exceed the limit of the index buffer 253 fMaxVertices = maxQuadVertices; 254 } 255 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices, 256 0, 257 GrTCast<void**>(&fVertices), 258 NULL); 259 GrAlwaysAssert(success); 260 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize()); 261 } 262 263 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft); 264 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop); 265 SkScalar width = SkIntToScalar(glyph->fBounds.width()); 266 SkScalar height = SkIntToScalar(glyph->fBounds.height()); 267 268 SkScalar scale = fTextRatio; 269 dx *= scale; 270 dy *= scale; 271 sx += dx; 272 sy += dy; 273 width *= scale; 274 height *= scale; 275 276 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX); 277 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY); 278 GrFixed tw = SkIntToFixed(glyph->fBounds.width()); 279 GrFixed th = SkIntToFixed(glyph->fBounds.height()); 280 281 static const size_t kVertexSize = 2 * sizeof(SkPoint); 282 fVertices[2*fCurrVertex].setRectFan(sx, 283 sy, 284 sx + width, 285 sy + height, 286 kVertexSize); 287 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), 288 SkFixedToFloat(texture->normalizeFixedY(ty)), 289 SkFixedToFloat(texture->normalizeFixedX(tx + tw)), 290 SkFixedToFloat(texture->normalizeFixedY(ty + th)), 291 kVertexSize); 292 fCurrVertex += 4; 293} 294 295inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) { 296 GrTextContext::init(paint, skPaint); 297 298 fStrike = NULL; 299 300 fCurrTexture = NULL; 301 fCurrVertex = 0; 302 303 fVertices = NULL; 304 fMaxVertices = 0; 305 306 fTextRatio = fSkPaint.getTextSize()/kBaseDFFontSize; 307 308 fSkPaint.setTextSize(SkIntToScalar(kBaseDFFontSize)); 309 fSkPaint.setLCDRenderText(false); 310 fSkPaint.setAutohinted(false); 311 fSkPaint.setSubpixelText(true); 312} 313 314inline void GrDistanceFieldTextContext::finish() { 315 flushGlyphs(); 316 317 GrTextContext::finish(); 318} 319 320void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint, 321 const char text[], size_t byteLength, 322 SkScalar x, SkScalar y) { 323 SkASSERT(byteLength == 0 || text != NULL); 324 325 // nothing to draw or can't draw 326 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/ 327 || fSkPaint.getRasterizer()) { 328 return; 329 } 330 331 this->init(paint, skPaint); 332 333 SkScalar sizeRatio = fTextRatio; 334 335 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); 336 337 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); 338 SkGlyphCache* cache = autoCache.getCache(); 339 GrFontScaler* fontScaler = GetGrFontScaler(cache); 340 341 // need to measure first 342 // TODO - generate positions and pre-load cache as well? 343 const char* stop = text + byteLength; 344 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { 345 SkFixed stopX = 0; 346 SkFixed stopY = 0; 347 348 const char* textPtr = text; 349 while (textPtr < stop) { 350 // don't need x, y here, since all subpixel variants will have the 351 // same advance 352 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); 353 354 stopX += glyph.fAdvanceX; 355 stopY += glyph.fAdvanceY; 356 } 357 SkASSERT(textPtr == stop); 358 359 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio; 360 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio; 361 362 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { 363 alignX = SkScalarHalf(alignX); 364 alignY = SkScalarHalf(alignY); 365 } 366 367 x -= alignX; 368 y -= alignY; 369 } 370 371 SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf; 372 SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; 373 SkFixed fixedScale = SkScalarToFixed(sizeRatio); 374 while (text < stop) { 375 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 376 377 if (glyph.fWidth) { 378 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 379 glyph.getSubXFixed(), 380 glyph.getSubYFixed()), 381 SkFixedFloorToFixed(fx), 382 SkFixedFloorToFixed(fy), 383 fontScaler); 384 } 385 386 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale); 387 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale); 388 } 389 390 this->finish(); 391} 392 393void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint, 394 const char text[], size_t byteLength, 395 const SkScalar pos[], SkScalar constY, 396 int scalarsPerPosition) { 397 398 SkASSERT(byteLength == 0 || text != NULL); 399 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 400 401 // nothing to draw 402 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) { 403 return; 404 } 405 406 this->init(paint, skPaint); 407 408 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); 409 410 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); 411 SkGlyphCache* cache = autoCache.getCache(); 412 GrFontScaler* fontScaler = GetGrFontScaler(cache); 413 414 const char* stop = text + byteLength; 415 416 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { 417 while (text < stop) { 418 // the last 2 parameters are ignored 419 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 420 421 if (glyph.fWidth) { 422 SkScalar x = pos[0]; 423 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; 424 425 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 426 glyph.getSubXFixed(), 427 glyph.getSubYFixed()), 428 SkScalarToFixed(x) + SK_FixedHalf, //d1g.fHalfSampleX, 429 SkScalarToFixed(y) + SK_FixedHalf, //d1g.fHalfSampleY, 430 fontScaler); 431 } 432 pos += scalarsPerPosition; 433 } 434 } else { 435 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0; 436 while (text < stop) { 437 // the last 2 parameters are ignored 438 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 439 440 if (glyph.fWidth) { 441 SkScalar x = pos[0]; 442 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; 443 444 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 445 glyph.getSubXFixed(), 446 glyph.getSubYFixed()), 447 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift) 448 + SK_FixedHalf, //d1g.fHalfSampleX, 449 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift) 450 + SK_FixedHalf, //d1g.fHalfSampleY, 451 fontScaler); 452 } 453 pos += scalarsPerPosition; 454 } 455 } 456 457 this->finish(); 458} 459