GrDistanceFieldTextContext.cpp revision 4362a38563a958083aca2b456aaaa9f756f6f4e1
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 "GrDrawTargetCaps.h"
12#include "GrFontScaler.h"
13#include "SkGlyphCache.h"
14#include "GrIndexBuffer.h"
15#include "GrTextStrike.h"
16#include "GrTextStrike_impl.h"
17#include "SkDraw.h"
18#include "SkGpuDevice.h"
19#include "SkPath.h"
20#include "SkRTConf.h"
21#include "SkStrokeRec.h"
22#include "effects/GrDistanceFieldTextureEffect.h"
23
24static const int kGlyphCoordsAttributeIndex = 1;
25
26static const int kBaseDFFontSize = 32;
27
28SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
29                "Dump the contents of the font cache before every purge.");
30
31#if SK_FORCE_DISTANCEFIELD_FONTS
32static const bool kForceDistanceFieldFonts = true;
33#else
34static const bool kForceDistanceFieldFonts = false;
35#endif
36
37GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
38                                                       const SkDeviceProperties& properties)
39                                                    : GrTextContext(context, properties) {
40    fStrike = NULL;
41
42    fCurrTexture = NULL;
43    fCurrVertex = 0;
44
45    fVertices = NULL;
46    fMaxVertices = 0;
47}
48
49GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
50    this->flushGlyphs();
51}
52
53bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
54    return (kForceDistanceFieldFonts || paint.isDistanceFieldTextTEMP()) &&
55           !paint.getRasterizer() && !paint.getMaskFilter() &&
56           paint.getStyle() == SkPaint::kFill_Style &&
57           fContext->getTextTarget()->caps()->shaderDerivativeSupport() &&
58           !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
59}
60
61static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
62    unsigned r = SkColorGetR(c);
63    unsigned g = SkColorGetG(c);
64    unsigned b = SkColorGetB(c);
65    return GrColorPackRGBA(r, g, b, 0xff);
66}
67
68void GrDistanceFieldTextContext::flushGlyphs() {
69    if (NULL == fDrawTarget) {
70        return;
71    }
72
73    GrDrawState* drawState = fDrawTarget->drawState();
74    GrDrawState::AutoRestoreEffects are(drawState);
75    drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
76
77    if (fCurrVertex > 0) {
78        // setup our sampler state for our text texture/atlas
79        SkASSERT(GrIsALIGN4(fCurrVertex));
80        SkASSERT(fCurrTexture);
81        GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
82
83        // This effect could be stored with one of the cache objects (atlas?)
84        drawState->addCoverageEffect(
85                         GrDistanceFieldTextureEffect::Create(fCurrTexture, params,
86                                                              fContext->getMatrix().isSimilarity()),
87                         kGlyphCoordsAttributeIndex)->unref();
88
89        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
90            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
91                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
92                fPaint.numColorStages()) {
93                GrPrintf("LCD Text will not draw correctly.\n");
94            }
95            // We don't use the GrPaint's color in this case because it's been premultiplied by
96            // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
97            // the mask texture color. The end result is that we get
98            //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
99            int a = SkColorGetA(fSkPaint.getColor());
100            // paintAlpha
101            drawState->setColor(SkColorSetARGB(a, a, a, a));
102            // paintColor
103            drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
104            drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
105        } else {
106            // set back to normal in case we took LCD path previously.
107            drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
108            drawState->setColor(fPaint.getColor());
109        }
110
111        int nGlyphs = fCurrVertex / 4;
112        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
113        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
114                                          nGlyphs,
115                                          4, 6);
116        fDrawTarget->resetVertexSource();
117        fVertices = NULL;
118        fMaxVertices = 0;
119        fCurrVertex = 0;
120        SkSafeSetNull(fCurrTexture);
121    }
122}
123
124namespace {
125
126// position + texture coord
127extern const GrVertexAttrib gTextVertexAttribs[] = {
128    {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
129    {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
130};
131
132};
133
134void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
135                                                 GrFixed vx, GrFixed vy,
136                                                 GrFontScaler* scaler) {
137    if (NULL == fDrawTarget) {
138        return;
139    }
140    if (NULL == fStrike) {
141        fStrike = fContext->getFontCache()->getStrike(scaler, true);
142    }
143
144    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
145    if (NULL == glyph || glyph->fBounds.isEmpty()) {
146        return;
147    }
148
149    SkScalar sx = SkFixedToScalar(vx);
150    SkScalar sy = SkFixedToScalar(vy);
151/*
152    // not valid, need to find a different solution for this
153    vx += SkIntToFixed(glyph->fBounds.fLeft);
154    vy += SkIntToFixed(glyph->fBounds.fTop);
155
156    // keep them as ints until we've done the clip-test
157    GrFixed width = glyph->fBounds.width();
158    GrFixed height = glyph->fBounds.height();
159
160    // check if we clipped out
161    if (true || NULL == glyph->fPlot) {
162        int x = vx >> 16;
163        int y = vy >> 16;
164        if (fClipRect.quickReject(x, y, x + width, y + height)) {
165//            SkCLZ(3);    // so we can set a break-point in the debugger
166            return;
167        }
168    }
169*/
170    if (NULL == glyph->fPlot) {
171        if (fStrike->addGlyphToAtlas(glyph, scaler)) {
172            goto HAS_ATLAS;
173        }
174
175        // try to clear out an unused plot before we flush
176        if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
177            fStrike->addGlyphToAtlas(glyph, scaler)) {
178            goto HAS_ATLAS;
179        }
180
181        if (c_DumpFontCache) {
182#ifdef SK_DEVELOPER
183            fContext->getFontCache()->dump();
184#endif
185        }
186
187        // before we purge the cache, we must flush any accumulated draws
188        this->flushGlyphs();
189        fContext->flush();
190
191        // we should have an unused plot now
192        if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
193            fStrike->addGlyphToAtlas(glyph, scaler)) {
194            goto HAS_ATLAS;
195        }
196
197        if (NULL == glyph->fPath) {
198            SkPath* path = SkNEW(SkPath);
199            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
200                // flag the glyph as being dead?
201                delete path;
202                return;
203            }
204            glyph->fPath = path;
205        }
206
207        GrContext::AutoMatrix am;
208        SkMatrix translate;
209        translate.setTranslate(sx, sy);
210        GrPaint tmpPaint(fPaint);
211        am.setPreConcat(fContext, translate, &tmpPaint);
212        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
213        fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
214        return;
215    }
216
217HAS_ATLAS:
218    SkASSERT(glyph->fPlot);
219    GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
220    glyph->fPlot->setDrawToken(drawToken);
221
222    GrTexture* texture = glyph->fPlot->texture();
223    SkASSERT(texture);
224
225    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
226        this->flushGlyphs();
227        fCurrTexture = texture;
228        fCurrTexture->ref();
229    }
230
231    if (NULL == fVertices) {
232       // If we need to reserve vertices allow the draw target to suggest
233        // a number of verts to reserve and whether to perform a flush.
234        fMaxVertices = kMinRequestedVerts;
235        fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
236            SK_ARRAY_COUNT(gTextVertexAttribs));
237        bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
238        if (flush) {
239            this->flushGlyphs();
240            fContext->flush();
241            fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
242                SK_ARRAY_COUNT(gTextVertexAttribs));
243        }
244        fMaxVertices = kDefaultRequestedVerts;
245        // ignore return, no point in flushing again.
246        fDrawTarget->geometryHints(&fMaxVertices, NULL);
247
248        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
249        if (fMaxVertices < kMinRequestedVerts) {
250            fMaxVertices = kDefaultRequestedVerts;
251        } else if (fMaxVertices > maxQuadVertices) {
252            // don't exceed the limit of the index buffer
253            fMaxVertices = maxQuadVertices;
254        }
255        bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
256                                                               0,
257                                                               GrTCast<void**>(&fVertices),
258                                                               NULL);
259        GrAlwaysAssert(success);
260        SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
261    }
262
263    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft);
264    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop);
265    SkScalar width = SkIntToScalar(glyph->fBounds.width());
266    SkScalar height = SkIntToScalar(glyph->fBounds.height());
267
268    SkScalar scale = fTextRatio;
269    dx *= scale;
270    dy *= scale;
271    sx += dx;
272    sy += dy;
273    width *= scale;
274    height *= scale;
275
276    GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
277    GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
278    GrFixed tw = SkIntToFixed(glyph->fBounds.width());
279    GrFixed th = SkIntToFixed(glyph->fBounds.height());
280
281    static const size_t kVertexSize = 2 * sizeof(SkPoint);
282    fVertices[2*fCurrVertex].setRectFan(sx,
283                                        sy,
284                                        sx + width,
285                                        sy + height,
286                                        kVertexSize);
287    fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
288                                          SkFixedToFloat(texture->normalizeFixedY(ty)),
289                                          SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
290                                          SkFixedToFloat(texture->normalizeFixedY(ty + th)),
291                                          kVertexSize);
292    fCurrVertex += 4;
293}
294
295inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
296    GrTextContext::init(paint, skPaint);
297
298    fStrike = NULL;
299
300    fCurrTexture = NULL;
301    fCurrVertex = 0;
302
303    fVertices = NULL;
304    fMaxVertices = 0;
305
306    fTextRatio = fSkPaint.getTextSize()/kBaseDFFontSize;
307
308    fSkPaint.setTextSize(SkIntToScalar(kBaseDFFontSize));
309    fSkPaint.setLCDRenderText(false);
310    fSkPaint.setAutohinted(false);
311    fSkPaint.setSubpixelText(true);
312}
313
314inline void GrDistanceFieldTextContext::finish() {
315    flushGlyphs();
316
317    GrTextContext::finish();
318}
319
320void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
321                                          const char text[], size_t byteLength,
322                                          SkScalar x, SkScalar y) {
323    SkASSERT(byteLength == 0 || text != NULL);
324
325    // nothing to draw or can't draw
326    if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
327        || fSkPaint.getRasterizer()) {
328        return;
329    }
330
331    this->init(paint, skPaint);
332
333    SkScalar sizeRatio = fTextRatio;
334
335    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
336
337    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, NULL);
338    SkGlyphCache*       cache = autoCache.getCache();
339    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
340
341    // need to measure first
342    // TODO - generate positions and pre-load cache as well?
343    const char* stop = text + byteLength;
344    if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
345        SkFixed    stopX = 0;
346        SkFixed    stopY = 0;
347
348        const char* textPtr = text;
349        while (textPtr < stop) {
350            // don't need x, y here, since all subpixel variants will have the
351            // same advance
352            const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
353
354            stopX += glyph.fAdvanceX;
355            stopY += glyph.fAdvanceY;
356        }
357        SkASSERT(textPtr == stop);
358
359        SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
360        SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
361
362        if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
363            alignX = SkScalarHalf(alignX);
364            alignY = SkScalarHalf(alignY);
365        }
366
367        x -= alignX;
368        y -= alignY;
369    }
370
371    SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf;
372    SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf;
373    SkFixed fixedScale = SkScalarToFixed(sizeRatio);
374    while (text < stop) {
375        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
376
377        if (glyph.fWidth) {
378            this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
379                                                glyph.getSubXFixed(),
380                                                glyph.getSubYFixed()),
381                                  SkFixedFloorToFixed(fx),
382                                  SkFixedFloorToFixed(fy),
383                                  fontScaler);
384        }
385
386        fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
387        fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
388    }
389
390    this->finish();
391}
392
393void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
394                                             const char text[], size_t byteLength,
395                                             const SkScalar pos[], SkScalar constY,
396                                             int scalarsPerPosition) {
397
398    SkASSERT(byteLength == 0 || text != NULL);
399    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
400
401    // nothing to draw
402    if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
403        return;
404    }
405
406    this->init(paint, skPaint);
407
408    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
409
410    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, NULL);
411    SkGlyphCache*       cache = autoCache.getCache();
412    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
413
414    const char*        stop = text + byteLength;
415
416    if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
417        while (text < stop) {
418            // the last 2 parameters are ignored
419            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
420
421            if (glyph.fWidth) {
422                SkScalar x = pos[0];
423                SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
424
425                this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
426                                                    glyph.getSubXFixed(),
427                                                    glyph.getSubYFixed()),
428                                      SkScalarToFixed(x) + SK_FixedHalf, //d1g.fHalfSampleX,
429                                      SkScalarToFixed(y) + SK_FixedHalf, //d1g.fHalfSampleY,
430                                      fontScaler);
431            }
432            pos += scalarsPerPosition;
433        }
434    } else {
435        int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
436        while (text < stop) {
437            // the last 2 parameters are ignored
438            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
439
440            if (glyph.fWidth) {
441                SkScalar x = pos[0];
442                SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
443
444                this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
445                                                    glyph.getSubXFixed(),
446                                                    glyph.getSubYFixed()),
447                                      SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift)
448                                        + SK_FixedHalf, //d1g.fHalfSampleX,
449                                      SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift)
450                                        + SK_FixedHalf, //d1g.fHalfSampleY,
451                                      fontScaler);
452            }
453            pos += scalarsPerPosition;
454        }
455    }
456
457    this->finish();
458}
459