GrTextContext.cpp revision 08283afc265f1153834256fc1012519813ba6b73
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 "GrGpuVertex.h" 16#include "GrIndexBuffer.h" 17#include "GrTextStrike.h" 18#include "GrTextStrike_impl.h" 19#include "SkPath.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 drawState->stage(kGlyphMaskStage)->reset(); 33 34 GrAssert(GrIsALIGN4(fCurrVertex)); 35 GrAssert(fCurrTexture); 36 GrTextureParams params(SkShader::kRepeat_TileMode, false); 37 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, GrMatrix::I(), params); 38 39 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 40 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || 41 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || 42 fPaint.hasColorStage()) { 43 GrPrintf("LCD Text will not draw correctly.\n"); 44 } 45 // setup blend so that we get mask * paintColor + (1-mask)*dstColor 46 drawState->setBlendConstant(fPaint.getColor()); 47 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 48 // don't modulate by the paint's color in the frag since we're 49 // already doing it via the blend const. 50 drawState->setColor(0xffffffff); 51 } else { 52 // set back to normal in case we took LCD path previously. 53 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); 54 drawState->setColor(fPaint.getColor()); 55 } 56 57 int nGlyphs = fCurrVertex / 4; 58 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 59 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 60 nGlyphs, 61 4, 6); 62 fDrawTarget->resetVertexSource(); 63 fVertices = NULL; 64 fMaxVertices = 0; 65 fCurrVertex = 0; 66 GrSafeSetNull(fCurrTexture); 67 } 68 drawState->disableStages(); 69 fDrawTarget = NULL; 70} 71 72GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) { 73 fContext = context; 74 fStrike = NULL; 75 76 fCurrTexture = NULL; 77 fCurrVertex = 0; 78 79 const GrClipData* clipData = context->getClip(); 80 81 GrRect devConservativeBound; 82 clipData->fClipStack->getConservativeBounds( 83 -clipData->fOrigin.fX, 84 -clipData->fOrigin.fY, 85 context->getRenderTarget()->width(), 86 context->getRenderTarget()->height(), 87 &devConservativeBound); 88 89 devConservativeBound.roundOut(&fClipRect); 90 91 fAutoMatrix.setIdentity(fContext, &fPaint); 92 93 fDrawTarget = NULL; 94 95 fVertices = NULL; 96 fMaxVertices = 0; 97 98 fVertexLayout = 99 GrDrawTarget::kTextFormat_VertexLayoutBit | 100 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0); 101} 102 103GrTextContext::~GrTextContext() { 104 this->flushGlyphs(); 105 if (fDrawTarget) { 106 fDrawTarget->drawState()->disableStages(); 107 } 108} 109 110void GrTextContext::flush() { 111 this->flushGlyphs(); 112} 113 114static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b, 115 int stride) { 116 v[0 * stride].setI(l, t); 117 v[1 * stride].setI(l, b); 118 v[2 * stride].setI(r, b); 119 v[3 * stride].setI(r, t); 120} 121 122void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 123 GrFixed vx, GrFixed vy, 124 GrFontScaler* scaler) { 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 += GrIntToFixed(glyph->fBounds.fLeft); 135 vy += GrIntToFixed(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// Gr_clz(3); // so we can set a break-point in the debugger 147 return; 148 } 149 } 150 151 if (NULL == glyph->fAtlas) { 152 if (fStrike->getGlyphAtlas(glyph, scaler)) { 153 goto HAS_ATLAS; 154 } 155 156 // before we purge the cache, we must flush any accumulated draws 157 this->flushGlyphs(); 158 fContext->flush(); 159 160 // try to purge 161 fContext->getFontCache()->purgeExceptFor(fStrike); 162 if (fStrike->getGlyphAtlas(glyph, scaler)) { 163 goto HAS_ATLAS; 164 } 165 166 if (NULL == glyph->fPath) { 167 SkPath* path = SkNEW(SkPath); 168 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 169 // flag the glyph as being dead? 170 delete path; 171 return; 172 } 173 glyph->fPath = path; 174 } 175 176 GrContext::AutoMatrix am; 177 GrMatrix translate; 178 translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), 179 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); 180 GrPaint tmpPaint(fPaint); 181 am.setPreConcat(fContext, translate, &tmpPaint); 182 fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill); 183 return; 184 } 185 186HAS_ATLAS: 187 GrAssert(glyph->fAtlas); 188 189 // now promote them to fixed 190 width = GrIntToFixed(width); 191 height = GrIntToFixed(height); 192 193 GrTexture* texture = glyph->fAtlas->texture(); 194 GrAssert(texture); 195 196 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 197 this->flushGlyphs(); 198 fCurrTexture = texture; 199 fCurrTexture->ref(); 200 } 201 202 if (NULL == fVertices) { 203 // If we need to reserve vertices allow the draw target to suggest 204 // a number of verts to reserve and whether to perform a flush. 205 fMaxVertices = kMinRequestedVerts; 206 bool flush = (NULL != fDrawTarget) && 207 fDrawTarget->geometryHints(fVertexLayout, 208 &fMaxVertices, 209 NULL); 210 if (flush) { 211 this->flushGlyphs(); 212 fContext->flush(); 213 } 214 fDrawTarget = fContext->getTextTarget(fPaint); 215 fMaxVertices = kDefaultRequestedVerts; 216 // ignore return, no point in flushing again. 217 fDrawTarget->geometryHints(fVertexLayout, 218 &fMaxVertices, 219 NULL); 220 221 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 222 if (fMaxVertices < kMinRequestedVerts) { 223 fMaxVertices = kDefaultRequestedVerts; 224 } else if (fMaxVertices > maxQuadVertices) { 225 // don't exceed the limit of the index buffer 226 fMaxVertices = maxQuadVertices; 227 } 228 bool success = fDrawTarget->reserveVertexAndIndexSpace( 229 fVertexLayout, 230 fMaxVertices, 231 0, 232 GrTCast<void**>(&fVertices), 233 NULL); 234 GrAlwaysAssert(success); 235 } 236 237 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX); 238 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY); 239 240#if GR_TEXT_SCALAR_IS_USHORT 241 int x = vx >> 16; 242 int y = vy >> 16; 243 int w = width >> 16; 244 int h = height >> 16; 245 246 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2); 247 setRectFan(&fVertices[2*fCurrVertex+1], 248 texture->normalizeFixedX(tx), 249 texture->normalizeFixedY(ty), 250 texture->normalizeFixedX(tx + width), 251 texture->normalizeFixedY(ty + height), 252 2); 253#else 254 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height, 255 2 * sizeof(GrGpuTextVertex)); 256 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx), 257 texture->normalizeFixedY(ty), 258 texture->normalizeFixedX(tx + width), 259 texture->normalizeFixedY(ty + height), 260 2 * sizeof(GrGpuTextVertex)); 261#endif 262 fCurrVertex += 4; 263} 264 265