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