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