GrTextContext.cpp revision 85983288ba8f4abc1fba4c54da827d2b089472e0
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    fVertexLayout =  GrDrawState::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
97}
98
99GrTextContext::~GrTextContext() {
100    this->flushGlyphs();
101    if (fDrawTarget) {
102        fDrawTarget->drawState()->disableStages();
103    }
104}
105
106void GrTextContext::flush() {
107    this->flushGlyphs();
108}
109
110void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
111                                    GrFixed vx, GrFixed vy,
112                                    GrFontScaler* scaler) {
113    if (NULL == fStrike) {
114        fStrike = fContext->getFontCache()->getStrike(scaler);
115    }
116
117    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
118    if (NULL == glyph || glyph->fBounds.isEmpty()) {
119        return;
120    }
121
122    vx += SkIntToFixed(glyph->fBounds.fLeft);
123    vy += SkIntToFixed(glyph->fBounds.fTop);
124
125    // keep them as ints until we've done the clip-test
126    GrFixed width = glyph->fBounds.width();
127    GrFixed height = glyph->fBounds.height();
128
129    // check if we clipped out
130    if (true || NULL == glyph->fAtlas) {
131        int x = vx >> 16;
132        int y = vy >> 16;
133        if (fClipRect.quickReject(x, y, x + width, y + height)) {
134//            SkCLZ(3);    // so we can set a break-point in the debugger
135            return;
136        }
137    }
138
139    if (NULL == glyph->fAtlas) {
140        if (fStrike->getGlyphAtlas(glyph, scaler)) {
141            goto HAS_ATLAS;
142        }
143
144        // before we purge the cache, we must flush any accumulated draws
145        this->flushGlyphs();
146        fContext->flush();
147
148        // try to purge
149        fContext->getFontCache()->purgeExceptFor(fStrike);
150        if (fStrike->getGlyphAtlas(glyph, scaler)) {
151            goto HAS_ATLAS;
152        }
153
154        if (NULL == glyph->fPath) {
155            SkPath* path = SkNEW(SkPath);
156            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
157                // flag the glyph as being dead?
158                delete path;
159                return;
160            }
161            glyph->fPath = path;
162        }
163
164        GrContext::AutoMatrix am;
165        SkMatrix translate;
166        translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
167                               SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
168        GrPaint tmpPaint(fPaint);
169        am.setPreConcat(fContext, translate, &tmpPaint);
170        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
171        fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
172        return;
173    }
174
175HAS_ATLAS:
176    GrAssert(glyph->fAtlas);
177
178    // now promote them to fixed (TODO: Rethink using fixed pt).
179    width = SkIntToFixed(width);
180    height = SkIntToFixed(height);
181
182    GrTexture* texture = glyph->fAtlas->texture();
183    GrAssert(texture);
184
185    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
186        this->flushGlyphs();
187        fCurrTexture = texture;
188        fCurrTexture->ref();
189    }
190
191    if (NULL == fVertices) {
192        // If we need to reserve vertices allow the draw target to suggest
193        // a number of verts to reserve and whether to perform a flush.
194        fMaxVertices = kMinRequestedVerts;
195        bool flush = false;
196        fDrawTarget = fContext->getTextTarget(fPaint);
197        if (NULL != fDrawTarget) {
198            fDrawTarget->drawState()->setVertexLayout(fVertexLayout);
199            flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
200        }
201        if (flush) {
202            this->flushGlyphs();
203            fContext->flush();
204            // flushGlyphs() will reset fDrawTarget to NULL.
205            fDrawTarget = fContext->getTextTarget(fPaint);
206            fDrawTarget->drawState()->setVertexLayout(fVertexLayout);
207        }
208        fMaxVertices = kDefaultRequestedVerts;
209        // ignore return, no point in flushing again.
210        fDrawTarget->geometryHints(&fMaxVertices, NULL);
211
212        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
213        if (fMaxVertices < kMinRequestedVerts) {
214            fMaxVertices = kDefaultRequestedVerts;
215        } else if (fMaxVertices > maxQuadVertices) {
216            // don't exceed the limit of the index buffer
217            fMaxVertices = maxQuadVertices;
218        }
219        bool success = fDrawTarget->reserveVertexAndIndexSpace(
220                                                   fMaxVertices,
221                                                   0,
222                                                   GrTCast<void**>(&fVertices),
223                                                   NULL);
224        GrAlwaysAssert(success);
225    }
226
227    GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
228    GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
229
230    fVertices[2*fCurrVertex].setRectFan(SkFixedToFloat(vx),
231                                        SkFixedToFloat(vy),
232                                        SkFixedToFloat(vx + width),
233                                        SkFixedToFloat(vy + height),
234                                        2 * sizeof(SkPoint));
235    fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
236                                          SkFixedToFloat(texture->normalizeFixedY(ty)),
237                                          SkFixedToFloat(texture->normalizeFixedX(tx + width)),
238                                          SkFixedToFloat(texture->normalizeFixedY(ty + height)),
239                                          2 * sizeof(SkPoint));
240    fCurrVertex += 4;
241}
242