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 "GrFontScaler.h" 12#include "GrIndexBuffer.h" 13#include "GrTextStrike.h" 14#include "GrTextStrike_impl.h" 15#include "SkPath.h" 16#include "SkRTConf.h" 17#include "SkStrokeRec.h" 18#include "effects/GrDistanceFieldTextureEffect.h" 19 20static const int kGlyphCoordsAttributeIndex = 1; 21 22SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, 23 "Dump the contents of the font cache before every purge."); 24 25 26GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, 27 const GrPaint& paint, 28 SkColor color, 29 SkScalar textRatio) 30 : GrTextContext(context, paint) 31 , fTextRatio(textRatio) { 32 fSkPaintColor = color; 33 34 fStrike = NULL; 35 36 fCurrTexture = NULL; 37 fCurrVertex = 0; 38 39 fVertices = NULL; 40 fMaxVertices = 0; 41} 42 43GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { 44 this->flushGlyphs(); 45} 46 47static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { 48 unsigned r = SkColorGetR(c); 49 unsigned g = SkColorGetG(c); 50 unsigned b = SkColorGetB(c); 51 return GrColorPackRGBA(r, g, b, 0xff); 52} 53 54void GrDistanceFieldTextContext::flushGlyphs() { 55 if (NULL == fDrawTarget) { 56 return; 57 } 58 59 GrDrawState* drawState = fDrawTarget->drawState(); 60 GrDrawState::AutoRestoreEffects are(drawState); 61 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget()); 62 63 if (fCurrVertex > 0) { 64 // setup our sampler state for our text texture/atlas 65 SkASSERT(GrIsALIGN4(fCurrVertex)); 66 SkASSERT(fCurrTexture); 67 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); 68 69 // This effect could be stored with one of the cache objects (atlas?) 70 drawState->addCoverageEffect( 71 GrDistanceFieldTextureEffect::Create(fCurrTexture, params), 72 kGlyphCoordsAttributeIndex)->unref(); 73 74 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 75 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || 76 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || 77 fPaint.numColorStages()) { 78 GrPrintf("LCD Text will not draw correctly.\n"); 79 } 80 // We don't use the GrPaint's color in this case because it's been premultiplied by 81 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by 82 // the mask texture color. The end result is that we get 83 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor 84 int a = SkColorGetA(fSkPaintColor); 85 // paintAlpha 86 drawState->setColor(SkColorSetARGB(a, a, a, a)); 87 // paintColor 88 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor)); 89 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 90 } else { 91 // set back to normal in case we took LCD path previously. 92 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); 93 drawState->setColor(fPaint.getColor()); 94 } 95 96 int nGlyphs = fCurrVertex / 4; 97 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 98 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 99 nGlyphs, 100 4, 6); 101 fDrawTarget->resetVertexSource(); 102 fVertices = NULL; 103 fMaxVertices = 0; 104 fCurrVertex = 0; 105 SkSafeSetNull(fCurrTexture); 106 } 107} 108 109namespace { 110 111// position + texture coord 112extern const GrVertexAttrib gTextVertexAttribs[] = { 113 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 114 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} 115}; 116 117}; 118 119void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 120 GrFixed vx, GrFixed vy, 121 GrFontScaler* scaler) { 122 if (NULL == fDrawTarget) { 123 return; 124 } 125 if (NULL == fStrike) { 126 fStrike = fContext->getFontCache()->getStrike(scaler, true); 127 } 128 129 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 130 if (NULL == glyph || glyph->fBounds.isEmpty()) { 131 return; 132 } 133 134 SkScalar sx = SkFixedToScalar(vx); 135 SkScalar sy = SkFixedToScalar(vy); 136/* 137 // not valid, need to find a different solution for this 138 vx += SkIntToFixed(glyph->fBounds.fLeft); 139 vy += SkIntToFixed(glyph->fBounds.fTop); 140 141 // keep them as ints until we've done the clip-test 142 GrFixed width = glyph->fBounds.width(); 143 GrFixed height = glyph->fBounds.height(); 144 145 // check if we clipped out 146 if (true || NULL == glyph->fPlot) { 147 int x = vx >> 16; 148 int y = vy >> 16; 149 if (fClipRect.quickReject(x, y, x + width, y + height)) { 150// SkCLZ(3); // so we can set a break-point in the debugger 151 return; 152 } 153 } 154*/ 155 if (NULL == glyph->fPlot) { 156 if (fStrike->getGlyphAtlas(glyph, scaler)) { 157 goto HAS_ATLAS; 158 } 159 160 // try to clear out an unused plot before we flush 161 fContext->getFontCache()->freePlotExceptFor(fStrike); 162 if (fStrike->getGlyphAtlas(glyph, scaler)) { 163 goto HAS_ATLAS; 164 } 165 166 if (c_DumpFontCache) { 167#ifdef SK_DEVELOPER 168 fContext->getFontCache()->dump(); 169#endif 170 } 171 172 // before we purge the cache, we must flush any accumulated draws 173 this->flushGlyphs(); 174 fContext->flush(); 175 176 // try to purge 177 fContext->getFontCache()->purgeExceptFor(fStrike); 178 // need to use new flush count here 179 if (fStrike->getGlyphAtlas(glyph, scaler)) { 180 goto HAS_ATLAS; 181 } 182 183 if (NULL == glyph->fPath) { 184 SkPath* path = SkNEW(SkPath); 185 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 186 // flag the glyph as being dead? 187 delete path; 188 return; 189 } 190 glyph->fPath = path; 191 } 192 193 GrContext::AutoMatrix am; 194 SkMatrix translate; 195 translate.setTranslate(sx, sy); 196 GrPaint tmpPaint(fPaint); 197 am.setPreConcat(fContext, translate, &tmpPaint); 198 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 199 fContext->drawPath(tmpPaint, *glyph->fPath, stroke); 200 return; 201 } 202 203HAS_ATLAS: 204 SkASSERT(glyph->fPlot); 205 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); 206 glyph->fPlot->setDrawToken(drawToken); 207 208 GrTexture* texture = glyph->fPlot->texture(); 209 SkASSERT(texture); 210 211 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 212 this->flushGlyphs(); 213 fCurrTexture = texture; 214 fCurrTexture->ref(); 215 } 216 217 if (NULL == fVertices) { 218 // If we need to reserve vertices allow the draw target to suggest 219 // a number of verts to reserve and whether to perform a flush. 220 fMaxVertices = kMinRequestedVerts; 221 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 222 SK_ARRAY_COUNT(gTextVertexAttribs)); 223 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); 224 if (flush) { 225 this->flushGlyphs(); 226 fContext->flush(); 227 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 228 SK_ARRAY_COUNT(gTextVertexAttribs)); 229 } 230 fMaxVertices = kDefaultRequestedVerts; 231 // ignore return, no point in flushing again. 232 fDrawTarget->geometryHints(&fMaxVertices, NULL); 233 234 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 235 if (fMaxVertices < kMinRequestedVerts) { 236 fMaxVertices = kDefaultRequestedVerts; 237 } else if (fMaxVertices > maxQuadVertices) { 238 // don't exceed the limit of the index buffer 239 fMaxVertices = maxQuadVertices; 240 } 241 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices, 242 0, 243 GrTCast<void**>(&fVertices), 244 NULL); 245 GrAlwaysAssert(success); 246 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize()); 247 } 248 249 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft); 250 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop); 251 SkScalar width = SkIntToScalar(glyph->fBounds.width()); 252 SkScalar height = SkIntToScalar(glyph->fBounds.height()); 253 254 SkScalar scale = fTextRatio; 255 dx *= scale; 256 dy *= scale; 257 sx += dx; 258 sy += dy; 259 width *= scale; 260 height *= scale; 261 262 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX); 263 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY); 264 GrFixed tw = SkIntToFixed(glyph->fBounds.width()); 265 GrFixed th = SkIntToFixed(glyph->fBounds.height()); 266 267 fVertices[2*fCurrVertex].setRectFan(sx, 268 sy, 269 sx + width, 270 sy + height, 271 2 * sizeof(SkPoint)); 272 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), 273 SkFixedToFloat(texture->normalizeFixedY(ty)), 274 SkFixedToFloat(texture->normalizeFixedX(tx + tw)), 275 SkFixedToFloat(texture->normalizeFixedY(ty + th)), 276 2 * sizeof(SkPoint)); 277 fCurrVertex += 4; 278} 279