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