GrTextContext.cpp revision 858804dfe62c957050080a6fdce9226387ae7b7d
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    fAutoMatrix.setIdentity(fContext, &fPaint);
93
94    fDrawTarget = NULL;
95
96    fVertices = NULL;
97    fMaxVertices = 0;
98
99    fVertexLayout =
100        GrDrawTarget::kTextFormat_VertexLayoutBit |
101        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
102}
103
104GrTextContext::~GrTextContext() {
105    this->flushGlyphs();
106    if (fDrawTarget) {
107        fDrawTarget->drawState()->disableStages();
108    }
109}
110
111void GrTextContext::flush() {
112    this->flushGlyphs();
113}
114
115static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
116                              int stride) {
117    v[0 * stride].setI(l, t);
118    v[1 * stride].setI(l, b);
119    v[2 * stride].setI(r, b);
120    v[3 * stride].setI(r, t);
121}
122
123void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
124                                    GrFixed vx, GrFixed vy,
125                                    GrFontScaler* scaler) {
126    if (NULL == fStrike) {
127        fStrike = fContext->getFontCache()->getStrike(scaler);
128    }
129
130    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
131    if (NULL == glyph || glyph->fBounds.isEmpty()) {
132        return;
133    }
134
135    vx += GrIntToFixed(glyph->fBounds.fLeft);
136    vy += GrIntToFixed(glyph->fBounds.fTop);
137
138    // keep them as ints until we've done the clip-test
139    GrFixed width = glyph->fBounds.width();
140    GrFixed height = glyph->fBounds.height();
141
142    // check if we clipped out
143    if (true || NULL == glyph->fAtlas) {
144        int x = vx >> 16;
145        int y = vy >> 16;
146        if (fClipRect.quickReject(x, y, x + width, y + height)) {
147//            Gr_clz(3);    // so we can set a break-point in the debugger
148            return;
149        }
150    }
151
152    if (NULL == glyph->fAtlas) {
153        if (fStrike->getGlyphAtlas(glyph, scaler)) {
154            goto HAS_ATLAS;
155        }
156
157        // before we purge the cache, we must flush any accumulated draws
158        this->flushGlyphs();
159        fContext->flush();
160
161        // try to purge
162        fContext->getFontCache()->purgeExceptFor(fStrike);
163        if (fStrike->getGlyphAtlas(glyph, scaler)) {
164            goto HAS_ATLAS;
165        }
166
167        if (NULL == glyph->fPath) {
168            SkPath* path = SkNEW(SkPath);
169            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
170                // flag the glyph as being dead?
171                delete path;
172                return;
173            }
174            glyph->fPath = path;
175        }
176
177        GrContext::AutoMatrix am;
178        GrMatrix translate;
179        translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
180                               GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
181        GrPaint tmpPaint(fPaint);
182        am.setPreConcat(fContext, translate, &tmpPaint);
183        fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill);
184        return;
185    }
186
187HAS_ATLAS:
188    GrAssert(glyph->fAtlas);
189
190    // now promote them to fixed
191    width = GrIntToFixed(width);
192    height = GrIntToFixed(height);
193
194    GrTexture* texture = glyph->fAtlas->texture();
195    GrAssert(texture);
196
197    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
198        this->flushGlyphs();
199        fCurrTexture = texture;
200        fCurrTexture->ref();
201    }
202
203    if (NULL == fVertices) {
204        // If we need to reserve vertices allow the draw target to suggest
205        // a number of verts to reserve and whether to perform a flush.
206        fMaxVertices = kMinRequestedVerts;
207        bool flush = (NULL != fDrawTarget) &&
208                     fDrawTarget->geometryHints(fVertexLayout,
209                                                &fMaxVertices,
210                                                NULL);
211        if (flush) {
212            this->flushGlyphs();
213            fContext->flush();
214        }
215        fDrawTarget = fContext->getTextTarget(fPaint);
216        fMaxVertices = kDefaultRequestedVerts;
217        // ignore return, no point in flushing again.
218        fDrawTarget->geometryHints(fVertexLayout,
219                                   &fMaxVertices,
220                                   NULL);
221
222        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
223        if (fMaxVertices < kMinRequestedVerts) {
224            fMaxVertices = kDefaultRequestedVerts;
225        } else if (fMaxVertices > maxQuadVertices) {
226            // don't exceed the limit of the index buffer
227            fMaxVertices = maxQuadVertices;
228        }
229        bool success = fDrawTarget->reserveVertexAndIndexSpace(
230                                                   fVertexLayout,
231                                                   fMaxVertices,
232                                                   0,
233                                                   GrTCast<void**>(&fVertices),
234                                                   NULL);
235        GrAlwaysAssert(success);
236    }
237
238    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
239    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
240
241#if GR_TEXT_SCALAR_IS_USHORT
242    int x = vx >> 16;
243    int y = vy >> 16;
244    int w = width >> 16;
245    int h = height >> 16;
246
247    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
248    setRectFan(&fVertices[2*fCurrVertex+1],
249               texture->normalizeFixedX(tx),
250               texture->normalizeFixedY(ty),
251               texture->normalizeFixedX(tx + width),
252               texture->normalizeFixedY(ty + height),
253               2);
254#else
255    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
256                                        2 * sizeof(GrGpuTextVertex));
257    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
258                                          texture->normalizeFixedY(ty),
259                                          texture->normalizeFixedX(tx + width),
260                                          texture->normalizeFixedY(ty + height),
261                                          2 * sizeof(GrGpuTextVertex));
262#endif
263    fCurrVertex += 4;
264}
265
266