GrTextContext.cpp revision bed83a66f5fa5821a3a08da32157a6155960b15e
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(rt, clip, paint, 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(rt, clip, paint, 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    SkPaint runPaint = skPaint;
101
102    SkTextBlob::RunIterator it(blob);
103    for (;!it.done(); it.next()) {
104        size_t textLen = it.glyphCount() * sizeof(uint16_t);
105        const SkPoint& offset = it.offset();
106        // applyFontToPaint() always overwrites the exact same attributes,
107        // so it is safe to not re-seed the paint for this reason.
108        it.applyFontToPaint(&runPaint);
109
110        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
111            // A false return from filter() means we should abort the current draw.
112            runPaint = skPaint;
113            continue;
114        }
115
116        runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
117
118        GrPaint grPaint;
119        if (!SkPaint2GrPaint(fContext, fRenderTarget, runPaint, viewMatrix, true, &grPaint)) {
120            return;
121        }
122
123        switch (it.positioning()) {
124        case SkTextBlob::kDefault_Positioning:
125            this->drawText(rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(),
126                           textLen, x + offset.x(), y + offset.y(), clipBounds);
127            break;
128        case SkTextBlob::kHorizontal_Positioning:
129            this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
130                              textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds);
131            break;
132        case SkTextBlob::kFull_Positioning:
133            this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
134                              textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
135            break;
136        default:
137            SkFAIL("unhandled positioning mode");
138        }
139
140        if (drawFilter) {
141            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
142            runPaint = skPaint;
143        }
144    }
145}
146
147void GrTextContext::drawTextAsPath(const SkPaint& skPaint, const SkMatrix& viewMatrix,
148                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
149                                   const SkIRect& clipBounds) {
150    SkTextToPathIter iter(text, byteLength, skPaint, true);
151
152    SkMatrix    matrix;
153    matrix.setScale(iter.getPathScale(), iter.getPathScale());
154    matrix.postTranslate(x, y);
155
156    const SkPath* iterPath;
157    SkScalar xpos, prevXPos = 0;
158
159    while (iter.next(&iterPath, &xpos)) {
160        matrix.postTranslate(xpos - prevXPos, 0);
161        if (iterPath) {
162            const SkPaint& pnt = iter.getPaint();
163            fGpuDevice->internalDrawPath(*iterPath, pnt, viewMatrix, &matrix, clipBounds, false);
164        }
165        prevXPos = xpos;
166    }
167}
168
169void GrTextContext::drawPosTextAsPath(const SkPaint& origPaint, const SkMatrix& viewMatrix,
170                                      const char text[], size_t byteLength,
171                                      const SkScalar pos[], int scalarsPerPosition,
172                                      const SkPoint& offset, const SkIRect& clipBounds) {
173    // setup our std paint, in hopes of getting hits in the cache
174    SkPaint paint(origPaint);
175    SkScalar matrixScale = paint.setupForAsPaths();
176
177    SkMatrix matrix;
178    matrix.setScale(matrixScale, matrixScale);
179
180    // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
181    paint.setStyle(SkPaint::kFill_Style);
182    paint.setPathEffect(NULL);
183
184    SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
185    SkAutoGlyphCache    autoCache(paint, NULL, NULL);
186    SkGlyphCache*       cache = autoCache.getCache();
187
188    const char*        stop = text + byteLength;
189    SkTextAlignProc    alignProc(paint.getTextAlign());
190    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
191
192    // Now restore the original settings, so we "draw" with whatever style/stroking.
193    paint.setStyle(origPaint.getStyle());
194    paint.setPathEffect(origPaint.getPathEffect());
195
196    while (text < stop) {
197        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
198        if (glyph.fWidth) {
199            const SkPath* path = cache->findPath(glyph);
200            if (path) {
201                SkPoint tmsLoc;
202                tmsProc(pos, &tmsLoc);
203                SkPoint loc;
204                alignProc(tmsLoc, glyph, &loc);
205
206                matrix[SkMatrix::kMTransX] = loc.fX;
207                matrix[SkMatrix::kMTransY] = loc.fY;
208                fGpuDevice->internalDrawPath(*path, paint, viewMatrix, &matrix, clipBounds, false);
209            }
210        }
211        pos += scalarsPerPosition;
212    }
213}
214
215//*** change to output positions?
216int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
217                                const char text[], size_t byteLength, SkVector* stopVector) {
218    SkFixed     x = 0, y = 0;
219    const char* stop = text + byteLength;
220
221    SkAutoKern  autokern;
222
223    int numGlyphs = 0;
224    while (text < stop) {
225        // don't need x, y here, since all subpixel variants will have the
226        // same advance
227        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
228
229        x += autokern.adjust(glyph) + glyph.fAdvanceX;
230        y += glyph.fAdvanceY;
231        ++numGlyphs;
232    }
233    stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
234
235    SkASSERT(text == stop);
236
237    return numGlyphs;
238}
239
240static void GlyphCacheAuxProc(void* data) {
241    GrFontScaler* scaler = (GrFontScaler*)data;
242    SkSafeUnref(scaler);
243}
244
245GrFontScaler* GrTextContext::GetGrFontScaler(SkGlyphCache* cache) {
246    void* auxData;
247    GrFontScaler* scaler = NULL;
248
249    if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
250        scaler = (GrFontScaler*)auxData;
251    }
252    if (NULL == scaler) {
253        scaler = SkNEW_ARGS(GrFontScaler, (cache));
254        cache->setAuxProc(GlyphCacheAuxProc, scaler);
255    }
256
257    return scaler;
258}
259