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