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