GrTextContext.cpp revision dfdb7e5240276493077b7c6e1f3cc8b8a0e195ba
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 (NULL == fDrawTarget) {
27        return;
28    }
29    GrDrawState* drawState = fDrawTarget->drawState();
30    if (fCurrVertex > 0) {
31        // setup our sampler state for our text texture/atlas
32        drawState->sampler(kGlyphMaskStage)->reset();
33
34        GrAssert(GrIsALIGN4(fCurrVertex));
35        GrAssert(fCurrTexture);
36        GrTextureParams params(SkShader::kRepeat_TileMode, false);
37        drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, GrMatrix::I(), params);
38
39        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
40            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
41                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
42                fPaint.hasColorStage()) {
43                GrPrintf("LCD Text will not draw correctly.\n");
44            }
45            // setup blend so that we get mask * paintColor + (1-mask)*dstColor
46            drawState->setBlendConstant(fPaint.getColor());
47            drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
48            // don't modulate by the paint's color in the frag since we're
49            // already doing it via the blend const.
50            drawState->setColor(0xffffffff);
51        } else {
52            // set back to normal in case we took LCD path previously.
53            drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
54            drawState->setColor(fPaint.getColor());
55        }
56
57        int nGlyphs = fCurrVertex / 4;
58        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
59        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
60                                          nGlyphs,
61                                          4, 6);
62        fDrawTarget->resetVertexSource();
63        fVertices = NULL;
64        fMaxVertices = 0;
65        fCurrVertex = 0;
66        GrSafeSetNull(fCurrTexture);
67    }
68    drawState->disableStages();
69    fDrawTarget = NULL;
70}
71
72GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) {
73    fContext = context;
74    fStrike = NULL;
75
76    fCurrTexture = NULL;
77    fCurrVertex = 0;
78
79    const GrClipData* clipData = context->getClip();
80
81    GrRect devConservativeBound;
82    clipData->fClipStack->getConservativeBounds(
83                                     -clipData->fOrigin.fX,
84                                     -clipData->fOrigin.fY,
85                                     context->getRenderTarget()->width(),
86                                     context->getRenderTarget()->height(),
87                                     &devConservativeBound);
88
89    devConservativeBound.roundOut(&fClipRect);
90
91    fAutoMatrix.setIdentity(fContext, &fPaint);
92
93    fDrawTarget = NULL;
94
95    fVertices = NULL;
96    fMaxVertices = 0;
97
98    fVertexLayout =
99        GrDrawTarget::kTextFormat_VertexLayoutBit |
100        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
101}
102
103GrTextContext::~GrTextContext() {
104    this->flushGlyphs();
105    if (fDrawTarget) {
106        fDrawTarget->drawState()->disableStages();
107    }
108}
109
110void GrTextContext::flush() {
111    this->flushGlyphs();
112}
113
114static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
115                              int stride) {
116    v[0 * stride].setI(l, t);
117    v[1 * stride].setI(l, b);
118    v[2 * stride].setI(r, b);
119    v[3 * stride].setI(r, t);
120}
121
122void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
123                                    GrFixed vx, GrFixed vy,
124                                    GrFontScaler* scaler) {
125    if (NULL == fStrike) {
126        fStrike = fContext->getFontCache()->getStrike(scaler);
127    }
128
129    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
130    if (NULL == glyph || glyph->fBounds.isEmpty()) {
131        return;
132    }
133
134    vx += GrIntToFixed(glyph->fBounds.fLeft);
135    vy += GrIntToFixed(glyph->fBounds.fTop);
136
137    // keep them as ints until we've done the clip-test
138    GrFixed width = glyph->fBounds.width();
139    GrFixed height = glyph->fBounds.height();
140
141    // check if we clipped out
142    if (true || NULL == glyph->fAtlas) {
143        int x = vx >> 16;
144        int y = vy >> 16;
145        if (fClipRect.quickReject(x, y, x + width, y + height)) {
146//            Gr_clz(3);    // so we can set a break-point in the debugger
147            return;
148        }
149    }
150
151    if (NULL == glyph->fAtlas) {
152        if (fStrike->getGlyphAtlas(glyph, scaler)) {
153            goto HAS_ATLAS;
154        }
155
156        // before we purge the cache, we must flush any accumulated draws
157        this->flushGlyphs();
158        fContext->flush();
159
160        // try to purge
161        fContext->getFontCache()->purgeExceptFor(fStrike);
162        if (fStrike->getGlyphAtlas(glyph, scaler)) {
163            goto HAS_ATLAS;
164        }
165
166        if (NULL == glyph->fPath) {
167            SkPath* path = SkNEW(SkPath);
168            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
169                // flag the glyph as being dead?
170                delete path;
171                return;
172            }
173            glyph->fPath = path;
174        }
175
176        GrContext::AutoMatrix am;
177        GrMatrix translate;
178        translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
179                               GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
180        GrPaint tmpPaint(fPaint);
181        am.setPreConcat(fContext, translate, &tmpPaint);
182        fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill);
183        return;
184    }
185
186HAS_ATLAS:
187    GrAssert(glyph->fAtlas);
188
189    // now promote them to fixed
190    width = GrIntToFixed(width);
191    height = GrIntToFixed(height);
192
193    GrTexture* texture = glyph->fAtlas->texture();
194    GrAssert(texture);
195
196    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
197        this->flushGlyphs();
198        fCurrTexture = texture;
199        fCurrTexture->ref();
200    }
201
202    if (NULL == fVertices) {
203        // If we need to reserve vertices allow the draw target to suggest
204        // a number of verts to reserve and whether to perform a flush.
205        fMaxVertices = kMinRequestedVerts;
206        bool flush = (NULL != fDrawTarget) &&
207                     fDrawTarget->geometryHints(fVertexLayout,
208                                                &fMaxVertices,
209                                                NULL);
210        if (flush) {
211            this->flushGlyphs();
212            fContext->flush();
213        }
214        fDrawTarget = fContext->getTextTarget(fPaint);
215        fMaxVertices = kDefaultRequestedVerts;
216        // ignore return, no point in flushing again.
217        fDrawTarget->geometryHints(fVertexLayout,
218                                   &fMaxVertices,
219                                   NULL);
220
221        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
222        if (fMaxVertices < kMinRequestedVerts) {
223            fMaxVertices = kDefaultRequestedVerts;
224        } else if (fMaxVertices > maxQuadVertices) {
225            // don't exceed the limit of the index buffer
226            fMaxVertices = maxQuadVertices;
227        }
228        bool success = fDrawTarget->reserveVertexAndIndexSpace(
229                                                   fVertexLayout,
230                                                   fMaxVertices,
231                                                   0,
232                                                   GrTCast<void**>(&fVertices),
233                                                   NULL);
234        GrAlwaysAssert(success);
235    }
236
237    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
238    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
239
240#if GR_TEXT_SCALAR_IS_USHORT
241    int x = vx >> 16;
242    int y = vy >> 16;
243    int w = width >> 16;
244    int h = height >> 16;
245
246    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
247    setRectFan(&fVertices[2*fCurrVertex+1],
248               texture->normalizeFixedX(tx),
249               texture->normalizeFixedY(ty),
250               texture->normalizeFixedX(tx + width),
251               texture->normalizeFixedY(ty + height),
252               2);
253#else
254    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
255                                        2 * sizeof(GrGpuTextVertex));
256    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
257                                          texture->normalizeFixedY(ty),
258                                          texture->normalizeFixedX(tx + width),
259                                          texture->normalizeFixedY(ty + height),
260                                          2 * sizeof(GrGpuTextVertex));
261#endif
262    fCurrVertex += 4;
263}
264
265