1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/gpu/GrDistanceFieldTextContext.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,458 @@ 1.4 +/* 1.5 + * Copyright 2013 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "GrDistanceFieldTextContext.h" 1.12 +#include "GrAtlas.h" 1.13 +#include "GrDrawTarget.h" 1.14 +#include "GrDrawTargetCaps.h" 1.15 +#include "GrFontScaler.h" 1.16 +#include "SkGlyphCache.h" 1.17 +#include "GrIndexBuffer.h" 1.18 +#include "GrTextStrike.h" 1.19 +#include "GrTextStrike_impl.h" 1.20 +#include "SkDraw.h" 1.21 +#include "SkGpuDevice.h" 1.22 +#include "SkPath.h" 1.23 +#include "SkRTConf.h" 1.24 +#include "SkStrokeRec.h" 1.25 +#include "effects/GrDistanceFieldTextureEffect.h" 1.26 + 1.27 +static const int kGlyphCoordsAttributeIndex = 1; 1.28 + 1.29 +static const int kBaseDFFontSize = 32; 1.30 + 1.31 +SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, 1.32 + "Dump the contents of the font cache before every purge."); 1.33 + 1.34 +#if SK_FORCE_DISTANCEFIELD_FONTS 1.35 +static const bool kForceDistanceFieldFonts = true; 1.36 +#else 1.37 +static const bool kForceDistanceFieldFonts = false; 1.38 +#endif 1.39 + 1.40 +GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, 1.41 + const SkDeviceProperties& properties) 1.42 + : GrTextContext(context, properties) { 1.43 + fStrike = NULL; 1.44 + 1.45 + fCurrTexture = NULL; 1.46 + fCurrVertex = 0; 1.47 + 1.48 + fVertices = NULL; 1.49 + fMaxVertices = 0; 1.50 +} 1.51 + 1.52 +GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { 1.53 + this->flushGlyphs(); 1.54 +} 1.55 + 1.56 +bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) { 1.57 + return (kForceDistanceFieldFonts || paint.isDistanceFieldTextTEMP()) && 1.58 + !paint.getRasterizer() && !paint.getMaskFilter() && 1.59 + paint.getStyle() == SkPaint::kFill_Style && 1.60 + fContext->getTextTarget()->caps()->shaderDerivativeSupport() && 1.61 + !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix()); 1.62 +} 1.63 + 1.64 +static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { 1.65 + unsigned r = SkColorGetR(c); 1.66 + unsigned g = SkColorGetG(c); 1.67 + unsigned b = SkColorGetB(c); 1.68 + return GrColorPackRGBA(r, g, b, 0xff); 1.69 +} 1.70 + 1.71 +void GrDistanceFieldTextContext::flushGlyphs() { 1.72 + if (NULL == fDrawTarget) { 1.73 + return; 1.74 + } 1.75 + 1.76 + GrDrawState* drawState = fDrawTarget->drawState(); 1.77 + GrDrawState::AutoRestoreEffects are(drawState); 1.78 + drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget()); 1.79 + 1.80 + if (fCurrVertex > 0) { 1.81 + // setup our sampler state for our text texture/atlas 1.82 + SkASSERT(GrIsALIGN4(fCurrVertex)); 1.83 + SkASSERT(fCurrTexture); 1.84 + GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); 1.85 + 1.86 + // This effect could be stored with one of the cache objects (atlas?) 1.87 + SkISize size = fStrike->getAtlasSize(); 1.88 + drawState->addCoverageEffect( 1.89 + GrDistanceFieldTextureEffect::Create(fCurrTexture, params, size), 1.90 + kGlyphCoordsAttributeIndex)->unref(); 1.91 + 1.92 + if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 1.93 + if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || 1.94 + kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || 1.95 + fPaint.numColorStages()) { 1.96 + GrPrintf("LCD Text will not draw correctly.\n"); 1.97 + } 1.98 + // We don't use the GrPaint's color in this case because it's been premultiplied by 1.99 + // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by 1.100 + // the mask texture color. The end result is that we get 1.101 + // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor 1.102 + int a = SkColorGetA(fSkPaint.getColor()); 1.103 + // paintAlpha 1.104 + drawState->setColor(SkColorSetARGB(a, a, a, a)); 1.105 + // paintColor 1.106 + drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor())); 1.107 + drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 1.108 + } else { 1.109 + // set back to normal in case we took LCD path previously. 1.110 + drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); 1.111 + drawState->setColor(fPaint.getColor()); 1.112 + } 1.113 + 1.114 + int nGlyphs = fCurrVertex / 4; 1.115 + fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 1.116 + fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 1.117 + nGlyphs, 1.118 + 4, 6); 1.119 + fDrawTarget->resetVertexSource(); 1.120 + fVertices = NULL; 1.121 + fMaxVertices = 0; 1.122 + fCurrVertex = 0; 1.123 + SkSafeSetNull(fCurrTexture); 1.124 + } 1.125 +} 1.126 + 1.127 +namespace { 1.128 + 1.129 +// position + texture coord 1.130 +extern const GrVertexAttrib gTextVertexAttribs[] = { 1.131 + {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 1.132 + {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} 1.133 +}; 1.134 + 1.135 +}; 1.136 + 1.137 +void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 1.138 + GrFixed vx, GrFixed vy, 1.139 + GrFontScaler* scaler) { 1.140 + if (NULL == fDrawTarget) { 1.141 + return; 1.142 + } 1.143 + if (NULL == fStrike) { 1.144 + fStrike = fContext->getFontCache()->getStrike(scaler, true); 1.145 + } 1.146 + 1.147 + GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 1.148 + if (NULL == glyph || glyph->fBounds.isEmpty()) { 1.149 + return; 1.150 + } 1.151 + 1.152 + SkScalar sx = SkFixedToScalar(vx); 1.153 + SkScalar sy = SkFixedToScalar(vy); 1.154 +/* 1.155 + // not valid, need to find a different solution for this 1.156 + vx += SkIntToFixed(glyph->fBounds.fLeft); 1.157 + vy += SkIntToFixed(glyph->fBounds.fTop); 1.158 + 1.159 + // keep them as ints until we've done the clip-test 1.160 + GrFixed width = glyph->fBounds.width(); 1.161 + GrFixed height = glyph->fBounds.height(); 1.162 + 1.163 + // check if we clipped out 1.164 + if (true || NULL == glyph->fPlot) { 1.165 + int x = vx >> 16; 1.166 + int y = vy >> 16; 1.167 + if (fClipRect.quickReject(x, y, x + width, y + height)) { 1.168 +// SkCLZ(3); // so we can set a break-point in the debugger 1.169 + return; 1.170 + } 1.171 + } 1.172 +*/ 1.173 + if (NULL == glyph->fPlot) { 1.174 + if (fStrike->addGlyphToAtlas(glyph, scaler)) { 1.175 + goto HAS_ATLAS; 1.176 + } 1.177 + 1.178 + // try to clear out an unused plot before we flush 1.179 + if (fContext->getFontCache()->freeUnusedPlot(fStrike) && 1.180 + fStrike->addGlyphToAtlas(glyph, scaler)) { 1.181 + goto HAS_ATLAS; 1.182 + } 1.183 + 1.184 + if (c_DumpFontCache) { 1.185 +#ifdef SK_DEVELOPER 1.186 + fContext->getFontCache()->dump(); 1.187 +#endif 1.188 + } 1.189 + 1.190 + // before we purge the cache, we must flush any accumulated draws 1.191 + this->flushGlyphs(); 1.192 + fContext->flush(); 1.193 + 1.194 + // we should have an unused plot now 1.195 + if (fContext->getFontCache()->freeUnusedPlot(fStrike) && 1.196 + fStrike->addGlyphToAtlas(glyph, scaler)) { 1.197 + goto HAS_ATLAS; 1.198 + } 1.199 + 1.200 + if (NULL == glyph->fPath) { 1.201 + SkPath* path = SkNEW(SkPath); 1.202 + if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 1.203 + // flag the glyph as being dead? 1.204 + delete path; 1.205 + return; 1.206 + } 1.207 + glyph->fPath = path; 1.208 + } 1.209 + 1.210 + GrContext::AutoMatrix am; 1.211 + SkMatrix translate; 1.212 + translate.setTranslate(sx, sy); 1.213 + GrPaint tmpPaint(fPaint); 1.214 + am.setPreConcat(fContext, translate, &tmpPaint); 1.215 + SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 1.216 + fContext->drawPath(tmpPaint, *glyph->fPath, stroke); 1.217 + return; 1.218 + } 1.219 + 1.220 +HAS_ATLAS: 1.221 + SkASSERT(glyph->fPlot); 1.222 + GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); 1.223 + glyph->fPlot->setDrawToken(drawToken); 1.224 + 1.225 + GrTexture* texture = glyph->fPlot->texture(); 1.226 + SkASSERT(texture); 1.227 + 1.228 + if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 1.229 + this->flushGlyphs(); 1.230 + fCurrTexture = texture; 1.231 + fCurrTexture->ref(); 1.232 + } 1.233 + 1.234 + if (NULL == fVertices) { 1.235 + // If we need to reserve vertices allow the draw target to suggest 1.236 + // a number of verts to reserve and whether to perform a flush. 1.237 + fMaxVertices = kMinRequestedVerts; 1.238 + fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 1.239 + SK_ARRAY_COUNT(gTextVertexAttribs)); 1.240 + bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); 1.241 + if (flush) { 1.242 + this->flushGlyphs(); 1.243 + fContext->flush(); 1.244 + fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 1.245 + SK_ARRAY_COUNT(gTextVertexAttribs)); 1.246 + } 1.247 + fMaxVertices = kDefaultRequestedVerts; 1.248 + // ignore return, no point in flushing again. 1.249 + fDrawTarget->geometryHints(&fMaxVertices, NULL); 1.250 + 1.251 + int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 1.252 + if (fMaxVertices < kMinRequestedVerts) { 1.253 + fMaxVertices = kDefaultRequestedVerts; 1.254 + } else if (fMaxVertices > maxQuadVertices) { 1.255 + // don't exceed the limit of the index buffer 1.256 + fMaxVertices = maxQuadVertices; 1.257 + } 1.258 + bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices, 1.259 + 0, 1.260 + GrTCast<void**>(&fVertices), 1.261 + NULL); 1.262 + GrAlwaysAssert(success); 1.263 + SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize()); 1.264 + } 1.265 + 1.266 + SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft); 1.267 + SkScalar dy = SkIntToScalar(glyph->fBounds.fTop); 1.268 + SkScalar width = SkIntToScalar(glyph->fBounds.width()); 1.269 + SkScalar height = SkIntToScalar(glyph->fBounds.height()); 1.270 + 1.271 + SkScalar scale = fTextRatio; 1.272 + dx *= scale; 1.273 + dy *= scale; 1.274 + sx += dx; 1.275 + sy += dy; 1.276 + width *= scale; 1.277 + height *= scale; 1.278 + 1.279 + GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX); 1.280 + GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY); 1.281 + GrFixed tw = SkIntToFixed(glyph->fBounds.width()); 1.282 + GrFixed th = SkIntToFixed(glyph->fBounds.height()); 1.283 + 1.284 + static const size_t kVertexSize = 2 * sizeof(SkPoint); 1.285 + fVertices[2*fCurrVertex].setRectFan(sx, 1.286 + sy, 1.287 + sx + width, 1.288 + sy + height, 1.289 + kVertexSize); 1.290 + fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), 1.291 + SkFixedToFloat(texture->normalizeFixedY(ty)), 1.292 + SkFixedToFloat(texture->normalizeFixedX(tx + tw)), 1.293 + SkFixedToFloat(texture->normalizeFixedY(ty + th)), 1.294 + kVertexSize); 1.295 + fCurrVertex += 4; 1.296 +} 1.297 + 1.298 +inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) { 1.299 + GrTextContext::init(paint, skPaint); 1.300 + 1.301 + fStrike = NULL; 1.302 + 1.303 + fCurrTexture = NULL; 1.304 + fCurrVertex = 0; 1.305 + 1.306 + fVertices = NULL; 1.307 + fMaxVertices = 0; 1.308 + 1.309 + fTextRatio = fSkPaint.getTextSize()/kBaseDFFontSize; 1.310 + 1.311 + fSkPaint.setTextSize(SkIntToScalar(kBaseDFFontSize)); 1.312 + fSkPaint.setLCDRenderText(false); 1.313 + fSkPaint.setAutohinted(false); 1.314 + fSkPaint.setSubpixelText(true); 1.315 +} 1.316 + 1.317 +inline void GrDistanceFieldTextContext::finish() { 1.318 + flushGlyphs(); 1.319 + 1.320 + GrTextContext::finish(); 1.321 +} 1.322 + 1.323 +void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint, 1.324 + const char text[], size_t byteLength, 1.325 + SkScalar x, SkScalar y) { 1.326 + SkASSERT(byteLength == 0 || text != NULL); 1.327 + 1.328 + // nothing to draw or can't draw 1.329 + if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/ 1.330 + || fSkPaint.getRasterizer()) { 1.331 + return; 1.332 + } 1.333 + 1.334 + this->init(paint, skPaint); 1.335 + 1.336 + SkScalar sizeRatio = fTextRatio; 1.337 + 1.338 + SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); 1.339 + 1.340 + SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); 1.341 + SkGlyphCache* cache = autoCache.getCache(); 1.342 + GrFontScaler* fontScaler = GetGrFontScaler(cache); 1.343 + 1.344 + // need to measure first 1.345 + // TODO - generate positions and pre-load cache as well? 1.346 + const char* stop = text + byteLength; 1.347 + if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { 1.348 + SkFixed stopX = 0; 1.349 + SkFixed stopY = 0; 1.350 + 1.351 + const char* textPtr = text; 1.352 + while (textPtr < stop) { 1.353 + // don't need x, y here, since all subpixel variants will have the 1.354 + // same advance 1.355 + const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); 1.356 + 1.357 + stopX += glyph.fAdvanceX; 1.358 + stopY += glyph.fAdvanceY; 1.359 + } 1.360 + SkASSERT(textPtr == stop); 1.361 + 1.362 + SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio; 1.363 + SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio; 1.364 + 1.365 + if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { 1.366 + alignX = SkScalarHalf(alignX); 1.367 + alignY = SkScalarHalf(alignY); 1.368 + } 1.369 + 1.370 + x -= alignX; 1.371 + y -= alignY; 1.372 + } 1.373 + 1.374 + SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf; 1.375 + SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; 1.376 + SkFixed fixedScale = SkScalarToFixed(sizeRatio); 1.377 + while (text < stop) { 1.378 + const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy); 1.379 + 1.380 + if (glyph.fWidth) { 1.381 + this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 1.382 + glyph.getSubXFixed(), 1.383 + glyph.getSubYFixed()), 1.384 + SkFixedFloorToFixed(fx), 1.385 + SkFixedFloorToFixed(fy), 1.386 + fontScaler); 1.387 + } 1.388 + 1.389 + fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale); 1.390 + fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale); 1.391 + } 1.392 + 1.393 + this->finish(); 1.394 +} 1.395 + 1.396 +void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint, 1.397 + const char text[], size_t byteLength, 1.398 + const SkScalar pos[], SkScalar constY, 1.399 + int scalarsPerPosition) { 1.400 + 1.401 + SkASSERT(byteLength == 0 || text != NULL); 1.402 + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); 1.403 + 1.404 + // nothing to draw 1.405 + if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) { 1.406 + return; 1.407 + } 1.408 + 1.409 + this->init(paint, skPaint); 1.410 + 1.411 + SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); 1.412 + 1.413 + SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); 1.414 + SkGlyphCache* cache = autoCache.getCache(); 1.415 + GrFontScaler* fontScaler = GetGrFontScaler(cache); 1.416 + 1.417 + const char* stop = text + byteLength; 1.418 + 1.419 + if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { 1.420 + while (text < stop) { 1.421 + // the last 2 parameters are ignored 1.422 + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 1.423 + 1.424 + if (glyph.fWidth) { 1.425 + SkScalar x = pos[0]; 1.426 + SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; 1.427 + 1.428 + this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 1.429 + glyph.getSubXFixed(), 1.430 + glyph.getSubYFixed()), 1.431 + SkScalarToFixed(x) + SK_FixedHalf, //d1g.fHalfSampleX, 1.432 + SkScalarToFixed(y) + SK_FixedHalf, //d1g.fHalfSampleY, 1.433 + fontScaler); 1.434 + } 1.435 + pos += scalarsPerPosition; 1.436 + } 1.437 + } else { 1.438 + int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0; 1.439 + while (text < stop) { 1.440 + // the last 2 parameters are ignored 1.441 + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); 1.442 + 1.443 + if (glyph.fWidth) { 1.444 + SkScalar x = pos[0]; 1.445 + SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; 1.446 + 1.447 + this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), 1.448 + glyph.getSubXFixed(), 1.449 + glyph.getSubYFixed()), 1.450 + SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift) 1.451 + + SK_FixedHalf, //d1g.fHalfSampleX, 1.452 + SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift) 1.453 + + SK_FixedHalf, //d1g.fHalfSampleY, 1.454 + fontScaler); 1.455 + } 1.456 + pos += scalarsPerPosition; 1.457 + } 1.458 + } 1.459 + 1.460 + this->finish(); 1.461 +}