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