GrTextContext.cpp revision 858804dfe62c957050080a6fdce9226387ae7b7d
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 23 kGlyphMaskStage = GrPaint::kTotalStages, 24}; 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 drawState->sampler(kGlyphMaskStage)->reset(); 34 35 GrAssert(GrIsALIGN4(fCurrVertex)); 36 GrAssert(fCurrTexture); 37 GrTextureParams params(SkShader::kRepeat_TileMode, false); 38 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, 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 fVertexLayout = 100 GrDrawTarget::kTextFormat_VertexLayoutBit | 101 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0); 102} 103 104GrTextContext::~GrTextContext() { 105 this->flushGlyphs(); 106 if (fDrawTarget) { 107 fDrawTarget->drawState()->disableStages(); 108 } 109} 110 111void GrTextContext::flush() { 112 this->flushGlyphs(); 113} 114 115static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b, 116 int stride) { 117 v[0 * stride].setI(l, t); 118 v[1 * stride].setI(l, b); 119 v[2 * stride].setI(r, b); 120 v[3 * stride].setI(r, t); 121} 122 123void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 124 GrFixed vx, GrFixed vy, 125 GrFontScaler* scaler) { 126 if (NULL == fStrike) { 127 fStrike = fContext->getFontCache()->getStrike(scaler); 128 } 129 130 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 131 if (NULL == glyph || glyph->fBounds.isEmpty()) { 132 return; 133 } 134 135 vx += GrIntToFixed(glyph->fBounds.fLeft); 136 vy += GrIntToFixed(glyph->fBounds.fTop); 137 138 // keep them as ints until we've done the clip-test 139 GrFixed width = glyph->fBounds.width(); 140 GrFixed height = glyph->fBounds.height(); 141 142 // check if we clipped out 143 if (true || NULL == glyph->fAtlas) { 144 int x = vx >> 16; 145 int y = vy >> 16; 146 if (fClipRect.quickReject(x, y, x + width, y + height)) { 147// Gr_clz(3); // so we can set a break-point in the debugger 148 return; 149 } 150 } 151 152 if (NULL == glyph->fAtlas) { 153 if (fStrike->getGlyphAtlas(glyph, scaler)) { 154 goto HAS_ATLAS; 155 } 156 157 // before we purge the cache, we must flush any accumulated draws 158 this->flushGlyphs(); 159 fContext->flush(); 160 161 // try to purge 162 fContext->getFontCache()->purgeExceptFor(fStrike); 163 if (fStrike->getGlyphAtlas(glyph, scaler)) { 164 goto HAS_ATLAS; 165 } 166 167 if (NULL == glyph->fPath) { 168 SkPath* path = SkNEW(SkPath); 169 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 170 // flag the glyph as being dead? 171 delete path; 172 return; 173 } 174 glyph->fPath = path; 175 } 176 177 GrContext::AutoMatrix am; 178 GrMatrix translate; 179 translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), 180 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); 181 GrPaint tmpPaint(fPaint); 182 am.setPreConcat(fContext, translate, &tmpPaint); 183 fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill); 184 return; 185 } 186 187HAS_ATLAS: 188 GrAssert(glyph->fAtlas); 189 190 // now promote them to fixed 191 width = GrIntToFixed(width); 192 height = GrIntToFixed(height); 193 194 GrTexture* texture = glyph->fAtlas->texture(); 195 GrAssert(texture); 196 197 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 198 this->flushGlyphs(); 199 fCurrTexture = texture; 200 fCurrTexture->ref(); 201 } 202 203 if (NULL == fVertices) { 204 // If we need to reserve vertices allow the draw target to suggest 205 // a number of verts to reserve and whether to perform a flush. 206 fMaxVertices = kMinRequestedVerts; 207 bool flush = (NULL != fDrawTarget) && 208 fDrawTarget->geometryHints(fVertexLayout, 209 &fMaxVertices, 210 NULL); 211 if (flush) { 212 this->flushGlyphs(); 213 fContext->flush(); 214 } 215 fDrawTarget = fContext->getTextTarget(fPaint); 216 fMaxVertices = kDefaultRequestedVerts; 217 // ignore return, no point in flushing again. 218 fDrawTarget->geometryHints(fVertexLayout, 219 &fMaxVertices, 220 NULL); 221 222 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 223 if (fMaxVertices < kMinRequestedVerts) { 224 fMaxVertices = kDefaultRequestedVerts; 225 } else if (fMaxVertices > maxQuadVertices) { 226 // don't exceed the limit of the index buffer 227 fMaxVertices = maxQuadVertices; 228 } 229 bool success = fDrawTarget->reserveVertexAndIndexSpace( 230 fVertexLayout, 231 fMaxVertices, 232 0, 233 GrTCast<void**>(&fVertices), 234 NULL); 235 GrAlwaysAssert(success); 236 } 237 238 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX); 239 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY); 240 241#if GR_TEXT_SCALAR_IS_USHORT 242 int x = vx >> 16; 243 int y = vy >> 16; 244 int w = width >> 16; 245 int h = height >> 16; 246 247 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2); 248 setRectFan(&fVertices[2*fCurrVertex+1], 249 texture->normalizeFixedX(tx), 250 texture->normalizeFixedY(ty), 251 texture->normalizeFixedX(tx + width), 252 texture->normalizeFixedY(ty + height), 253 2); 254#else 255 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height, 256 2 * sizeof(GrGpuTextVertex)); 257 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx), 258 texture->normalizeFixedY(ty), 259 texture->normalizeFixedX(tx + width), 260 texture->normalizeFixedY(ty + height), 261 2 * sizeof(GrGpuTextVertex)); 262#endif 263 fCurrVertex += 4; 264} 265 266