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