GrTextContext.cpp revision 3cbaa2d4da8bc39a99bf3afaaab43cc6dc481723
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
23    kGlyphMaskStage = GrPaint::kTotalStages,
24};
25
26void GrTextContext::flushGlyphs() {
27    if (NULL == fDrawTarget) {
28        return;
29    }
30    GrDrawState* drawState = fDrawTarget->drawState();
31    if (fCurrVertex > 0) {
32        // setup our sampler state for our text texture/atlas
33        drawState->sampler(kGlyphMaskStage)->reset();
34
35        GrAssert(GrIsALIGN4(fCurrVertex));
36        GrAssert(fCurrTexture);
37        GrTextureParams params(SkShader::kRepeat_TileMode, false);
38        drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, 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    // save the context's original matrix off and restore in destructor
93    // this must be done before getTextTarget.
94    fOrigViewMatrix = fContext->getMatrix();
95    fContext->setIdentityMatrix();
96
97    /*
98     We need to call preConcatMatrix with our viewmatrix's inverse, for each
99     texture and mask in the paint. However, computing the inverse can be
100     expensive, and its possible we may not have any textures or masks, so these
101     two loops are written such that we only compute the inverse (once) if we
102     need it. We do this on our copy of the paint rather than directly on the
103     draw target because we re-provide the paint to the context when we have
104     to flush our glyphs or draw a glyph as a path midstream.
105    */
106    bool invVMComputed = false;
107    GrMatrix invVM;
108    for (int t = 0; t < GrPaint::kMaxColorStages; ++t) {
109        if (fPaint.isColorStageEnabled(t)) {
110            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
111                invVMComputed = true;
112                fPaint.colorSampler(t)->preConcatMatrix(invVM);
113            }
114        }
115    }
116    for (int m = 0; m < GrPaint::kMaxCoverageStages; ++m) {
117        if (fPaint.isCoverageStageEnabled(m)) {
118            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
119                invVMComputed = true;
120                fPaint.coverageSampler(m)->preConcatMatrix(invVM);
121            }
122        }
123    }
124
125    fDrawTarget = NULL;
126
127    fVertices = NULL;
128    fMaxVertices = 0;
129
130    fVertexLayout =
131        GrDrawTarget::kTextFormat_VertexLayoutBit |
132        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
133}
134
135GrTextContext::~GrTextContext() {
136    this->flushGlyphs();
137    if (fDrawTarget) {
138        fDrawTarget->drawState()->disableStages();
139    }
140    fContext->setMatrix(fOrigViewMatrix);
141}
142
143void GrTextContext::flush() {
144    this->flushGlyphs();
145}
146
147static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
148                              int stride) {
149    v[0 * stride].setI(l, t);
150    v[1 * stride].setI(l, b);
151    v[2 * stride].setI(r, b);
152    v[3 * stride].setI(r, t);
153}
154
155void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
156                                    GrFixed vx, GrFixed vy,
157                                    GrFontScaler* scaler) {
158    if (NULL == fStrike) {
159        fStrike = fContext->getFontCache()->getStrike(scaler);
160    }
161
162    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
163    if (NULL == glyph || glyph->fBounds.isEmpty()) {
164        return;
165    }
166
167    vx += GrIntToFixed(glyph->fBounds.fLeft);
168    vy += GrIntToFixed(glyph->fBounds.fTop);
169
170    // keep them as ints until we've done the clip-test
171    GrFixed width = glyph->fBounds.width();
172    GrFixed height = glyph->fBounds.height();
173
174    // check if we clipped out
175    if (true || NULL == glyph->fAtlas) {
176        int x = vx >> 16;
177        int y = vy >> 16;
178        if (fClipRect.quickReject(x, y, x + width, y + height)) {
179//            Gr_clz(3);    // so we can set a break-point in the debugger
180            return;
181        }
182    }
183
184    if (NULL == glyph->fAtlas) {
185        if (fStrike->getGlyphAtlas(glyph, scaler)) {
186            goto HAS_ATLAS;
187        }
188
189        // before we purge the cache, we must flush any accumulated draws
190        this->flushGlyphs();
191        fContext->flush();
192
193        // try to purge
194        fContext->getFontCache()->purgeExceptFor(fStrike);
195        if (fStrike->getGlyphAtlas(glyph, scaler)) {
196            goto HAS_ATLAS;
197        }
198
199        if (NULL == glyph->fPath) {
200            SkPath* path = SkNEW(SkPath);
201            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
202                // flag the glyph as being dead?
203                delete path;
204                return;
205            }
206            glyph->fPath = path;
207        }
208
209        GrContext::AutoMatrix am;
210        GrMatrix translate;
211        translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
212                               GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
213        GrPaint tmpPaint(fPaint);
214        am.setPreConcat(fContext, translate, &tmpPaint);
215        fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill);
216        return;
217    }
218
219HAS_ATLAS:
220    GrAssert(glyph->fAtlas);
221
222    // now promote them to fixed
223    width = GrIntToFixed(width);
224    height = GrIntToFixed(height);
225
226    GrTexture* texture = glyph->fAtlas->texture();
227    GrAssert(texture);
228
229    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
230        this->flushGlyphs();
231        fCurrTexture = texture;
232        fCurrTexture->ref();
233    }
234
235    if (NULL == fVertices) {
236        // If we need to reserve vertices allow the draw target to suggest
237        // a number of verts to reserve and whether to perform a flush.
238        fMaxVertices = kMinRequestedVerts;
239        bool flush = (NULL != fDrawTarget) &&
240                     fDrawTarget->geometryHints(fVertexLayout,
241                                                &fMaxVertices,
242                                                NULL);
243        if (flush) {
244            this->flushGlyphs();
245            fContext->flush();
246        }
247        fDrawTarget = fContext->getTextTarget(fPaint);
248        fMaxVertices = kDefaultRequestedVerts;
249        // ignore return, no point in flushing again.
250        fDrawTarget->geometryHints(fVertexLayout,
251                                   &fMaxVertices,
252                                   NULL);
253
254        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
255        if (fMaxVertices < kMinRequestedVerts) {
256            fMaxVertices = kDefaultRequestedVerts;
257        } else if (fMaxVertices > maxQuadVertices) {
258            // don't exceed the limit of the index buffer
259            fMaxVertices = maxQuadVertices;
260        }
261        bool success = fDrawTarget->reserveVertexAndIndexSpace(
262                                                   fVertexLayout,
263                                                   fMaxVertices,
264                                                   0,
265                                                   GrTCast<void**>(&fVertices),
266                                                   NULL);
267        GrAlwaysAssert(success);
268    }
269
270    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
271    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
272
273#if GR_TEXT_SCALAR_IS_USHORT
274    int x = vx >> 16;
275    int y = vy >> 16;
276    int w = width >> 16;
277    int h = height >> 16;
278
279    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
280    setRectFan(&fVertices[2*fCurrVertex+1],
281               texture->normalizeFixedX(tx),
282               texture->normalizeFixedY(ty),
283               texture->normalizeFixedX(tx + width),
284               texture->normalizeFixedY(ty + height),
285               2);
286#else
287    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
288                                        2 * sizeof(GrGpuTextVertex));
289    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
290                                          texture->normalizeFixedY(ty),
291                                          texture->normalizeFixedX(tx + width),
292                                          texture->normalizeFixedY(ty + height),
293                                          2 * sizeof(GrGpuTextVertex));
294#endif
295    fCurrVertex += 4;
296}
297
298