GrTextContext.cpp revision 88becf450f015007d785f2b5aa7fe4690e295868
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, !fExtMatrix.isIdentity());
38        drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, params);
39
40        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
41            if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
42                kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
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.fColor);
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.fSrcBlendCoeff,
55                                    fPaint.fDstBlendCoeff);
56            drawState->setColor(fPaint.fColor);
57        }
58
59        int nGlyphs = fCurrVertex / 4;
60        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
61        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
62                                          nGlyphs,
63                                          4, 6);
64        fDrawTarget->resetVertexSource();
65        fVertices = NULL;
66        fMaxVertices = 0;
67        fCurrVertex = 0;
68        GrSafeSetNull(fCurrTexture);
69    }
70    drawState->disableStages();
71    fDrawTarget = NULL;
72}
73
74GrTextContext::GrTextContext(GrContext* context,
75                             const GrPaint& paint,
76                             const GrMatrix* extMatrix) : fPaint(paint) {
77    fContext = context;
78    fStrike = NULL;
79
80    fCurrTexture = NULL;
81    fCurrVertex = 0;
82
83    if (NULL != extMatrix) {
84        fExtMatrix = *extMatrix;
85    } else {
86        fExtMatrix.reset();
87    }
88
89    const GrClipData* clipData = context->getClip();
90
91    GrRect devConservativeBound;
92    clipData->fClipStack->getConservativeBounds(
93                                     -clipData->fOrigin.fX,
94                                     -clipData->fOrigin.fY,
95                                     context->getRenderTarget()->width(),
96                                     context->getRenderTarget()->height(),
97                                     &devConservativeBound);
98
99    if (!fExtMatrix.isIdentity()) {
100        GrMatrix inverse;
101        if (fExtMatrix.invert(&inverse)) {
102            inverse.mapRect(&devConservativeBound);
103        }
104    }
105
106    devConservativeBound.roundOut(&fClipRect);
107
108    // save the context's original matrix off and restore in destructor
109    // this must be done before getTextTarget.
110    fOrigViewMatrix = fContext->getMatrix();
111    fContext->setMatrix(fExtMatrix);
112
113    /*
114     We need to call preConcatMatrix with our viewmatrix's inverse, for each
115     texture and mask in the paint. However, computing the inverse can be
116     expensive, and its possible we may not have any textures or masks, so these
117     two loops are written such that we only compute the inverse (once) if we
118     need it. We do this on our copy of the paint rather than directly on the
119     draw target because we re-provide the paint to the context when we have
120     to flush our glyphs or draw a glyph as a path midstream.
121    */
122    bool invVMComputed = false;
123    GrMatrix invVM;
124    for (int t = 0; t < GrPaint::kMaxColorStages; ++t) {
125        if (fPaint.isColorStageEnabled(t)) {
126            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
127                invVMComputed = true;
128                fPaint.colorSampler(t)->preConcatMatrix(invVM);
129            }
130        }
131    }
132    for (int m = 0; m < GrPaint::kMaxCoverageStages; ++m) {
133        if (fPaint.isCoverageStageEnabled(m)) {
134            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
135                invVMComputed = true;
136                fPaint.coverageSampler(m)->preConcatMatrix(invVM);
137            }
138        }
139    }
140
141    fDrawTarget = NULL;
142
143    fVertices = NULL;
144    fMaxVertices = 0;
145
146    fVertexLayout =
147        GrDrawTarget::kTextFormat_VertexLayoutBit |
148        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
149}
150
151GrTextContext::~GrTextContext() {
152    this->flushGlyphs();
153    if (fDrawTarget) {
154        fDrawTarget->drawState()->disableStages();
155    }
156    fContext->setMatrix(fOrigViewMatrix);
157}
158
159void GrTextContext::flush() {
160    this->flushGlyphs();
161}
162
163static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
164                              int stride) {
165    v[0 * stride].setI(l, t);
166    v[1 * stride].setI(l, b);
167    v[2 * stride].setI(r, b);
168    v[3 * stride].setI(r, t);
169}
170
171void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
172                                    GrFixed vx, GrFixed vy,
173                                    GrFontScaler* scaler) {
174    if (NULL == fStrike) {
175        fStrike = fContext->getFontCache()->getStrike(scaler);
176    }
177
178    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
179    if (NULL == glyph || glyph->fBounds.isEmpty()) {
180        return;
181    }
182
183    vx += GrIntToFixed(glyph->fBounds.fLeft);
184    vy += GrIntToFixed(glyph->fBounds.fTop);
185
186    // keep them as ints until we've done the clip-test
187    GrFixed width = glyph->fBounds.width();
188    GrFixed height = glyph->fBounds.height();
189
190    // check if we clipped out
191    if (true || NULL == glyph->fAtlas) {
192        int x = vx >> 16;
193        int y = vy >> 16;
194        if (fClipRect.quickReject(x, y, x + width, y + height)) {
195//            Gr_clz(3);    // so we can set a break-point in the debugger
196            return;
197        }
198    }
199
200    if (NULL == glyph->fAtlas) {
201        if (fStrike->getGlyphAtlas(glyph, scaler)) {
202            goto HAS_ATLAS;
203        }
204
205        // before we purge the cache, we must flush any accumulated draws
206        this->flushGlyphs();
207        fContext->flush();
208
209        // try to purge
210        fContext->getFontCache()->purgeExceptFor(fStrike);
211        if (fStrike->getGlyphAtlas(glyph, scaler)) {
212            goto HAS_ATLAS;
213        }
214
215        if (NULL == glyph->fPath) {
216            SkPath* path = SkNEW(SkPath);
217            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
218                // flag the glyph as being dead?
219                delete path;
220                return;
221            }
222            glyph->fPath = path;
223        }
224
225        GrPoint translate;
226        translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
227                      GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
228        fContext->drawPath(fPaint, *glyph->fPath, kWinding_GrPathFill,
229                           &translate);
230        return;
231    }
232
233HAS_ATLAS:
234    GrAssert(glyph->fAtlas);
235
236    // now promote them to fixed
237    width = GrIntToFixed(width);
238    height = GrIntToFixed(height);
239
240    GrTexture* texture = glyph->fAtlas->texture();
241    GrAssert(texture);
242
243    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
244        this->flushGlyphs();
245        fCurrTexture = texture;
246        fCurrTexture->ref();
247    }
248
249    if (NULL == fVertices) {
250        // If we need to reserve vertices allow the draw target to suggest
251        // a number of verts to reserve and whether to perform a flush.
252        fMaxVertices = kMinRequestedVerts;
253        bool flush = (NULL != fDrawTarget) &&
254                     fDrawTarget->geometryHints(fVertexLayout,
255                                                &fMaxVertices,
256                                                NULL);
257        if (flush) {
258            this->flushGlyphs();
259            fContext->flush();
260        }
261        fDrawTarget = fContext->getTextTarget(fPaint);
262        fMaxVertices = kDefaultRequestedVerts;
263        // ignore return, no point in flushing again.
264        fDrawTarget->geometryHints(fVertexLayout,
265                                   &fMaxVertices,
266                                   NULL);
267
268        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
269        if (fMaxVertices < kMinRequestedVerts) {
270            fMaxVertices = kDefaultRequestedVerts;
271        } else if (fMaxVertices > maxQuadVertices) {
272            // don't exceed the limit of the index buffer
273            fMaxVertices = maxQuadVertices;
274        }
275        bool success = fDrawTarget->reserveVertexAndIndexSpace(
276                                                   fVertexLayout,
277                                                   fMaxVertices,
278                                                   0,
279                                                   GrTCast<void**>(&fVertices),
280                                                   NULL);
281        GrAlwaysAssert(success);
282    }
283
284    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
285    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
286
287#if GR_TEXT_SCALAR_IS_USHORT
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