GrTextContext.cpp revision f6de475e5cbd143f348ff7738919e397b7fe7f57
1/* 2 * Copyright 2010 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 9 10#include "GrTextContext.h" 11#include "GrAtlas.h" 12#include "GrContext.h" 13#include "GrDrawTarget.h" 14#include "GrFontScaler.h" 15#include "GrIndexBuffer.h" 16#include "GrTextStrike.h" 17#include "GrTextStrike_impl.h" 18#include "SkPath.h" 19#include "SkStrokeRec.h" 20 21static const int kGlyphCoordsAttributeIndex = 1; 22 23void GrTextContext::flushGlyphs() { 24 if (NULL == fDrawTarget) { 25 return; 26 } 27 28 GrDrawState* drawState = fDrawTarget->drawState(); 29 GrDrawState::AutoRestoreEffects are(drawState); 30 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget()); 31 32 if (fCurrVertex > 0) { 33 // setup our sampler state for our text texture/atlas 34 SkASSERT(GrIsALIGN4(fCurrVertex)); 35 SkASSERT(fCurrTexture); 36 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode); 37 38 // This effect could be stored with one of the cache objects (atlas?) 39 drawState->addCoverageEffect( 40 GrSimpleTextureEffect::CreateWithCustomCoords(fCurrTexture, params), 41 kGlyphCoordsAttributeIndex)->unref(); 42 43 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 44 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || 45 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || 46 fPaint.numColorStages()) { 47 GrPrintf("LCD Text will not draw correctly.\n"); 48 } 49 // setup blend so that we get mask * paintColor + (1-mask)*dstColor 50 drawState->setBlendConstant(fPaint.getColor()); 51 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 52 // don't modulate by the paint's color in the frag since we're 53 // already doing it via the blend const. 54 drawState->setColor(0xffffffff); 55 } else { 56 // set back to normal in case we took LCD path previously. 57 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); 58 drawState->setColor(fPaint.getColor()); 59 } 60 61 int nGlyphs = fCurrVertex / 4; 62 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 63 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 64 nGlyphs, 65 4, 6); 66 fDrawTarget->resetVertexSource(); 67 fVertices = NULL; 68 fMaxVertices = 0; 69 fCurrVertex = 0; 70 GrSafeSetNull(fCurrTexture); 71 } 72} 73 74GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) { 75 fContext = context; 76 fStrike = NULL; 77 78 fCurrTexture = NULL; 79 fCurrVertex = 0; 80 81 const GrClipData* clipData = context->getClip(); 82 83 SkRect devConservativeBound; 84 clipData->fClipStack->getConservativeBounds( 85 -clipData->fOrigin.fX, 86 -clipData->fOrigin.fY, 87 context->getRenderTarget()->width(), 88 context->getRenderTarget()->height(), 89 &devConservativeBound); 90 91 devConservativeBound.roundOut(&fClipRect); 92 93 fAutoMatrix.setIdentity(fContext, &fPaint); 94 95 fDrawTarget = fContext->getTextTarget(); 96 97 fVertices = NULL; 98 fMaxVertices = 0; 99} 100 101GrTextContext::~GrTextContext() { 102 this->flushGlyphs(); 103} 104 105void GrTextContext::flush() { 106 this->flushGlyphs(); 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 GrTextContext::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); 127 } 128 129 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 130 if (NULL == glyph || glyph->fBounds.isEmpty()) { 131 return; 132 } 133 134 vx += SkIntToFixed(glyph->fBounds.fLeft); 135 vy += SkIntToFixed(glyph->fBounds.fTop); 136 137 // keep them as ints until we've done the clip-test 138 GrFixed width = glyph->fBounds.width(); 139 GrFixed height = glyph->fBounds.height(); 140 141 // check if we clipped out 142 if (true || NULL == glyph->fAtlas) { 143 int x = vx >> 16; 144 int y = vy >> 16; 145 if (fClipRect.quickReject(x, y, x + width, y + height)) { 146// SkCLZ(3); // so we can set a break-point in the debugger 147 return; 148 } 149 } 150 151 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); 152 if (NULL == glyph->fAtlas) { 153 if (fStrike->getGlyphAtlas(glyph, scaler, drawToken)) { 154 goto HAS_ATLAS; 155 } 156 157 // try to clear out an unused atlas before we flush 158 fContext->getFontCache()->freeAtlasExceptFor(fStrike); 159 if (fStrike->getGlyphAtlas(glyph, scaler, drawToken)) { 160 goto HAS_ATLAS; 161 } 162 163 // before we purge the cache, we must flush any accumulated draws 164 this->flushGlyphs(); 165 fContext->flush(); 166 167 // try to purge 168 fContext->getFontCache()->purgeExceptFor(fStrike); 169 // need to use new flush count here 170 if (fStrike->getGlyphAtlas(glyph, scaler, drawToken)) { 171 goto HAS_ATLAS; 172 } 173 174 if (NULL == glyph->fPath) { 175 SkPath* path = SkNEW(SkPath); 176 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 177 // flag the glyph as being dead? 178 delete path; 179 return; 180 } 181 glyph->fPath = path; 182 } 183 184 GrContext::AutoMatrix am; 185 SkMatrix translate; 186 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)), 187 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop))); 188 GrPaint tmpPaint(fPaint); 189 am.setPreConcat(fContext, translate, &tmpPaint); 190 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 191 fContext->drawPath(tmpPaint, *glyph->fPath, stroke); 192 return; 193 } 194 195HAS_ATLAS: 196 SkASSERT(glyph->fAtlas); 197 198 // now promote them to fixed (TODO: Rethink using fixed pt). 199 width = SkIntToFixed(width); 200 height = SkIntToFixed(height); 201 202 GrTexture* texture = glyph->fAtlas->texture(); 203 SkASSERT(texture); 204 205 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 206 this->flushGlyphs(); 207 fCurrTexture = texture; 208 fCurrTexture->ref(); 209 } 210 211 if (NULL == fVertices) { 212 // If we need to reserve vertices allow the draw target to suggest 213 // a number of verts to reserve and whether to perform a flush. 214 fMaxVertices = kMinRequestedVerts; 215 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 216 SK_ARRAY_COUNT(gTextVertexAttribs)); 217 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); 218 if (flush) { 219 this->flushGlyphs(); 220 fContext->flush(); 221 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( 222 SK_ARRAY_COUNT(gTextVertexAttribs)); 223 } 224 fMaxVertices = kDefaultRequestedVerts; 225 // ignore return, no point in flushing again. 226 fDrawTarget->geometryHints(&fMaxVertices, NULL); 227 228 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 229 if (fMaxVertices < kMinRequestedVerts) { 230 fMaxVertices = kDefaultRequestedVerts; 231 } else if (fMaxVertices > maxQuadVertices) { 232 // don't exceed the limit of the index buffer 233 fMaxVertices = maxQuadVertices; 234 } 235 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices, 236 0, 237 GrTCast<void**>(&fVertices), 238 NULL); 239 GrAlwaysAssert(success); 240 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize()); 241 } 242 243 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX); 244 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY); 245 246 fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx), 247 SkFixedToFloat(vy), 248 SkFixedToFloat(vx + width), 249 SkFixedToFloat(vy + height), 250 2 * sizeof(SkPoint)); 251 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), 252 SkFixedToFloat(texture->normalizeFixedY(ty)), 253 SkFixedToFloat(texture->normalizeFixedX(tx + width)), 254 SkFixedToFloat(texture->normalizeFixedY(ty + height)), 255 2 * sizeof(SkPoint)); 256 fCurrVertex += 4; 257} 258