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