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