1/*
2 * Copyright 2014 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 "GrStencilAndCoverTextContext.h"
9#include "GrDrawTarget.h"
10#include "GrGpu.h"
11#include "GrPath.h"
12#include "GrPathRange.h"
13#include "SkAutoKern.h"
14#include "SkDraw.h"
15#include "SkDrawProcs.h"
16#include "SkGlyphCache.h"
17#include "SkGpuDevice.h"
18#include "SkPath.h"
19#include "SkTextMapStateProc.h"
20#include "SkTextFormatParams.h"
21
22GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
23    GrContext* context, const SkDeviceProperties& properties)
24    : GrTextContext(context, properties)
25    , fPendingGlyphCount(0) {
26}
27
28GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
29}
30
31void GrStencilAndCoverTextContext::drawText(const GrPaint& paint,
32                                            const SkPaint& skPaint,
33                                            const char text[],
34                                            size_t byteLength,
35                                            SkScalar x, SkScalar y) {
36    SkASSERT(byteLength == 0 || text != NULL);
37
38    if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
39        return;
40    }
41
42    // This is the slow path, mainly used by Skia unit tests.  The other
43    // backends (8888, gpu, ...) use device-space dependent glyph caches. In
44    // order to match the glyph positions that the other code paths produce, we
45    // must also use device-space dependent glyph cache. This has the
46    // side-effect that the glyph shape outline will be in device-space,
47    // too. This in turn has the side-effect that NVPR can not stroke the paths,
48    // as the stroke in NVPR is defined in object-space.
49    // NOTE: here we have following coincidence that works at the moment:
50    // - When using the device-space glyphs, the transforms we pass to NVPR
51    // instanced drawing are the global transforms, and the view transform is
52    // identity. NVPR can not use non-affine transforms in the instanced
53    // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
54    // will turn off the use of device-space glyphs when perspective transforms
55    // are in use.
56
57    this->init(paint, skPaint, byteLength, kMaxAccuracy_RenderMode);
58
59    // Transform our starting point.
60    if (fNeedsDeviceSpaceGlyphs) {
61        SkPoint loc;
62        fContextInitialMatrix.mapXY(x, y, &loc);
63        x = loc.fX;
64        y = loc.fY;
65    }
66
67    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
68
69    fTransformType = GrPathRendering::kTranslate_PathTransformType;
70
71    const char* stop = text + byteLength;
72
73    // Measure first if needed.
74    if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
75        SkFixed    stopX = 0;
76        SkFixed    stopY = 0;
77
78        const char* textPtr = text;
79        while (textPtr < stop) {
80            // We don't need x, y here, since all subpixel variants will have the
81            // same advance.
82            const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
83
84            stopX += glyph.fAdvanceX;
85            stopY += glyph.fAdvanceY;
86        }
87        SkASSERT(textPtr == stop);
88
89        SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
90        SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
91
92        if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
93            alignX = SkScalarHalf(alignX);
94            alignY = SkScalarHalf(alignY);
95        }
96
97        x -= alignX;
98        y -= alignY;
99    }
100
101    SkAutoKern autokern;
102
103    SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
104
105    SkFixed fx = SkScalarToFixed(x);
106    SkFixed fy = SkScalarToFixed(y);
107    while (text < stop) {
108        const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
109        fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
110        if (glyph.fWidth) {
111            this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
112        }
113
114        fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
115        fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
116    }
117
118    this->finish();
119}
120
121void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint,
122                                               const SkPaint& skPaint,
123                                               const char text[],
124                                               size_t byteLength,
125                                               const SkScalar pos[],
126                                               SkScalar constY,
127                                               int scalarsPerPosition) {
128    SkASSERT(byteLength == 0 || text != NULL);
129    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
130
131    // nothing to draw
132    if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
133        return;
134    }
135
136    // This is the fast path.  Here we do not bake in the device-transform to
137    // the glyph outline or the advances. This is because we do not need to
138    // position the glyphs at all, since the caller has done the positioning.
139    // The positioning is based on SkPaint::measureText of individual
140    // glyphs. That already uses glyph cache without device transforms. Device
141    // transform is not part of SkPaint::measureText API, and thus we use the
142    // same glyphs as what were measured.
143
144    const float textTranslateY = (1 == scalarsPerPosition ? constY : 0);
145    this->init(paint, skPaint, byteLength, kMaxPerformance_RenderMode, textTranslateY);
146
147    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
148
149    const char* stop = text + byteLength;
150
151    if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
152        if (1 == scalarsPerPosition) {
153            fTransformType = GrPathRendering::kTranslateX_PathTransformType;
154            while (text < stop) {
155                const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
156                if (glyph.fWidth) {
157                    this->appendGlyph(glyph.getGlyphID(), *pos);
158                }
159                pos++;
160            }
161        } else {
162            SkASSERT(2 == scalarsPerPosition);
163            fTransformType = GrPathRendering::kTranslate_PathTransformType;
164            while (text < stop) {
165                const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
166                if (glyph.fWidth) {
167                    this->appendGlyph(glyph.getGlyphID(), pos[0], pos[1]);
168                }
169                pos += 2;
170            }
171        }
172    } else {
173        fTransformType = GrPathRendering::kTranslate_PathTransformType;
174        SkTextMapStateProc tmsProc(SkMatrix::I(), 0, scalarsPerPosition);
175        SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
176        while (text < stop) {
177            const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
178            if (glyph.fWidth) {
179                SkPoint tmsLoc;
180                tmsProc(pos, &tmsLoc);
181                SkPoint loc;
182                alignProc(tmsLoc, glyph, &loc);
183
184                this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
185            }
186            pos += scalarsPerPosition;
187        }
188    }
189
190    this->finish();
191}
192
193bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
194    if (paint.getRasterizer()) {
195        return false;
196    }
197    if (paint.getMaskFilter()) {
198        return false;
199    }
200    if (paint.getPathEffect()) {
201        return false;
202    }
203
204    // No hairlines unless we can map the 1 px width to the object space.
205    if (paint.getStyle() == SkPaint::kStroke_Style
206        && paint.getStrokeWidth() == 0
207        && fContext->getMatrix().hasPerspective()) {
208        return false;
209    }
210
211    // No color bitmap fonts.
212    SkScalerContext::Rec    rec;
213    SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
214    return rec.getFormat() != SkMask::kARGB32_Format;
215}
216
217static GrPathRange* get_gr_glyphs(GrContext* ctx,
218                                  const SkTypeface* typeface,
219                                  const SkDescriptor* desc,
220                                  const SkStrokeRec& stroke) {
221    static const GrCacheID::Domain gGlyphsDomain = GrCacheID::GenerateDomain();
222
223    GrCacheID::Key key;
224    uint64_t* keyData = key.fData64;
225    keyData[0] = desc ? desc->getChecksum() : 0;
226    keyData[0] = (keyData[0] << 32) | (typeface ? typeface->uniqueID() : 0);
227    keyData[1] = GrPath::ComputeStrokeKey(stroke);
228    GrResourceKey resourceKey = GrResourceKey(GrCacheID(gGlyphsDomain, key),
229                                              GrPathRange::resourceType(), 0);
230
231    SkAutoTUnref<GrPathRange> glyphs(
232        static_cast<GrPathRange*>(ctx->findAndRefCachedResource(resourceKey)));
233    if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
234        glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
235        ctx->addResourceToCache(resourceKey, glyphs);
236    }
237
238    return glyphs.detach();
239}
240
241void GrStencilAndCoverTextContext::init(const GrPaint& paint,
242                                        const SkPaint& skPaint,
243                                        size_t textByteLength,
244                                        RenderMode renderMode,
245                                        SkScalar textTranslateY) {
246    GrTextContext::init(paint, skPaint);
247
248    fContextInitialMatrix = fContext->getMatrix();
249
250    const bool otherBackendsWillDrawAsPaths =
251        SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
252
253    fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
254                              kMaxAccuracy_RenderMode == renderMode &&
255                              SkToBool(fContextInitialMatrix.getType() &
256                                       (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
257
258    if (fNeedsDeviceSpaceGlyphs) {
259        // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
260        SkASSERT(!fContextInitialMatrix.hasPerspective());
261        SkASSERT(0 == textTranslateY); // TODO: Handle textTranslateY in device-space usecase.
262
263        fTextRatio = fTextInverseRatio = 1.0f;
264
265        // Glyphs loaded by GPU path rendering have an inverted y-direction.
266        SkMatrix m;
267        m.setScale(1, -1);
268        fContext->setMatrix(m);
269
270        // Post-flip the initial matrix so we're left with just the flip after
271        // the paint preConcats the inverse.
272        m = fContextInitialMatrix;
273        m.postScale(1, -1);
274        fPaint.localCoordChangeInverse(m);
275
276        // The whole shape (including stroke) will be baked into the glyph outlines. Make
277        // NVPR just fill the baked shapes.
278        fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, false);
279        fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
280                                &fGlyphCache->getDescriptor(),
281                                SkStrokeRec(SkStrokeRec::kFill_InitStyle));
282    } else {
283        // Don't bake strokes into the glyph outlines. We will stroke the glyphs
284        // using the GPU instead. This is the fast path.
285        SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint);
286        fSkPaint.setStyle(SkPaint::kFill_Style);
287
288        if (gpuStroke.isHairlineStyle()) {
289            // Approximate hairline stroke.
290            SkScalar strokeWidth = SK_Scalar1 /
291                (SkVector::Make(fContextInitialMatrix.getScaleX(),
292                                fContextInitialMatrix.getSkewY()).length());
293            gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
294
295        } else if (fSkPaint.isFakeBoldText() &&
296#ifdef SK_USE_FREETYPE_EMBOLDEN
297                   kMaxPerformance_RenderMode == renderMode &&
298#endif
299                   SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) {
300
301            // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
302            SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
303                                                        kStdFakeBoldInterpKeys,
304                                                        kStdFakeBoldInterpValues,
305                                                        kStdFakeBoldInterpLength);
306            SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
307            gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra,
308                                     true /*strokeAndFill*/);
309
310            fSkPaint.setFakeBoldText(false);
311        }
312
313        bool canUseRawPaths;
314
315        if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
316            // We can draw the glyphs from canonically sized paths.
317            fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
318            fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
319
320            // Compensate for the glyphs being scaled by fTextRatio.
321            if (!gpuStroke.isFillStyle()) {
322                gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio,
323                                         SkStrokeRec::kStrokeAndFill_Style == gpuStroke.getStyle());
324            }
325
326            fSkPaint.setLinearText(true);
327            fSkPaint.setLCDRenderText(false);
328            fSkPaint.setAutohinted(false);
329            fSkPaint.setHinting(SkPaint::kNo_Hinting);
330            fSkPaint.setSubpixelText(true);
331            fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
332
333            canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
334                             0 == fSkPaint.getTextSkewX() &&
335                             !fSkPaint.isFakeBoldText() &&
336                             !fSkPaint.isVerticalText();
337        } else {
338            fTextRatio = fTextInverseRatio = 1.0f;
339            canUseRawPaths = false;
340        }
341
342        SkMatrix textMatrix;
343        textMatrix.setTranslate(0, textTranslateY);
344        // Glyphs loaded by GPU path rendering have an inverted y-direction.
345        textMatrix.preScale(fTextRatio, -fTextRatio);
346        fPaint.localCoordChange(textMatrix);
347        fContext->concatMatrix(textMatrix);
348
349        fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false);
350        fGlyphs = canUseRawPaths ?
351                      get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) :
352                      get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
353                                    &fGlyphCache->getDescriptor(), gpuStroke);
354    }
355
356    fStateRestore.set(fDrawTarget->drawState());
357
358    fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
359                                           fContext->getRenderTarget());
360
361    GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
362                                 kZero_StencilOp,
363                                 kZero_StencilOp,
364                                 kNotEqual_StencilFunc,
365                                 0xffff,
366                                 0x0000,
367                                 0xffff);
368
369    *fDrawTarget->drawState()->stencil() = kStencilPass;
370
371    SkASSERT(0 == fPendingGlyphCount);
372}
373
374inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x) {
375    SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType);
376
377    if (fPendingGlyphCount >= kGlyphBufferSize) {
378        this->flush();
379    }
380
381    fIndexBuffer[fPendingGlyphCount] = glyphID;
382    fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x;
383
384    ++fPendingGlyphCount;
385}
386
387inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
388    SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType);
389
390    if (fPendingGlyphCount >= kGlyphBufferSize) {
391        this->flush();
392    }
393
394    fIndexBuffer[fPendingGlyphCount] = glyphID;
395    fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
396    fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y;
397
398    ++fPendingGlyphCount;
399}
400
401void GrStencilAndCoverTextContext::flush() {
402    if (0 == fPendingGlyphCount) {
403        return;
404    }
405
406    fDrawTarget->drawPaths(fGlyphs, fIndexBuffer, fPendingGlyphCount,
407                           fTransformBuffer, fTransformType, SkPath::kWinding_FillType);
408
409    fPendingGlyphCount = 0;
410}
411
412void GrStencilAndCoverTextContext::finish() {
413    this->flush();
414
415    fGlyphs->unref();
416    fGlyphs = NULL;
417
418    SkGlyphCache::AttachCache(fGlyphCache);
419    fGlyphCache = NULL;
420
421    fDrawTarget->drawState()->stencil()->setDisabled();
422    fStateRestore.set(NULL);
423    fContext->setMatrix(fContextInitialMatrix);
424    GrTextContext::finish();
425}
426
427