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