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