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