GrTextContext.cpp revision 8f9cbd62ec108d410b91155dcf6a4789c641246f
1
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10
11#include "GrTextContext.h"
12#include "GrAtlas.h"
13#include "GrContext.h"
14#include "GrTextStrike.h"
15#include "GrTextStrike_impl.h"
16#include "GrFontScaler.h"
17#include "GrIndexBuffer.h"
18#include "GrGpuVertex.h"
19#include "GrDrawTarget.h"
20
21enum {
22    kGlyphMaskStage = GrPaint::kTotalStages,
23};
24
25void GrTextContext::flushGlyphs() {
26    if (fCurrVertex > 0) {
27        GrDrawTarget::AutoStateRestore asr(fDrawTarget);
28        GrDrawState* drawState = fDrawTarget->drawState();
29        // setup our sampler state for our text texture/atlas
30        GrSamplerState::Filter filter;
31        if (fExtMatrix.isIdentity()) {
32            filter = GrSamplerState::kNearest_Filter;
33        } else {
34            filter = GrSamplerState::kBilinear_Filter;
35        }
36        GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
37                               filter);
38        drawState->setSampler(kGlyphMaskStage, sampler);
39
40        GrAssert(GrIsALIGN4(fCurrVertex));
41        int nIndices = fCurrVertex + (fCurrVertex >> 1);
42        GrAssert(fCurrTexture);
43        drawState->setTexture(kGlyphMaskStage, fCurrTexture);
44
45        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
46            if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
47                kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
48                fPaint.hasTexture()) {
49                GrPrintf("LCD Text will not draw correctly.\n");
50            }
51            // setup blend so that we get mask * paintColor + (1-mask)*dstColor
52            drawState->setBlendConstant(fPaint.fColor);
53            fDrawTarget->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
54            // don't modulate by the paint's color in the frag since we're
55            // already doing it via the blend const.
56            drawState->setColor(0xffffffff);
57        } else {
58            // set back to normal in case we took LCD path previously.
59            fDrawTarget->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
60            drawState->setColor(fPaint.fColor);
61        }
62
63        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
64
65        fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
66                                 0, 0, fCurrVertex, nIndices);
67        fDrawTarget->resetVertexSource();
68        fVertices = NULL;
69        fMaxVertices = 0;
70        fCurrVertex = 0;
71        fCurrTexture->unref();
72        fCurrTexture = NULL;
73    }
74}
75
76GrTextContext::GrTextContext(GrContext* context,
77                             const GrPaint& paint,
78                             const GrMatrix* extMatrix) : fPaint(paint) {
79    fContext = context;
80    fStrike = NULL;
81
82    fCurrTexture = NULL;
83    fCurrVertex = 0;
84
85    if (NULL != extMatrix) {
86        fExtMatrix = *extMatrix;
87    } else {
88        fExtMatrix = GrMatrix::I();
89    }
90    if (context->getClip().hasConservativeBounds()) {
91        if (!fExtMatrix.isIdentity()) {
92            GrMatrix inverse;
93            GrRect r = context->getClip().getConservativeBounds();
94            if (fExtMatrix.invert(&inverse)) {
95                inverse.mapRect(&r);
96                r.roundOut(&fClipRect);
97            }
98        } else {
99            context->getClip().getConservativeBounds().roundOut(&fClipRect);
100        }
101    } else {
102        fClipRect.setLargest();
103    }
104
105    // save the context's original matrix off and restore in destructor
106    // this must be done before getTextTarget.
107    fOrigViewMatrix = fContext->getMatrix();
108    fContext->setMatrix(fExtMatrix);
109
110    /*
111     We need to call preConcatMatrix with our viewmatrix's inverse, for each
112     texture and mask in the paint. However, computing the inverse can be
113     expensive, and its possible we may not have any textures or masks, so these
114     two loops are written such that we only compute the inverse (once) if we
115     need it. We do this on our copy of the paint rather than directly on the
116     draw target because we re-provide the paint to the context when we have
117     to flush our glyphs or draw a glyph as a path midstream.
118    */
119    bool invVMComputed = false;
120    GrMatrix invVM;
121    for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
122        if (NULL != fPaint.getTexture(t)) {
123            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
124                invVMComputed = true;
125                fPaint.textureSampler(t)->preConcatMatrix(invVM);
126            }
127        }
128    }
129    for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
130        if (NULL != fPaint.getMask(m)) {
131            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
132                invVMComputed = true;
133                fPaint.maskSampler(m)->preConcatMatrix(invVM);
134            }
135        }
136    }
137
138    fDrawTarget = fContext->getTextTarget(fPaint);
139
140    fVertices = NULL;
141    fMaxVertices = 0;
142
143    fVertexLayout =
144        GrDrawTarget::kTextFormat_VertexLayoutBit |
145        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
146
147    int stageMask = paint.getActiveStageMask();
148    if (stageMask) {
149        for (int i = 0; i < GrPaint::kTotalStages; ++i) {
150            if ((1 << i) & stageMask) {
151                fVertexLayout |=
152                    GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
153                GrAssert(i != kGlyphMaskStage);
154            }
155        }
156    }
157}
158
159GrTextContext::~GrTextContext() {
160    this->flushGlyphs();
161    fContext->setMatrix(fOrigViewMatrix);
162}
163
164void GrTextContext::flush() {
165    this->flushGlyphs();
166}
167
168static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
169                              int stride) {
170    v[0 * stride].setI(l, t);
171    v[1 * stride].setI(l, b);
172    v[2 * stride].setI(r, b);
173    v[3 * stride].setI(r, t);
174}
175
176void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
177                                    GrFixed vx, GrFixed vy,
178                                    GrFontScaler* scaler) {
179    if (NULL == fStrike) {
180        fStrike = fContext->getFontCache()->getStrike(scaler);
181    }
182
183    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
184    if (NULL == glyph || glyph->fBounds.isEmpty()) {
185        return;
186    }
187
188    vx += GrIntToFixed(glyph->fBounds.fLeft);
189    vy += GrIntToFixed(glyph->fBounds.fTop);
190
191    // keep them as ints until we've done the clip-test
192    GrFixed width = glyph->fBounds.width();
193    GrFixed height = glyph->fBounds.height();
194
195    // check if we clipped out
196    if (true || NULL == glyph->fAtlas) {
197        int x = vx >> 16;
198        int y = vy >> 16;
199        if (fClipRect.quickReject(x, y, x + width, y + height)) {
200//            Gr_clz(3);    // so we can set a break-point in the debugger
201            return;
202        }
203    }
204
205    if (NULL == glyph->fAtlas) {
206        if (fStrike->getGlyphAtlas(glyph, scaler)) {
207            goto HAS_ATLAS;
208        }
209
210        // before we purge the cache, we must flush any accumulated draws
211        this->flushGlyphs();
212        fContext->flushText();
213
214        // try to purge
215        fContext->getFontCache()->purgeExceptFor(fStrike);
216        if (fStrike->getGlyphAtlas(glyph, scaler)) {
217            goto HAS_ATLAS;
218        }
219
220        if (NULL == glyph->fPath) {
221            GrPath* path = new GrPath;
222            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
223                // flag the glyph as being dead?
224                delete path;
225                return;
226            }
227            glyph->fPath = path;
228        }
229
230        GrPoint translate;
231        translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
232                      GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
233        fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
234                           &translate);
235        return;
236    }
237
238HAS_ATLAS:
239    GrAssert(glyph->fAtlas);
240
241    // now promote them to fixed
242    width = GrIntToFixed(width);
243    height = GrIntToFixed(height);
244
245    GrTexture* texture = glyph->fAtlas->texture();
246    GrAssert(texture);
247
248    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
249        this->flushGlyphs();
250        fCurrTexture = texture;
251        fCurrTexture->ref();
252    }
253
254    if (NULL == fVertices) {
255        // If we need to reserve vertices allow the draw target to suggest
256        // a number of verts to reserve and whether to perform a flush.
257        fMaxVertices = kMinRequestedVerts;
258        bool flush = fDrawTarget->geometryHints(fVertexLayout,
259                                               &fMaxVertices,
260                                               NULL);
261        if (flush) {
262            this->flushGlyphs();
263            fContext->flushText();
264            fDrawTarget = fContext->getTextTarget(fPaint);
265            fMaxVertices = kDefaultRequestedVerts;
266            // ignore return, no point in flushing again.
267            fDrawTarget->geometryHints(fVertexLayout,
268                                       &fMaxVertices,
269                                       NULL);
270        }
271
272        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
273        if (fMaxVertices < kMinRequestedVerts) {
274            fMaxVertices = kDefaultRequestedVerts;
275        } else if (fMaxVertices > maxQuadVertices) {
276            // don't exceed the limit of the index buffer
277            fMaxVertices = maxQuadVertices;
278        }
279        bool success = fDrawTarget->reserveVertexSpace(fVertexLayout,
280                                                   fMaxVertices,
281                                                   GrTCast<void**>(&fVertices));
282        GrAlwaysAssert(success);
283    }
284
285    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
286    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
287
288#if GR_GL_TEXT_TEXTURE_NORMALIZED
289    int x = vx >> 16;
290    int y = vy >> 16;
291    int w = width >> 16;
292    int h = height >> 16;
293
294    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
295    setRectFan(&fVertices[2*fCurrVertex+1],
296               texture->normalizeFixedX(tx),
297               texture->normalizeFixedY(ty),
298               texture->normalizeFixedX(tx + width),
299               texture->normalizeFixedY(ty + height),
300               2);
301#else
302    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
303                                        2 * sizeof(GrGpuTextVertex));
304    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
305                                          texture->normalizeFixedY(ty),
306                                          texture->normalizeFixedX(tx + width),
307                                          texture->normalizeFixedY(ty + height),
308                                          2 * sizeof(GrGpuTextVertex));
309#endif
310    fCurrVertex += 4;
311}
312
313
314