1/*
2 * Copyright 2013 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#include "GrDistanceFieldTextContext.h"
9#include "GrAtlas.h"
10#include "GrDrawTarget.h"
11#include "GrFontScaler.h"
12#include "GrIndexBuffer.h"
13#include "GrTextStrike.h"
14#include "GrTextStrike_impl.h"
15#include "SkPath.h"
16#include "SkRTConf.h"
17#include "SkStrokeRec.h"
18#include "effects/GrDistanceFieldTextureEffect.h"
19
20static const int kGlyphCoordsAttributeIndex = 1;
21
22SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
23                "Dump the contents of the font cache before every purge.");
24
25
26GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
27                                                       const GrPaint& paint,
28                                                       SkColor color,
29                                                       SkScalar textRatio)
30                                                     : GrTextContext(context, paint)
31                                                     , fTextRatio(textRatio) {
32    fSkPaintColor = color;
33
34    fStrike = NULL;
35
36    fCurrTexture = NULL;
37    fCurrVertex = 0;
38
39    fVertices = NULL;
40    fMaxVertices = 0;
41}
42
43GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
44    this->flushGlyphs();
45}
46
47static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
48    unsigned r = SkColorGetR(c);
49    unsigned g = SkColorGetG(c);
50    unsigned b = SkColorGetB(c);
51    return GrColorPackRGBA(r, g, b, 0xff);
52}
53
54void GrDistanceFieldTextContext::flushGlyphs() {
55    if (NULL == fDrawTarget) {
56        return;
57    }
58
59    GrDrawState* drawState = fDrawTarget->drawState();
60    GrDrawState::AutoRestoreEffects are(drawState);
61    drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
62
63    if (fCurrVertex > 0) {
64        // setup our sampler state for our text texture/atlas
65        SkASSERT(GrIsALIGN4(fCurrVertex));
66        SkASSERT(fCurrTexture);
67        GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
68
69        // This effect could be stored with one of the cache objects (atlas?)
70        drawState->addCoverageEffect(
71                                GrDistanceFieldTextureEffect::Create(fCurrTexture, params),
72                                kGlyphCoordsAttributeIndex)->unref();
73
74        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
75            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
76                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
77                fPaint.numColorStages()) {
78                GrPrintf("LCD Text will not draw correctly.\n");
79            }
80            // We don't use the GrPaint's color in this case because it's been premultiplied by
81            // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
82            // the mask texture color. The end result is that we get
83            //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
84            int a = SkColorGetA(fSkPaintColor);
85            // paintAlpha
86            drawState->setColor(SkColorSetARGB(a, a, a, a));
87            // paintColor
88            drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor));
89            drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
90        } else {
91            // set back to normal in case we took LCD path previously.
92            drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
93            drawState->setColor(fPaint.getColor());
94        }
95
96        int nGlyphs = fCurrVertex / 4;
97        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
98        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
99                                          nGlyphs,
100                                          4, 6);
101        fDrawTarget->resetVertexSource();
102        fVertices = NULL;
103        fMaxVertices = 0;
104        fCurrVertex = 0;
105        SkSafeSetNull(fCurrTexture);
106    }
107}
108
109namespace {
110
111// position + texture coord
112extern const GrVertexAttrib gTextVertexAttribs[] = {
113    {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
114    {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
115};
116
117};
118
119void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
120                                                 GrFixed vx, GrFixed vy,
121                                                 GrFontScaler* scaler) {
122    if (NULL == fDrawTarget) {
123        return;
124    }
125    if (NULL == fStrike) {
126        fStrike = fContext->getFontCache()->getStrike(scaler, true);
127    }
128
129    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
130    if (NULL == glyph || glyph->fBounds.isEmpty()) {
131        return;
132    }
133
134    SkScalar sx = SkFixedToScalar(vx);
135    SkScalar sy = SkFixedToScalar(vy);
136/*
137    // not valid, need to find a different solution for this
138    vx += SkIntToFixed(glyph->fBounds.fLeft);
139    vy += SkIntToFixed(glyph->fBounds.fTop);
140
141    // keep them as ints until we've done the clip-test
142    GrFixed width = glyph->fBounds.width();
143    GrFixed height = glyph->fBounds.height();
144
145    // check if we clipped out
146    if (true || NULL == glyph->fPlot) {
147        int x = vx >> 16;
148        int y = vy >> 16;
149        if (fClipRect.quickReject(x, y, x + width, y + height)) {
150//            SkCLZ(3);    // so we can set a break-point in the debugger
151            return;
152        }
153    }
154*/
155    if (NULL == glyph->fPlot) {
156        if (fStrike->getGlyphAtlas(glyph, scaler)) {
157            goto HAS_ATLAS;
158        }
159
160        // try to clear out an unused plot before we flush
161        fContext->getFontCache()->freePlotExceptFor(fStrike);
162        if (fStrike->getGlyphAtlas(glyph, scaler)) {
163            goto HAS_ATLAS;
164        }
165
166        if (c_DumpFontCache) {
167#ifdef SK_DEVELOPER
168            fContext->getFontCache()->dump();
169#endif
170        }
171
172        // before we purge the cache, we must flush any accumulated draws
173        this->flushGlyphs();
174        fContext->flush();
175
176        // try to purge
177        fContext->getFontCache()->purgeExceptFor(fStrike);
178        // need to use new flush count here
179        if (fStrike->getGlyphAtlas(glyph, scaler)) {
180            goto HAS_ATLAS;
181        }
182
183        if (NULL == glyph->fPath) {
184            SkPath* path = SkNEW(SkPath);
185            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
186                // flag the glyph as being dead?
187                delete path;
188                return;
189            }
190            glyph->fPath = path;
191        }
192
193        GrContext::AutoMatrix am;
194        SkMatrix translate;
195        translate.setTranslate(sx, sy);
196        GrPaint tmpPaint(fPaint);
197        am.setPreConcat(fContext, translate, &tmpPaint);
198        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
199        fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
200        return;
201    }
202
203HAS_ATLAS:
204    SkASSERT(glyph->fPlot);
205    GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
206    glyph->fPlot->setDrawToken(drawToken);
207
208    GrTexture* texture = glyph->fPlot->texture();
209    SkASSERT(texture);
210
211    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
212        this->flushGlyphs();
213        fCurrTexture = texture;
214        fCurrTexture->ref();
215    }
216
217    if (NULL == fVertices) {
218       // If we need to reserve vertices allow the draw target to suggest
219        // a number of verts to reserve and whether to perform a flush.
220        fMaxVertices = kMinRequestedVerts;
221        fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
222            SK_ARRAY_COUNT(gTextVertexAttribs));
223        bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
224        if (flush) {
225            this->flushGlyphs();
226            fContext->flush();
227            fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
228                SK_ARRAY_COUNT(gTextVertexAttribs));
229        }
230        fMaxVertices = kDefaultRequestedVerts;
231        // ignore return, no point in flushing again.
232        fDrawTarget->geometryHints(&fMaxVertices, NULL);
233
234        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
235        if (fMaxVertices < kMinRequestedVerts) {
236            fMaxVertices = kDefaultRequestedVerts;
237        } else if (fMaxVertices > maxQuadVertices) {
238            // don't exceed the limit of the index buffer
239            fMaxVertices = maxQuadVertices;
240        }
241        bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
242                                                               0,
243                                                               GrTCast<void**>(&fVertices),
244                                                               NULL);
245        GrAlwaysAssert(success);
246        SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
247    }
248
249    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
250    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
251    SkScalar width = SkIntToScalar(glyph->fBounds.width());
252    SkScalar height = SkIntToScalar(glyph->fBounds.height());
253
254    SkScalar scale = fTextRatio;
255    dx *= scale;
256    dy *= scale;
257    sx += dx;
258    sy += dy;
259    width *= scale;
260    height *= scale;
261
262    GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
263    GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
264    GrFixed tw = SkIntToFixed(glyph->fBounds.width());
265    GrFixed th = SkIntToFixed(glyph->fBounds.height());
266
267    fVertices[2*fCurrVertex].setRectFan(sx,
268                                        sy,
269                                        sx + width,
270                                        sy + height,
271                                        2 * sizeof(SkPoint));
272    fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
273                                          SkFixedToFloat(texture->normalizeFixedY(ty)),
274                                          SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
275                                          SkFixedToFloat(texture->normalizeFixedY(ty + th)),
276                                          2 * sizeof(SkPoint));
277    fCurrVertex += 4;
278}
279