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