GrTextContext.cpp revision 39ee0ffa72fbd5df6d3ec6db4fdad0c1bc3946fd
1 2/* 3 * Copyright 2010 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 11#include "GrTextContext.h" 12#include "GrAtlas.h" 13#include "GrContext.h" 14#include "GrTextStrike.h" 15#include "GrTextStrike_impl.h" 16#include "GrFontScaler.h" 17#include "GrIndexBuffer.h" 18#include "GrGpuVertex.h" 19#include "GrDrawTarget.h" 20 21enum { 22 kGlyphMaskStage = GrPaint::kTotalStages, 23}; 24 25void GrTextContext::flushGlyphs() { 26 if (fCurrVertex > 0) { 27 GrDrawTarget::AutoStateRestore asr(fDrawTarget); 28 29 // setup our sampler state for our text texture/atlas 30 GrSamplerState::Filter filter; 31 if (fExtMatrix.isIdentity()) { 32 filter = GrSamplerState::kNearest_Filter; 33 } else { 34 filter = GrSamplerState::kBilinear_Filter; 35 } 36 GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode, 37 GrSamplerState::kRepeat_WrapMode, 38 filter); 39 fDrawTarget->setSamplerState(kGlyphMaskStage, sampler); 40 41 GrAssert(GrIsALIGN4(fCurrVertex)); 42 int nIndices = fCurrVertex + (fCurrVertex >> 1); 43 GrAssert(fCurrTexture); 44 fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture); 45 46 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 47 if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff || 48 kISA_BlendCoeff != fPaint.fDstBlendCoeff || 49 fPaint.hasTexture()) { 50 GrPrintf("LCD Text will not draw correctly.\n"); 51 } 52 // setup blend so that we get mask * paintColor + (1-mask)*dstColor 53 fDrawTarget->setBlendConstant(fPaint.fColor); 54 fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff); 55 // don't modulate by the paint's color in the frag since we're 56 // already doing it via the blend const. 57 fDrawTarget->setColor(0xffffffff); 58 } else { 59 // set back to normal in case we took LCD path previously. 60 fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff); 61 fDrawTarget->setColor(fPaint.fColor); 62 } 63 64 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 65 66 fDrawTarget->drawIndexed(kTriangles_PrimitiveType, 67 0, 0, fCurrVertex, nIndices); 68 fDrawTarget->resetVertexSource(); 69 fVertices = NULL; 70 fMaxVertices = 0; 71 fCurrVertex = 0; 72 fCurrTexture->unref(); 73 fCurrTexture = NULL; 74 } 75} 76 77GrTextContext::GrTextContext(GrContext* context, 78 const GrPaint& paint, 79 const GrMatrix* extMatrix) : fPaint(paint) { 80 fContext = context; 81 fStrike = NULL; 82 83 fCurrTexture = NULL; 84 fCurrVertex = 0; 85 86 if (NULL != extMatrix) { 87 fExtMatrix = *extMatrix; 88 } else { 89 fExtMatrix = GrMatrix::I(); 90 } 91 if (context->getClip().hasConservativeBounds()) { 92 if (!fExtMatrix.isIdentity()) { 93 GrMatrix inverse; 94 GrRect r = context->getClip().getConservativeBounds(); 95 if (fExtMatrix.invert(&inverse)) { 96 inverse.mapRect(&r); 97 r.roundOut(&fClipRect); 98 } 99 } else { 100 context->getClip().getConservativeBounds().roundOut(&fClipRect); 101 } 102 } else { 103 fClipRect.setLargest(); 104 } 105 106 // save the context's original matrix off and restore in destructor 107 // this must be done before getTextTarget. 108 fOrigViewMatrix = fContext->getMatrix(); 109 fContext->setMatrix(fExtMatrix); 110 111 /* 112 We need to call preConcatMatrix with our viewmatrix's inverse, for each 113 texture and mask in the paint. However, computing the inverse can be 114 expensive, and its possible we may not have any textures or masks, so these 115 two loops are written such that we only compute the inverse (once) if we 116 need it. We do this on our copy of the paint rather than directly on the 117 draw target because we re-provide the paint to the context when we have 118 to flush our glyphs or draw a glyph as a path midstream. 119 */ 120 bool invVMComputed = false; 121 GrMatrix invVM; 122 for (int t = 0; t < GrPaint::kMaxTextures; ++t) { 123 if (NULL != fPaint.getTexture(t)) { 124 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) { 125 invVMComputed = true; 126 fPaint.textureSampler(t)->preConcatMatrix(invVM); 127 } 128 } 129 } 130 for (int m = 0; m < GrPaint::kMaxMasks; ++m) { 131 if (NULL != fPaint.getMask(m)) { 132 if (invVMComputed || fOrigViewMatrix.invert(&invVM)) { 133 invVMComputed = true; 134 fPaint.maskSampler(m)->preConcatMatrix(invVM); 135 } 136 } 137 } 138 139 fDrawTarget = fContext->getTextTarget(fPaint); 140 141 fVertices = NULL; 142 fMaxVertices = 0; 143 144 fVertexLayout = 145 GrDrawTarget::kTextFormat_VertexLayoutBit | 146 GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0); 147 148 int stageMask = paint.getActiveStageMask(); 149 if (stageMask) { 150 for (int i = 0; i < GrPaint::kTotalStages; ++i) { 151 if ((1 << i) & stageMask) { 152 fVertexLayout |= 153 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i); 154 GrAssert(i != kGlyphMaskStage); 155 } 156 } 157 } 158} 159 160GrTextContext::~GrTextContext() { 161 this->flushGlyphs(); 162 fContext->setMatrix(fOrigViewMatrix); 163} 164 165void GrTextContext::flush() { 166 this->flushGlyphs(); 167} 168 169static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b, 170 int stride) { 171 v[0 * stride].setI(l, t); 172 v[1 * stride].setI(l, b); 173 v[2 * stride].setI(r, b); 174 v[3 * stride].setI(r, t); 175} 176 177void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 178 GrFixed vx, GrFixed vy, 179 GrFontScaler* scaler) { 180 if (NULL == fStrike) { 181 fStrike = fContext->getFontCache()->getStrike(scaler); 182 } 183 184 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 185 if (NULL == glyph || glyph->fBounds.isEmpty()) { 186 return; 187 } 188 189 vx += GrIntToFixed(glyph->fBounds.fLeft); 190 vy += GrIntToFixed(glyph->fBounds.fTop); 191 192 // keep them as ints until we've done the clip-test 193 GrFixed width = glyph->fBounds.width(); 194 GrFixed height = glyph->fBounds.height(); 195 196 // check if we clipped out 197 if (true || NULL == glyph->fAtlas) { 198 int x = vx >> 16; 199 int y = vy >> 16; 200 if (fClipRect.quickReject(x, y, x + width, y + height)) { 201// Gr_clz(3); // so we can set a break-point in the debugger 202 return; 203 } 204 } 205 206 if (NULL == glyph->fAtlas) { 207 if (fStrike->getGlyphAtlas(glyph, scaler)) { 208 goto HAS_ATLAS; 209 } 210 211 // before we purge the cache, we must flush any accumulated draws 212 this->flushGlyphs(); 213 fContext->flushText(); 214 215 // try to purge 216 fContext->getFontCache()->purgeExceptFor(fStrike); 217 if (fStrike->getGlyphAtlas(glyph, scaler)) { 218 goto HAS_ATLAS; 219 } 220 221 if (NULL == glyph->fPath) { 222 GrPath* path = new GrPath; 223 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 224 // flag the glyph as being dead? 225 delete path; 226 return; 227 } 228 glyph->fPath = path; 229 } 230 231 GrPoint translate; 232 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), 233 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); 234 fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill, 235 &translate); 236 return; 237 } 238 239HAS_ATLAS: 240 GrAssert(glyph->fAtlas); 241 242 // now promote them to fixed 243 width = GrIntToFixed(width); 244 height = GrIntToFixed(height); 245 246 GrTexture* texture = glyph->fAtlas->texture(); 247 GrAssert(texture); 248 249 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 250 this->flushGlyphs(); 251 fCurrTexture = texture; 252 fCurrTexture->ref(); 253 } 254 255 if (NULL == fVertices) { 256 // If we need to reserve vertices allow the draw target to suggest 257 // a number of verts to reserve and whether to perform a flush. 258 fMaxVertices = kMinRequestedVerts; 259 bool flush = fDrawTarget->geometryHints(fVertexLayout, 260 &fMaxVertices, 261 NULL); 262 if (flush) { 263 this->flushGlyphs(); 264 fContext->flushText(); 265 fDrawTarget = fContext->getTextTarget(fPaint); 266 fMaxVertices = kDefaultRequestedVerts; 267 // ignore return, no point in flushing again. 268 fDrawTarget->geometryHints(fVertexLayout, 269 &fMaxVertices, 270 NULL); 271 } 272 273 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 274 if (fMaxVertices < kMinRequestedVerts) { 275 fMaxVertices = kDefaultRequestedVerts; 276 } else if (fMaxVertices > maxQuadVertices) { 277 // don't exceed the limit of the index buffer 278 fMaxVertices = maxQuadVertices; 279 } 280 bool success = fDrawTarget->reserveVertexSpace(fVertexLayout, 281 fMaxVertices, 282 GrTCast<void**>(&fVertices)); 283 GrAlwaysAssert(success); 284 } 285 286 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX); 287 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY); 288 289#if GR_GL_TEXT_TEXTURE_NORMALIZED 290 int x = vx >> 16; 291 int y = vy >> 16; 292 int w = width >> 16; 293 int h = height >> 16; 294 295 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2); 296 setRectFan(&fVertices[2*fCurrVertex+1], 297 texture->normalizeFixedX(tx), 298 texture->normalizeFixedY(ty), 299 texture->normalizeFixedX(tx + width), 300 texture->normalizeFixedY(ty + height), 301 2); 302#else 303 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height, 304 2 * sizeof(GrGpuTextVertex)); 305 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx), 306 texture->normalizeFixedY(ty), 307 texture->normalizeFixedX(tx + width), 308 texture->normalizeFixedY(ty + height), 309 2 * sizeof(GrGpuTextVertex)); 310#endif 311 fCurrVertex += 4; 312} 313 314 315