GrTextContext.cpp revision 3cbaa2d4da8bc39a99bf3afaaab43cc6dc481723
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 // save the context's original matrix off and restore in destructor 93 // this must be done before getTextTarget. 94 fOrigViewMatrix = fContext->getMatrix(); 95 fContext->setIdentityMatrix(); 96 97 /* 98 We need to call preConcatMatrix with our viewmatrix's inverse, for each 99 texture and mask in the paint. However, computing the inverse can be 100 expensive, and its possible we may not have any textures or masks, so these 101 two loops are written such that we only compute the inverse (once) if we 102 need it. We do this on our copy of the paint rather than directly on the 103 draw target because we re-provide the paint to the context when we have 104 to flush our glyphs or draw a glyph as a path midstream. 105 */ 106 bool invVMComputed = false; 107 GrMatrix invVM; 108 for (int t = 0; t < GrPaint::kMaxColorStages; ++t) { 109 if (fPaint.isColorStageEnabled(t)) { 110 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) { 111 invVMComputed = true; 112 fPaint.colorSampler(t)->preConcatMatrix(invVM); 113 } 114 } 115 } 116 for (int m = 0; m < GrPaint::kMaxCoverageStages; ++m) { 117 if (fPaint.isCoverageStageEnabled(m)) { 118 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) { 119 invVMComputed = true; 120 fPaint.coverageSampler(m)->preConcatMatrix(invVM); 121 } 122 } 123 } 124 125 fDrawTarget = NULL; 126 127 fVertices = NULL; 128 fMaxVertices = 0; 129 130 fVertexLayout = 131 GrDrawTarget::kTextFormat_VertexLayoutBit | 132 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0); 133} 134 135GrTextContext::~GrTextContext() { 136 this->flushGlyphs(); 137 if (fDrawTarget) { 138 fDrawTarget->drawState()->disableStages(); 139 } 140 fContext->setMatrix(fOrigViewMatrix); 141} 142 143void GrTextContext::flush() { 144 this->flushGlyphs(); 145} 146 147static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b, 148 int stride) { 149 v[0 * stride].setI(l, t); 150 v[1 * stride].setI(l, b); 151 v[2 * stride].setI(r, b); 152 v[3 * stride].setI(r, t); 153} 154 155void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 156 GrFixed vx, GrFixed vy, 157 GrFontScaler* scaler) { 158 if (NULL == fStrike) { 159 fStrike = fContext->getFontCache()->getStrike(scaler); 160 } 161 162 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 163 if (NULL == glyph || glyph->fBounds.isEmpty()) { 164 return; 165 } 166 167 vx += GrIntToFixed(glyph->fBounds.fLeft); 168 vy += GrIntToFixed(glyph->fBounds.fTop); 169 170 // keep them as ints until we've done the clip-test 171 GrFixed width = glyph->fBounds.width(); 172 GrFixed height = glyph->fBounds.height(); 173 174 // check if we clipped out 175 if (true || NULL == glyph->fAtlas) { 176 int x = vx >> 16; 177 int y = vy >> 16; 178 if (fClipRect.quickReject(x, y, x + width, y + height)) { 179// Gr_clz(3); // so we can set a break-point in the debugger 180 return; 181 } 182 } 183 184 if (NULL == glyph->fAtlas) { 185 if (fStrike->getGlyphAtlas(glyph, scaler)) { 186 goto HAS_ATLAS; 187 } 188 189 // before we purge the cache, we must flush any accumulated draws 190 this->flushGlyphs(); 191 fContext->flush(); 192 193 // try to purge 194 fContext->getFontCache()->purgeExceptFor(fStrike); 195 if (fStrike->getGlyphAtlas(glyph, scaler)) { 196 goto HAS_ATLAS; 197 } 198 199 if (NULL == glyph->fPath) { 200 SkPath* path = SkNEW(SkPath); 201 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 202 // flag the glyph as being dead? 203 delete path; 204 return; 205 } 206 glyph->fPath = path; 207 } 208 209 GrContext::AutoMatrix am; 210 GrMatrix translate; 211 translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), 212 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); 213 GrPaint tmpPaint(fPaint); 214 am.setPreConcat(fContext, translate, &tmpPaint); 215 fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill); 216 return; 217 } 218 219HAS_ATLAS: 220 GrAssert(glyph->fAtlas); 221 222 // now promote them to fixed 223 width = GrIntToFixed(width); 224 height = GrIntToFixed(height); 225 226 GrTexture* texture = glyph->fAtlas->texture(); 227 GrAssert(texture); 228 229 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 230 this->flushGlyphs(); 231 fCurrTexture = texture; 232 fCurrTexture->ref(); 233 } 234 235 if (NULL == fVertices) { 236 // If we need to reserve vertices allow the draw target to suggest 237 // a number of verts to reserve and whether to perform a flush. 238 fMaxVertices = kMinRequestedVerts; 239 bool flush = (NULL != fDrawTarget) && 240 fDrawTarget->geometryHints(fVertexLayout, 241 &fMaxVertices, 242 NULL); 243 if (flush) { 244 this->flushGlyphs(); 245 fContext->flush(); 246 } 247 fDrawTarget = fContext->getTextTarget(fPaint); 248 fMaxVertices = kDefaultRequestedVerts; 249 // ignore return, no point in flushing again. 250 fDrawTarget->geometryHints(fVertexLayout, 251 &fMaxVertices, 252 NULL); 253 254 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 255 if (fMaxVertices < kMinRequestedVerts) { 256 fMaxVertices = kDefaultRequestedVerts; 257 } else if (fMaxVertices > maxQuadVertices) { 258 // don't exceed the limit of the index buffer 259 fMaxVertices = maxQuadVertices; 260 } 261 bool success = fDrawTarget->reserveVertexAndIndexSpace( 262 fVertexLayout, 263 fMaxVertices, 264 0, 265 GrTCast<void**>(&fVertices), 266 NULL); 267 GrAlwaysAssert(success); 268 } 269 270 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX); 271 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY); 272 273#if GR_TEXT_SCALAR_IS_USHORT 274 int x = vx >> 16; 275 int y = vy >> 16; 276 int w = width >> 16; 277 int h = height >> 16; 278 279 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2); 280 setRectFan(&fVertices[2*fCurrVertex+1], 281 texture->normalizeFixedX(tx), 282 texture->normalizeFixedY(ty), 283 texture->normalizeFixedX(tx + width), 284 texture->normalizeFixedY(ty + height), 285 2); 286#else 287 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height, 288 2 * sizeof(GrGpuTextVertex)); 289 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx), 290 texture->normalizeFixedY(ty), 291 texture->normalizeFixedX(tx + width), 292 texture->normalizeFixedY(ty + height), 293 2 * sizeof(GrGpuTextVertex)); 294#endif 295 fCurrVertex += 4; 296} 297 298