GrTextContext.cpp revision 375ff85e96cf0f8438ea0b11be67e85474e42c29
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 kGlyphMaskStage = GrPaint::kTotalStages, 23}; 24 25void GrTextContext::flushGlyphs() { 26 if (fCurrVertex > 0) { 27 GrDrawState* drawState = fDrawTarget->drawState(); 28 // setup our sampler state for our text texture/atlas 29 GrSamplerState::Filter filter; 30 if (fExtMatrix.isIdentity()) { 31 filter = GrSamplerState::kNearest_Filter; 32 } else { 33 filter = GrSamplerState::kBilinear_Filter; 34 } 35 drawState->sampler(kGlyphMaskStage)->reset( 36 GrSamplerState::kRepeat_WrapMode,filter); 37 38 GrAssert(GrIsALIGN4(fCurrVertex)); 39 GrAssert(fCurrTexture); 40 drawState->setTexture(kGlyphMaskStage, fCurrTexture); 41 42 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 43 if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff || 44 kISA_GrBlendCoeff != fPaint.fDstBlendCoeff || 45 fPaint.hasTexture()) { 46 GrPrintf("LCD Text will not draw correctly.\n"); 47 } 48 // setup blend so that we get mask * paintColor + (1-mask)*dstColor 49 drawState->setBlendConstant(fPaint.fColor); 50 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 51 // don't modulate by the paint's color in the frag since we're 52 // already doing it via the blend const. 53 drawState->setColor(0xffffffff); 54 } else { 55 // set back to normal in case we took LCD path previously. 56 drawState->setBlendFunc(fPaint.fSrcBlendCoeff, 57 fPaint.fDstBlendCoeff); 58 drawState->setColor(fPaint.fColor); 59 } 60 61 int nGlyphs = fCurrVertex / 4; 62 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 63 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 64 nGlyphs, 65 4, 6); 66 fDrawTarget->resetVertexSource(); 67 fVertices = NULL; 68 fMaxVertices = 0; 69 fCurrVertex = 0; 70 GrSafeSetNull(fCurrTexture); 71 drawState->setTexture(kGlyphMaskStage, NULL); 72 } 73} 74 75GrTextContext::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.reset(); 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 (fPaint.isTextureStageEnabled(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 (fPaint.isMaskStageEnabled(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 147GrTextContext::~GrTextContext() { 148 this->flushGlyphs(); 149 if (fDrawTarget) { 150 fDrawTarget->drawState()->disableStages(); 151 } 152 fContext->setMatrix(fOrigViewMatrix); 153} 154 155void GrTextContext::flush() { 156 this->flushGlyphs(); 157} 158 159static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b, 160 int stride) { 161 v[0 * stride].setI(l, t); 162 v[1 * stride].setI(l, b); 163 v[2 * stride].setI(r, b); 164 v[3 * stride].setI(r, t); 165} 166 167void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 168 GrFixed vx, GrFixed vy, 169 GrFontScaler* scaler) { 170 if (NULL == fStrike) { 171 fStrike = fContext->getFontCache()->getStrike(scaler); 172 } 173 174 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 175 if (NULL == glyph || glyph->fBounds.isEmpty()) { 176 return; 177 } 178 179 vx += GrIntToFixed(glyph->fBounds.fLeft); 180 vy += GrIntToFixed(glyph->fBounds.fTop); 181 182 // keep them as ints until we've done the clip-test 183 GrFixed width = glyph->fBounds.width(); 184 GrFixed height = glyph->fBounds.height(); 185 186 // check if we clipped out 187 if (true || NULL == glyph->fAtlas) { 188 int x = vx >> 16; 189 int y = vy >> 16; 190 if (fClipRect.quickReject(x, y, x + width, y + height)) { 191// Gr_clz(3); // so we can set a break-point in the debugger 192 return; 193 } 194 } 195 196 if (NULL == glyph->fAtlas) { 197 if (fStrike->getGlyphAtlas(glyph, scaler)) { 198 goto HAS_ATLAS; 199 } 200 201 // before we purge the cache, we must flush any accumulated draws 202 this->flushGlyphs(); 203 fContext->flush(); 204 205 // try to purge 206 fContext->getFontCache()->purgeExceptFor(fStrike); 207 if (fStrike->getGlyphAtlas(glyph, scaler)) { 208 goto HAS_ATLAS; 209 } 210 211 if (NULL == glyph->fPath) { 212 SkPath* path = new SkPath; 213 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 214 // flag the glyph as being dead? 215 delete path; 216 return; 217 } 218 glyph->fPath = path; 219 } 220 221 GrPoint translate; 222 translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), 223 GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); 224 fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill, 225 &translate); 226 return; 227 } 228 229HAS_ATLAS: 230 GrAssert(glyph->fAtlas); 231 232 // now promote them to fixed 233 width = GrIntToFixed(width); 234 height = GrIntToFixed(height); 235 236 GrTexture* texture = glyph->fAtlas->texture(); 237 GrAssert(texture); 238 239 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 240 this->flushGlyphs(); 241 fCurrTexture = texture; 242 fCurrTexture->ref(); 243 } 244 245 if (NULL == fVertices) { 246 // If we need to reserve vertices allow the draw target to suggest 247 // a number of verts to reserve and whether to perform a flush. 248 fMaxVertices = kMinRequestedVerts; 249 bool flush = fDrawTarget->geometryHints(fVertexLayout, 250 &fMaxVertices, 251 NULL); 252 if (flush) { 253 this->flushGlyphs(); 254 fContext->flush(); 255 fDrawTarget = fContext->getTextTarget(fPaint); 256 fMaxVertices = kDefaultRequestedVerts; 257 // ignore return, no point in flushing again. 258 fDrawTarget->geometryHints(fVertexLayout, 259 &fMaxVertices, 260 NULL); 261 } 262 263 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 264 if (fMaxVertices < kMinRequestedVerts) { 265 fMaxVertices = kDefaultRequestedVerts; 266 } else if (fMaxVertices > maxQuadVertices) { 267 // don't exceed the limit of the index buffer 268 fMaxVertices = maxQuadVertices; 269 } 270 bool success = fDrawTarget->reserveVertexAndIndexSpace( 271 fVertexLayout, 272 fMaxVertices, 273 0, 274 GrTCast<void**>(&fVertices), 275 NULL); 276 GrAlwaysAssert(success); 277 } 278 279 GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX); 280 GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY); 281 282#if GR_TEXT_SCALAR_IS_USHORT 283 int x = vx >> 16; 284 int y = vy >> 16; 285 int w = width >> 16; 286 int h = height >> 16; 287 288 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2); 289 setRectFan(&fVertices[2*fCurrVertex+1], 290 texture->normalizeFixedX(tx), 291 texture->normalizeFixedY(ty), 292 texture->normalizeFixedX(tx + width), 293 texture->normalizeFixedY(ty + height), 294 2); 295#else 296 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height, 297 2 * sizeof(GrGpuTextVertex)); 298 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx), 299 texture->normalizeFixedY(ty), 300 texture->normalizeFixedX(tx + width), 301 texture->normalizeFixedY(ty + height), 302 2 * sizeof(GrGpuTextVertex)); 303#endif 304 fCurrVertex += 4; 305} 306 307