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