GrTextContext.cpp revision f6de475e5cbd143f348ff7738919e397b7fe7f57
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 "GrIndexBuffer.h"
16#include "GrTextStrike.h"
17#include "GrTextStrike_impl.h"
18#include "SkPath.h"
19#include "SkStrokeRec.h"
20
21static const int kGlyphCoordsAttributeIndex = 1;
22
23void GrTextContext::flushGlyphs() {
24    if (NULL == fDrawTarget) {
25        return;
26    }
27
28    GrDrawState* drawState = fDrawTarget->drawState();
29    GrDrawState::AutoRestoreEffects are(drawState);
30    drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
31
32    if (fCurrVertex > 0) {
33        // setup our sampler state for our text texture/atlas
34        SkASSERT(GrIsALIGN4(fCurrVertex));
35        SkASSERT(fCurrTexture);
36        GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
37
38        // This effect could be stored with one of the cache objects (atlas?)
39        drawState->addCoverageEffect(
40                                GrSimpleTextureEffect::CreateWithCustomCoords(fCurrTexture, params),
41                                kGlyphCoordsAttributeIndex)->unref();
42
43        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
44            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
45                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
46                fPaint.numColorStages()) {
47                GrPrintf("LCD Text will not draw correctly.\n");
48            }
49            // setup blend so that we get mask * paintColor + (1-mask)*dstColor
50            drawState->setBlendConstant(fPaint.getColor());
51            drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
52            // don't modulate by the paint's color in the frag since we're
53            // already doing it via the blend const.
54            drawState->setColor(0xffffffff);
55        } else {
56            // set back to normal in case we took LCD path previously.
57            drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
58            drawState->setColor(fPaint.getColor());
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    }
72}
73
74GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) {
75    fContext = context;
76    fStrike = NULL;
77
78    fCurrTexture = NULL;
79    fCurrVertex = 0;
80
81    const GrClipData* clipData = context->getClip();
82
83    SkRect devConservativeBound;
84    clipData->fClipStack->getConservativeBounds(
85                                     -clipData->fOrigin.fX,
86                                     -clipData->fOrigin.fY,
87                                     context->getRenderTarget()->width(),
88                                     context->getRenderTarget()->height(),
89                                     &devConservativeBound);
90
91    devConservativeBound.roundOut(&fClipRect);
92
93    fAutoMatrix.setIdentity(fContext, &fPaint);
94
95    fDrawTarget = fContext->getTextTarget();
96
97    fVertices = NULL;
98    fMaxVertices = 0;
99}
100
101GrTextContext::~GrTextContext() {
102    this->flushGlyphs();
103}
104
105void GrTextContext::flush() {
106    this->flushGlyphs();
107}
108
109namespace {
110
111// position + texture coord
112extern const GrVertexAttrib gTextVertexAttribs[] = {
113    {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
114    {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
115};
116
117};
118
119void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
120                                    GrFixed vx, GrFixed vy,
121                                    GrFontScaler* scaler) {
122    if (NULL == fDrawTarget) {
123        return;
124    }
125    if (NULL == fStrike) {
126        fStrike = fContext->getFontCache()->getStrike(scaler);
127    }
128
129    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
130    if (NULL == glyph || glyph->fBounds.isEmpty()) {
131        return;
132    }
133
134    vx += SkIntToFixed(glyph->fBounds.fLeft);
135    vy += SkIntToFixed(glyph->fBounds.fTop);
136
137    // keep them as ints until we've done the clip-test
138    GrFixed width = glyph->fBounds.width();
139    GrFixed height = glyph->fBounds.height();
140
141    // check if we clipped out
142    if (true || NULL == glyph->fAtlas) {
143        int x = vx >> 16;
144        int y = vy >> 16;
145        if (fClipRect.quickReject(x, y, x + width, y + height)) {
146//            SkCLZ(3);    // so we can set a break-point in the debugger
147            return;
148        }
149    }
150
151    GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
152    if (NULL == glyph->fAtlas) {
153        if (fStrike->getGlyphAtlas(glyph, scaler, drawToken)) {
154            goto HAS_ATLAS;
155        }
156
157        // try to clear out an unused atlas before we flush
158        fContext->getFontCache()->freeAtlasExceptFor(fStrike);
159        if (fStrike->getGlyphAtlas(glyph, scaler, drawToken)) {
160            goto HAS_ATLAS;
161        }
162
163        // before we purge the cache, we must flush any accumulated draws
164        this->flushGlyphs();
165        fContext->flush();
166
167        // try to purge
168        fContext->getFontCache()->purgeExceptFor(fStrike);
169        // need to use new flush count here
170        if (fStrike->getGlyphAtlas(glyph, scaler, drawToken)) {
171            goto HAS_ATLAS;
172        }
173
174        if (NULL == glyph->fPath) {
175            SkPath* path = SkNEW(SkPath);
176            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
177                // flag the glyph as being dead?
178                delete path;
179                return;
180            }
181            glyph->fPath = path;
182        }
183
184        GrContext::AutoMatrix am;
185        SkMatrix translate;
186        translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
187                               SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
188        GrPaint tmpPaint(fPaint);
189        am.setPreConcat(fContext, translate, &tmpPaint);
190        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
191        fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
192        return;
193    }
194
195HAS_ATLAS:
196    SkASSERT(glyph->fAtlas);
197
198    // now promote them to fixed (TODO: Rethink using fixed pt).
199    width = SkIntToFixed(width);
200    height = SkIntToFixed(height);
201
202    GrTexture* texture = glyph->fAtlas->texture();
203    SkASSERT(texture);
204
205    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
206        this->flushGlyphs();
207        fCurrTexture = texture;
208        fCurrTexture->ref();
209    }
210
211    if (NULL == fVertices) {
212       // If we need to reserve vertices allow the draw target to suggest
213        // a number of verts to reserve and whether to perform a flush.
214        fMaxVertices = kMinRequestedVerts;
215        fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
216            SK_ARRAY_COUNT(gTextVertexAttribs));
217        bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
218        if (flush) {
219            this->flushGlyphs();
220            fContext->flush();
221            fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
222                SK_ARRAY_COUNT(gTextVertexAttribs));
223        }
224        fMaxVertices = kDefaultRequestedVerts;
225        // ignore return, no point in flushing again.
226        fDrawTarget->geometryHints(&fMaxVertices, NULL);
227
228        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
229        if (fMaxVertices < kMinRequestedVerts) {
230            fMaxVertices = kDefaultRequestedVerts;
231        } else if (fMaxVertices > maxQuadVertices) {
232            // don't exceed the limit of the index buffer
233            fMaxVertices = maxQuadVertices;
234        }
235        bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
236                                                               0,
237                                                               GrTCast<void**>(&fVertices),
238                                                               NULL);
239        GrAlwaysAssert(success);
240        SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
241    }
242
243    GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
244    GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
245
246    fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx),
247                                        SkFixedToFloat(vy),
248                                        SkFixedToFloat(vx + width),
249                                        SkFixedToFloat(vy + height),
250                                        2 * sizeof(SkPoint));
251    fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
252                                          SkFixedToFloat(texture->normalizeFixedY(ty)),
253                                          SkFixedToFloat(texture->normalizeFixedX(tx + width)),
254                                          SkFixedToFloat(texture->normalizeFixedY(ty + height)),
255                                          2 * sizeof(SkPoint));
256    fCurrVertex += 4;
257}
258