GrTextContext.cpp revision c377baf406996aed18d82d328029c82dbc3b8dda
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 "GrGpuVertex.h"
16#include "GrIndexBuffer.h"
17#include "GrTextStrike.h"
18#include "GrTextStrike_impl.h"
19#include "SkPath.h"
20
21enum {
22    kGlyphMaskStage = GrPaint::kTotalStages,
23};
24
25void GrTextContext::flushGlyphs() {
26    if (fCurrVertex > 0) {
27        GrDrawState* drawState = fDrawTarget->drawState();
28        // setup our sampler state for our text texture/atlas
29        GrSamplerState::Filter filter;
30        if (fExtMatrix.isIdentity()) {
31            filter = GrSamplerState::kNearest_Filter;
32        } else {
33            filter = GrSamplerState::kBilinear_Filter;
34        }
35        drawState->sampler(kGlyphMaskStage)->reset(
36            GrSamplerState::kRepeat_WrapMode,filter);
37
38        GrAssert(GrIsALIGN4(fCurrVertex));
39        GrAssert(fCurrTexture);
40        drawState->setTexture(kGlyphMaskStage, fCurrTexture);
41
42        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
43            if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
44                kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
45                fPaint.hasTexture()) {
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.fColor);
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.fSrcBlendCoeff,
57                                    fPaint.fDstBlendCoeff);
58            drawState->setColor(fPaint.fColor);
59        }
60
61        int nGlyphs = fCurrVertex / 4;
62        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
63        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
64                                          nGlyphs,
65                                          4, 6);
66        fDrawTarget->resetVertexSource();
67        fVertices = NULL;
68        fMaxVertices = 0;
69        fCurrVertex = 0;
70        GrSafeSetNull(fCurrTexture);
71        drawState->setTexture(kGlyphMaskStage, 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.reset();
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 (fPaint.isTextureStageEnabled(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 (fPaint.isMaskStageEnabled(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
147GrTextContext::~GrTextContext() {
148    this->flushGlyphs();
149    if (fDrawTarget) {
150        fDrawTarget->drawState()->disableStages();
151    }
152    fContext->setMatrix(fOrigViewMatrix);
153}
154
155void GrTextContext::flush() {
156    this->flushGlyphs();
157}
158
159static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
160                              int stride) {
161    v[0 * stride].setI(l, t);
162    v[1 * stride].setI(l, b);
163    v[2 * stride].setI(r, b);
164    v[3 * stride].setI(r, t);
165}
166
167void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
168                                    GrFixed vx, GrFixed vy,
169                                    GrFontScaler* scaler) {
170    if (NULL == fStrike) {
171        fStrike = fContext->getFontCache()->getStrike(scaler);
172    }
173
174    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
175    if (NULL == glyph || glyph->fBounds.isEmpty()) {
176        return;
177    }
178
179    vx += GrIntToFixed(glyph->fBounds.fLeft);
180    vy += GrIntToFixed(glyph->fBounds.fTop);
181
182    // keep them as ints until we've done the clip-test
183    GrFixed width = glyph->fBounds.width();
184    GrFixed height = glyph->fBounds.height();
185
186    // check if we clipped out
187    if (true || NULL == glyph->fAtlas) {
188        int x = vx >> 16;
189        int y = vy >> 16;
190        if (fClipRect.quickReject(x, y, x + width, y + height)) {
191//            Gr_clz(3);    // so we can set a break-point in the debugger
192            return;
193        }
194    }
195
196    if (NULL == glyph->fAtlas) {
197        if (fStrike->getGlyphAtlas(glyph, scaler)) {
198            goto HAS_ATLAS;
199        }
200
201        // before we purge the cache, we must flush any accumulated draws
202        this->flushGlyphs();
203        fContext->flush();
204
205        // try to purge
206        fContext->getFontCache()->purgeExceptFor(fStrike);
207        if (fStrike->getGlyphAtlas(glyph, scaler)) {
208            goto HAS_ATLAS;
209        }
210
211        if (NULL == glyph->fPath) {
212            SkPath* path = SkNEW(SkPath);
213            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
214                // flag the glyph as being dead?
215                delete path;
216                return;
217            }
218            glyph->fPath = path;
219        }
220
221        GrPoint translate;
222        translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
223                      GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
224        fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
225                           &translate);
226        return;
227    }
228
229HAS_ATLAS:
230    GrAssert(glyph->fAtlas);
231
232    // now promote them to fixed
233    width = GrIntToFixed(width);
234    height = GrIntToFixed(height);
235
236    GrTexture* texture = glyph->fAtlas->texture();
237    GrAssert(texture);
238
239    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
240        this->flushGlyphs();
241        fCurrTexture = texture;
242        fCurrTexture->ref();
243    }
244
245    if (NULL == fVertices) {
246        // If we need to reserve vertices allow the draw target to suggest
247        // a number of verts to reserve and whether to perform a flush.
248        fMaxVertices = kMinRequestedVerts;
249        bool flush = fDrawTarget->geometryHints(fVertexLayout,
250                                                &fMaxVertices,
251                                                NULL);
252        if (flush) {
253            this->flushGlyphs();
254            fContext->flush();
255            fDrawTarget = fContext->getTextTarget(fPaint);
256            fMaxVertices = kDefaultRequestedVerts;
257            // ignore return, no point in flushing again.
258            fDrawTarget->geometryHints(fVertexLayout,
259                                       &fMaxVertices,
260                                       NULL);
261        }
262
263        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
264        if (fMaxVertices < kMinRequestedVerts) {
265            fMaxVertices = kDefaultRequestedVerts;
266        } else if (fMaxVertices > maxQuadVertices) {
267            // don't exceed the limit of the index buffer
268            fMaxVertices = maxQuadVertices;
269        }
270        bool success = fDrawTarget->reserveVertexAndIndexSpace(
271                                                   fVertexLayout,
272                                                   fMaxVertices,
273                                                   0,
274                                                   GrTCast<void**>(&fVertices),
275                                                   NULL);
276        GrAlwaysAssert(success);
277    }
278
279    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
280    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
281
282#if GR_TEXT_SCALAR_IS_USHORT
283    int x = vx >> 16;
284    int y = vy >> 16;
285    int w = width >> 16;
286    int h = height >> 16;
287
288    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
289    setRectFan(&fVertices[2*fCurrVertex+1],
290               texture->normalizeFixedX(tx),
291               texture->normalizeFixedY(ty),
292               texture->normalizeFixedX(tx + width),
293               texture->normalizeFixedY(ty + height),
294               2);
295#else
296    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
297                                        2 * sizeof(GrGpuTextVertex));
298    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
299                                          texture->normalizeFixedY(ty),
300                                          texture->normalizeFixedX(tx + width),
301                                          texture->normalizeFixedY(ty + height),
302                                          2 * sizeof(GrGpuTextVertex));
303#endif
304    fCurrVertex += 4;
305}
306
307