GrTextContext.cpp revision ae683921ffda9108147a29da7319c7eee4dc9245
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#include "SkStrokeRec.h" 21 22enum { 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 GrAssert(GrIsALIGN4(fCurrVertex)); 34 GrAssert(fCurrTexture); 35 GrTextureParams params(SkShader::kRepeat_TileMode, false); 36 drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, SkMatrix::I(), params); 37 38 if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { 39 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || 40 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || 41 fPaint.hasColorStage()) { 42 GrPrintf("LCD Text will not draw correctly.\n"); 43 } 44 // setup blend so that we get mask * paintColor + (1-mask)*dstColor 45 drawState->setBlendConstant(fPaint.getColor()); 46 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); 47 // don't modulate by the paint's color in the frag since we're 48 // already doing it via the blend const. 49 drawState->setColor(0xffffffff); 50 } else { 51 // set back to normal in case we took LCD path previously. 52 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); 53 drawState->setColor(fPaint.getColor()); 54 } 55 56 int nGlyphs = fCurrVertex / 4; 57 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); 58 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType, 59 nGlyphs, 60 4, 6); 61 fDrawTarget->resetVertexSource(); 62 fVertices = NULL; 63 fMaxVertices = 0; 64 fCurrVertex = 0; 65 GrSafeSetNull(fCurrTexture); 66 } 67 drawState->disableStages(); 68 fDrawTarget = NULL; 69} 70 71GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) { 72 fContext = context; 73 fStrike = NULL; 74 75 fCurrTexture = NULL; 76 fCurrVertex = 0; 77 78 const GrClipData* clipData = context->getClip(); 79 80 GrRect devConservativeBound; 81 clipData->fClipStack->getConservativeBounds( 82 -clipData->fOrigin.fX, 83 -clipData->fOrigin.fY, 84 context->getRenderTarget()->width(), 85 context->getRenderTarget()->height(), 86 &devConservativeBound); 87 88 devConservativeBound.roundOut(&fClipRect); 89 90 fAutoMatrix.setIdentity(fContext, &fPaint); 91 92 fDrawTarget = NULL; 93 94 fVertices = NULL; 95 fMaxVertices = 0; 96 97 fVertexLayout = 98 GrDrawState::kTextFormat_VertexLayoutBit | 99 GrDrawState::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0); 100} 101 102GrTextContext::~GrTextContext() { 103 this->flushGlyphs(); 104 if (fDrawTarget) { 105 fDrawTarget->drawState()->disableStages(); 106 } 107} 108 109void GrTextContext::flush() { 110 this->flushGlyphs(); 111} 112 113static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b, 114 int stride) { 115 v[0 * stride].setI(l, t); 116 v[1 * stride].setI(l, b); 117 v[2 * stride].setI(r, b); 118 v[3 * stride].setI(r, t); 119} 120 121void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, 122 GrFixed vx, GrFixed vy, 123 GrFontScaler* scaler) { 124 if (NULL == fStrike) { 125 fStrike = fContext->getFontCache()->getStrike(scaler); 126 } 127 128 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); 129 if (NULL == glyph || glyph->fBounds.isEmpty()) { 130 return; 131 } 132 133 vx += SkIntToFixed(glyph->fBounds.fLeft); 134 vy += SkIntToFixed(glyph->fBounds.fTop); 135 136 // keep them as ints until we've done the clip-test 137 GrFixed width = glyph->fBounds.width(); 138 GrFixed height = glyph->fBounds.height(); 139 140 // check if we clipped out 141 if (true || NULL == glyph->fAtlas) { 142 int x = vx >> 16; 143 int y = vy >> 16; 144 if (fClipRect.quickReject(x, y, x + width, y + height)) { 145// SkCLZ(3); // so we can set a break-point in the debugger 146 return; 147 } 148 } 149 150 if (NULL == glyph->fAtlas) { 151 if (fStrike->getGlyphAtlas(glyph, scaler)) { 152 goto HAS_ATLAS; 153 } 154 155 // before we purge the cache, we must flush any accumulated draws 156 this->flushGlyphs(); 157 fContext->flush(); 158 159 // try to purge 160 fContext->getFontCache()->purgeExceptFor(fStrike); 161 if (fStrike->getGlyphAtlas(glyph, scaler)) { 162 goto HAS_ATLAS; 163 } 164 165 if (NULL == glyph->fPath) { 166 SkPath* path = SkNEW(SkPath); 167 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { 168 // flag the glyph as being dead? 169 delete path; 170 return; 171 } 172 glyph->fPath = path; 173 } 174 175 GrContext::AutoMatrix am; 176 SkMatrix translate; 177 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)), 178 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop))); 179 GrPaint tmpPaint(fPaint); 180 am.setPreConcat(fContext, translate, &tmpPaint); 181 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 182 fContext->drawPath(tmpPaint, *glyph->fPath, stroke); 183 return; 184 } 185 186HAS_ATLAS: 187 GrAssert(glyph->fAtlas); 188 189 // now promote them to fixed 190 width = SkIntToFixed(width); 191 height = SkIntToFixed(height); 192 193 GrTexture* texture = glyph->fAtlas->texture(); 194 GrAssert(texture); 195 196 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { 197 this->flushGlyphs(); 198 fCurrTexture = texture; 199 fCurrTexture->ref(); 200 } 201 202 if (NULL == fVertices) { 203 // If we need to reserve vertices allow the draw target to suggest 204 // a number of verts to reserve and whether to perform a flush. 205 fMaxVertices = kMinRequestedVerts; 206 bool flush = false; 207 fDrawTarget = fContext->getTextTarget(fPaint); 208 if (NULL != fDrawTarget) { 209 fDrawTarget->drawState()->setVertexLayout(fVertexLayout); 210 flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); 211 } 212 if (flush) { 213 this->flushGlyphs(); 214 fContext->flush(); 215 // flushGlyphs() will reset fDrawTarget to NULL. 216 fDrawTarget = fContext->getTextTarget(fPaint); 217 fDrawTarget->drawState()->setVertexLayout(fVertexLayout); 218 } 219 fMaxVertices = kDefaultRequestedVerts; 220 // ignore return, no point in flushing again. 221 fDrawTarget->geometryHints(&fMaxVertices, NULL); 222 223 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); 224 if (fMaxVertices < kMinRequestedVerts) { 225 fMaxVertices = kDefaultRequestedVerts; 226 } else if (fMaxVertices > maxQuadVertices) { 227 // don't exceed the limit of the index buffer 228 fMaxVertices = maxQuadVertices; 229 } 230 bool success = fDrawTarget->reserveVertexAndIndexSpace( 231 fMaxVertices, 232 0, 233 GrTCast<void**>(&fVertices), 234 NULL); 235 GrAlwaysAssert(success); 236 } 237 238 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX); 239 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY); 240 241#if GR_TEXT_SCALAR_IS_USHORT 242 int x = vx >> 16; 243 int y = vy >> 16; 244 int w = width >> 16; 245 int h = height >> 16; 246 247 setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2); 248 setRectFan(&fVertices[2*fCurrVertex+1], 249 texture->normalizeFixedX(tx), 250 texture->normalizeFixedY(ty), 251 texture->normalizeFixedX(tx + width), 252 texture->normalizeFixedY(ty + height), 253 2); 254#else 255 fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height, 256 2 * sizeof(GrGpuTextVertex)); 257 fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx), 258 texture->normalizeFixedY(ty), 259 texture->normalizeFixedX(tx + width), 260 texture->normalizeFixedY(ty + height), 261 2 * sizeof(GrGpuTextVertex)); 262#endif 263 fCurrVertex += 4; 264} 265