GrTextContext.cpp revision 9c328187d9dee33736b77dc14dfb59529d948bb1
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#include "GrTextContext.h"
9#include "GrContext.h"
10#include "GrDrawTarget.h"
11#include "GrFontScaler.h"
12
13#include "SkAutoKern.h"
14#include "SkDrawFilter.h"
15#include "SkDrawProcs.h"
16#include "SkGlyphCache.h"
17#include "SkGpuDevice.h"
18#include "SkTextBlob.h"
19#include "SkTextMapStateProc.h"
20#include "SkTextToPathIter.h"
21
22GrTextContext::GrTextContext(GrContext* context, SkGpuDevice* gpuDevice,
23                             const SkDeviceProperties& properties)
24    : fFallbackTextContext(NULL)
25    , fContext(context)
26    , fGpuDevice(gpuDevice)
27    , fDeviceProperties(properties)
28    , fDrawTarget(NULL) {
29}
30
31GrTextContext::~GrTextContext() {
32    SkDELETE(fFallbackTextContext);
33}
34
35void GrTextContext::init(GrRenderTarget* rt, const GrClip& clip, const GrPaint& grPaint,
36                         const SkPaint& skPaint, const SkIRect& regionClipBounds) {
37    fClip = clip;
38
39    fRenderTarget.reset(SkRef(rt));
40
41    fRegionClipBounds = regionClipBounds;
42    fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
43
44    fDrawTarget = fContext->getTextTarget();
45
46    fPaint = grPaint;
47    fSkPaint = skPaint;
48}
49
50void GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
51                             const SkPaint& skPaint, const SkMatrix& viewMatrix,
52                             const char text[], size_t byteLength,
53                             SkScalar x, SkScalar y, const SkIRect& clipBounds) {
54    if (!fContext->getTextTarget()) {
55        return;
56    }
57
58    GrTextContext* textContext = this;
59    do {
60        if (textContext->canDraw(skPaint, viewMatrix)) {
61            textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, x, y,
62                                    clipBounds);
63            return;
64        }
65        textContext = textContext->fFallbackTextContext;
66    } while (textContext);
67
68    // fall back to drawing as a path
69    this->drawTextAsPath(skPaint, viewMatrix, text, byteLength, x, y, clipBounds);
70}
71
72void GrTextContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
73                                const SkPaint& skPaint, const SkMatrix& viewMatrix,
74                                const char text[], size_t byteLength,
75                                const SkScalar pos[], int scalarsPerPosition,
76                                const SkPoint& offset, const SkIRect& clipBounds) {
77    if (!fContext->getTextTarget()) {
78        return;
79    }
80
81    GrTextContext* textContext = this;
82    do {
83        if (textContext->canDraw(skPaint, viewMatrix)) {
84            textContext->onDrawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, pos,
85                                       scalarsPerPosition, offset, clipBounds);
86            return;
87        }
88        textContext = textContext->fFallbackTextContext;
89    } while (textContext);
90
91    // fall back to drawing as a path
92    this->drawPosTextAsPath(skPaint, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset,
93                            clipBounds);
94}
95
96void GrTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, const SkPaint& skPaint,
97                                 const SkMatrix& viewMatrix, const SkTextBlob* blob,
98                                 SkScalar x, SkScalar y,
99                                 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
100    if (!fContext->getTextTarget()) {
101        return;
102    }
103
104    GrTextContext* textContext = this;
105    do {
106        if (textContext->canDraw(skPaint, viewMatrix)) {
107            textContext->onDrawTextBlob(rt, clip, skPaint, viewMatrix, blob, x, y, drawFilter,
108                                        clipBounds);
109            return;
110        }
111        textContext = textContext->fFallbackTextContext;
112    } while (textContext);
113}
114
115void GrTextContext::drawTextAsPath(const SkPaint& skPaint, const SkMatrix& viewMatrix,
116                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
117                                   const SkIRect& clipBounds) {
118    SkTextToPathIter iter(text, byteLength, skPaint, true);
119
120    SkMatrix    matrix;
121    matrix.setScale(iter.getPathScale(), iter.getPathScale());
122    matrix.postTranslate(x, y);
123
124    const SkPath* iterPath;
125    SkScalar xpos, prevXPos = 0;
126
127    while (iter.next(&iterPath, &xpos)) {
128        matrix.postTranslate(xpos - prevXPos, 0);
129        if (iterPath) {
130            const SkPaint& pnt = iter.getPaint();
131            fGpuDevice->internalDrawPath(*iterPath, pnt, viewMatrix, &matrix, clipBounds, false);
132        }
133        prevXPos = xpos;
134    }
135}
136
137void GrTextContext::drawPosTextAsPath(const SkPaint& origPaint, const SkMatrix& viewMatrix,
138                                      const char text[], size_t byteLength,
139                                      const SkScalar pos[], int scalarsPerPosition,
140                                      const SkPoint& offset, const SkIRect& clipBounds) {
141    // setup our std paint, in hopes of getting hits in the cache
142    SkPaint paint(origPaint);
143    SkScalar matrixScale = paint.setupForAsPaths();
144
145    SkMatrix matrix;
146    matrix.setScale(matrixScale, matrixScale);
147
148    // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
149    paint.setStyle(SkPaint::kFill_Style);
150    paint.setPathEffect(NULL);
151
152    SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
153    SkAutoGlyphCache    autoCache(paint, NULL, NULL);
154    SkGlyphCache*       cache = autoCache.getCache();
155
156    const char*        stop = text + byteLength;
157    SkTextAlignProc    alignProc(paint.getTextAlign());
158    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
159
160    // Now restore the original settings, so we "draw" with whatever style/stroking.
161    paint.setStyle(origPaint.getStyle());
162    paint.setPathEffect(origPaint.getPathEffect());
163
164    while (text < stop) {
165        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
166        if (glyph.fWidth) {
167            const SkPath* path = cache->findPath(glyph);
168            if (path) {
169                SkPoint tmsLoc;
170                tmsProc(pos, &tmsLoc);
171                SkPoint loc;
172                alignProc(tmsLoc, glyph, &loc);
173
174                matrix[SkMatrix::kMTransX] = loc.fX;
175                matrix[SkMatrix::kMTransY] = loc.fY;
176                fGpuDevice->internalDrawPath(*path, paint, viewMatrix, &matrix, clipBounds, false);
177            }
178        }
179        pos += scalarsPerPosition;
180    }
181}
182
183void GrTextContext::onDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
184                                   const SkPaint& skPaint, const SkMatrix& viewMatrix,
185                                   const SkTextBlob* blob, SkScalar x, SkScalar y,
186                                   SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
187    SkPaint runPaint = skPaint;
188
189    SkTextBlob::RunIterator it(blob);
190    for (;!it.done(); it.next()) {
191        size_t textLen = it.glyphCount() * sizeof(uint16_t);
192        const SkPoint& offset = it.offset();
193        // applyFontToPaint() always overwrites the exact same attributes,
194        // so it is safe to not re-seed the paint for this reason.
195        it.applyFontToPaint(&runPaint);
196
197        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
198            // A false return from filter() means we should abort the current draw.
199            runPaint = skPaint;
200            continue;
201        }
202
203        runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
204
205        GrPaint grPaint;
206        SkPaint2GrPaintShader(fContext, fRenderTarget, runPaint, viewMatrix, true, &grPaint);
207
208        switch (it.positioning()) {
209        case SkTextBlob::kDefault_Positioning:
210            this->drawText(rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(),
211                           textLen, x + offset.x(), y + offset.y(), clipBounds);
212            break;
213        case SkTextBlob::kHorizontal_Positioning:
214            this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
215                              textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds);
216            break;
217        case SkTextBlob::kFull_Positioning:
218            this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
219                              textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
220            break;
221        default:
222            SkFAIL("unhandled positioning mode");
223        }
224
225        if (drawFilter) {
226            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
227            runPaint = skPaint;
228        }
229    }
230}
231
232//*** change to output positions?
233int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
234                                const char text[], size_t byteLength, SkVector* stopVector) {
235    SkFixed     x = 0, y = 0;
236    const char* stop = text + byteLength;
237
238    SkAutoKern  autokern;
239
240    int numGlyphs = 0;
241    while (text < stop) {
242        // don't need x, y here, since all subpixel variants will have the
243        // same advance
244        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
245
246        x += autokern.adjust(glyph) + glyph.fAdvanceX;
247        y += glyph.fAdvanceY;
248        ++numGlyphs;
249    }
250    stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
251
252    SkASSERT(text == stop);
253
254    return numGlyphs;
255}
256
257static void GlyphCacheAuxProc(void* data) {
258    GrFontScaler* scaler = (GrFontScaler*)data;
259    SkSafeUnref(scaler);
260}
261
262GrFontScaler* GrTextContext::GetGrFontScaler(SkGlyphCache* cache) {
263    void* auxData;
264    GrFontScaler* scaler = NULL;
265
266    if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
267        scaler = (GrFontScaler*)auxData;
268    }
269    if (NULL == scaler) {
270        scaler = SkNEW_ARGS(GrFontScaler, (cache));
271        cache->setAuxProc(GlyphCacheAuxProc, scaler);
272    }
273
274    return scaler;
275}
276