GrStencilAndCoverTextContext.cpp revision da152ebb032c61bb2561bbb3e9a78037ad174d34
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 "GrAtlasTextContext.h"
10#include "GrContext.h"
11#include "GrDrawContext.h"
12#include "GrPath.h"
13#include "GrPathRange.h"
14#include "GrPipelineBuilder.h"
15#include "GrResourceProvider.h"
16#include "GrTextUtils.h"
17#include "SkAutoKern.h"
18#include "SkDraw.h"
19#include "SkDrawProcs.h"
20#include "SkGlyphCache.h"
21#include "SkGrPriv.h"
22#include "SkDrawFilter.h"
23#include "SkPath.h"
24#include "SkTextBlobRunIterator.h"
25#include "SkTextMapStateProc.h"
26#include "SkTextFormatParams.h"
27
28#include "batches/GrDrawPathBatch.h"
29
30template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
31    SkASSERT(*val);
32    delete *val;
33}
34
35template<typename T> static void delete_hash_table_entry(T* val) {
36    SkASSERT(*val);
37    delete *val;
38}
39
40GrStencilAndCoverTextContext::GrStencilAndCoverTextContext()
41    : fFallbackTextContext(nullptr)
42    , fCacheSize(0) {
43}
44
45GrStencilAndCoverTextContext*
46GrStencilAndCoverTextContext::Create() {
47    GrStencilAndCoverTextContext* textContext = new GrStencilAndCoverTextContext();
48    textContext->fFallbackTextContext = GrAtlasTextContext::Create();
49
50    return textContext;
51}
52
53GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
54    delete fFallbackTextContext;
55    fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
56    fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
57}
58
59bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
60    if (skPaint.getRasterizer()) {
61        return false;
62    }
63    if (skPaint.getMaskFilter()) {
64        return false;
65    }
66    if (SkPathEffect* pe = skPaint.getPathEffect()) {
67        if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
68            return false;
69        }
70    }
71    // No hairlines. They would require new paths with customized strokes for every new draw matrix.
72    return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
73}
74
75void GrStencilAndCoverTextContext::drawText(GrContext* context, GrDrawContext* dc,
76                                            const GrClip& clip, const GrPaint& paint,
77                                            const SkPaint& skPaint, const SkMatrix& viewMatrix,
78                                            const SkSurfaceProps& props,
79                                            const char text[], size_t byteLength,
80                                            SkScalar x, SkScalar y, const SkIRect& clipBounds) {
81    if (context->abandoned()) {
82        return;
83    } else if (this->canDraw(skPaint, viewMatrix)) {
84        if (skPaint.getTextSize() > 0) {
85            TextRun run(skPaint);
86            run.setText(text, byteLength, x, y);
87            run.draw(context, dc, paint, clip, viewMatrix, props, 0, 0,
88                     clipBounds, fFallbackTextContext, skPaint);
89        }
90        return;
91    } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
92                                             *context->caps()->shaderCaps())) {
93        fFallbackTextContext->drawText(context, dc, clip, paint, skPaint, viewMatrix, props, text,
94                                       byteLength, x, y, clipBounds);
95        return;
96    }
97
98    // fall back to drawing as a path
99    GrTextUtils::DrawTextAsPath(context, dc, clip, skPaint, viewMatrix, text, byteLength, x, y,
100                                clipBounds);
101}
102
103void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrDrawContext* dc,
104                                               const GrClip& clip,
105                                               const GrPaint& paint,
106                                               const SkPaint& skPaint,
107                                               const SkMatrix& viewMatrix,
108                                               const SkSurfaceProps& props,
109                                               const char text[],
110                                               size_t byteLength,
111                                               const SkScalar pos[],
112                                               int scalarsPerPosition,
113                                               const SkPoint& offset,
114                                               const SkIRect& clipBounds) {
115    if (context->abandoned()) {
116        return;
117    } else if (this->canDraw(skPaint, viewMatrix)) {
118        if (skPaint.getTextSize() > 0) {
119            TextRun run(skPaint);
120            run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
121            run.draw(context, dc, paint, clip, viewMatrix, props, 0, 0,
122                     clipBounds, fFallbackTextContext, skPaint);
123        }
124        return;
125    } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
126                                             *context->caps()->shaderCaps())) {
127        fFallbackTextContext->drawPosText(context, dc, clip, paint, skPaint, viewMatrix, props,
128                                          text, byteLength, pos,
129                                          scalarsPerPosition, offset, clipBounds);
130        return;
131    }
132
133    // fall back to drawing as a path
134    GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, skPaint, viewMatrix, text,
135                                   byteLength, pos, scalarsPerPosition, offset, clipBounds);
136}
137
138void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
139                                                        GrDrawContext* dc,
140                                                        const GrClip& clip, const SkPaint& skPaint,
141                                                        const SkMatrix& viewMatrix,
142                                                        const SkSurfaceProps& props,
143                                                        const SkTextBlob* blob,
144                                                        SkScalar x, SkScalar y,
145                                                        SkDrawFilter* drawFilter,
146                                                        const SkIRect& clipBounds) {
147    SkPaint runPaint = skPaint;
148
149    SkTextBlobRunIterator it(blob);
150    for (;!it.done(); it.next()) {
151        size_t textLen = it.glyphCount() * sizeof(uint16_t);
152        const SkPoint& offset = it.offset();
153
154        // applyFontToPaint() always overwrites the exact same attributes,
155        // so it is safe to not re-seed the paint for this reason.
156        it.applyFontToPaint(&runPaint);
157
158        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
159            // A false return from filter() means we should abort the current draw.
160            runPaint = skPaint;
161            continue;
162        }
163
164        runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint));
165
166        GrPaint grPaint;
167        if (!SkPaintToGrPaint(context, runPaint, viewMatrix, dc->isGammaCorrect(), &grPaint)) {
168            return;
169        }
170
171        switch (it.positioning()) {
172            case SkTextBlob::kDefault_Positioning:
173                this->drawText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
174                               (const char *)it.glyphs(),
175                               textLen, x + offset.x(), y + offset.y(), clipBounds);
176                break;
177            case SkTextBlob::kHorizontal_Positioning:
178                this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
179                                  (const char*)it.glyphs(),
180                                  textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
181                                  clipBounds);
182                break;
183            case SkTextBlob::kFull_Positioning:
184                this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
185                                  (const char*)it.glyphs(),
186                                  textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
187                break;
188        }
189
190        if (drawFilter) {
191            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
192            runPaint = skPaint;
193        }
194    }
195}
196
197void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrDrawContext* dc,
198                                                const GrClip& clip, const SkPaint& skPaint,
199                                                const SkMatrix& viewMatrix,
200                                                const SkSurfaceProps& props,
201                                                const SkTextBlob* skBlob, SkScalar x, SkScalar y,
202                                                SkDrawFilter* drawFilter,
203                                                const SkIRect& clipBounds) {
204    if (context->abandoned()) {
205        return;
206    }
207
208    if (!this->internalCanDraw(skPaint)) {
209        fFallbackTextContext->drawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob,
210                                           x, y, drawFilter, clipBounds);
211        return;
212    }
213
214    if (drawFilter || skPaint.getPathEffect()) {
215        // This draw can't be cached.
216        this->uncachedDrawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob, x, y,
217                                   drawFilter, clipBounds);
218        return;
219    }
220
221    GrPaint paint;
222    if (!SkPaintToGrPaint(context, skPaint, viewMatrix, dc->isGammaCorrect(), &paint)) {
223        return;
224    }
225
226    const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
227
228    TextBlob::Iter iter(blob);
229    for (TextRun* run = iter.get(); run; run = iter.next()) {
230        // The run's "font" overrides the anti-aliasing of the passed in paint!
231        paint.setAntiAlias(run->isAntiAlias());
232        run->draw(context, dc, paint, clip, viewMatrix, props,  x, y,
233                  clipBounds, fFallbackTextContext, skPaint);
234        run->releaseGlyphCache();
235    }
236}
237
238static inline int style_key_cnt(const GrStyle& style) {
239    int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec);
240    // We should be able to make a key because we filtered out arbitrary path effects.
241    SkASSERT(cnt > 0);
242    return cnt;
243}
244
245static inline void write_style_key(uint32_t* dst, const GrStyle& style) {
246    // Pass 1 for the scale since the GPU will apply the style not GrStyle::applyToPath().
247    GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Scalar1);
248}
249
250const GrStencilAndCoverTextContext::TextBlob&
251GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
252                                                   const SkPaint& skPaint) {
253    // The font-related parameters are baked into the text blob and will override this skPaint, so
254    // the only remaining properties that can affect a TextBlob are the ones related to stroke.
255    if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
256        if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
257            fLRUList.remove(*found);
258            fLRUList.addToTail(*found);
259            return **found;
260        }
261        TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
262        this->purgeToFit(*blob);
263        fBlobIdCache.set(skBlob->uniqueID(), blob);
264        fLRUList.addToTail(blob);
265        fCacheSize += blob->cpuMemorySize();
266        return *blob;
267    } else {
268        GrStyle style(skPaint);
269        SkSTArray<4, uint32_t, true> key;
270        key.reset(1 + style_key_cnt(style));
271        key[0] = skBlob->uniqueID();
272        write_style_key(&key[1], style);
273        if (TextBlob** found = fBlobKeyCache.find(key)) {
274            fLRUList.remove(*found);
275            fLRUList.addToTail(*found);
276            return **found;
277        }
278        TextBlob* blob = new TextBlob(key, skBlob, skPaint);
279        this->purgeToFit(*blob);
280        fBlobKeyCache.set(blob);
281        fLRUList.addToTail(blob);
282        fCacheSize += blob->cpuMemorySize();
283        return *blob;
284    }
285}
286
287void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
288    static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
289
290    size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
291    while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
292        TextBlob* lru = fLRUList.head();
293        if (1 == lru->key().count()) {
294            // 1-length keys are unterstood to be the blob id.
295            fBlobIdCache.remove(lru->key()[0]);
296        } else {
297            fBlobKeyCache.remove(lru->key());
298        }
299        fLRUList.remove(lru);
300        fCacheSize -= lru->cpuMemorySize();
301        delete lru;
302    }
303}
304
305////////////////////////////////////////////////////////////////////////////////////////////////////
306
307void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
308                                                  const SkPaint& skPaint) {
309    fCpuMemorySize = sizeof(TextBlob);
310    SkPaint runPaint(skPaint);
311    for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
312        iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
313        if (runPaint.getTextSize() <= 0) {
314            continue;
315        }
316        TextRun* run = this->addToTail(runPaint);
317
318        const char* text = reinterpret_cast<const char*>(iter.glyphs());
319        size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
320        const SkPoint& runOffset = iter.offset();
321
322        switch (iter.positioning()) {
323            case SkTextBlob::kDefault_Positioning:
324                run->setText(text, byteLength, runOffset.fX, runOffset.fY);
325                break;
326            case SkTextBlob::kHorizontal_Positioning:
327                run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
328                break;
329            case SkTextBlob::kFull_Positioning:
330                run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
331                break;
332        }
333
334        fCpuMemorySize += run->computeSizeInCache();
335    }
336}
337
338////////////////////////////////////////////////////////////////////////////////////////////////////
339
340class GrStencilAndCoverTextContext::FallbackBlobBuilder {
341public:
342    FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
343
344    bool isInitialized() const { return fBuilder != nullptr; }
345
346    void init(const SkPaint& font, SkScalar textRatio);
347
348    void appendGlyph(uint16_t glyphId, const SkPoint& pos);
349
350    const SkTextBlob* buildIfNeeded(int* count);
351
352private:
353    enum { kWriteBufferSize = 1024 };
354
355    void flush();
356
357    SkAutoTDelete<SkTextBlobBuilder>   fBuilder;
358    SkPaint                            fFont;
359    int                                fBuffIdx;
360    int                                fCount;
361    uint16_t                           fGlyphIds[kWriteBufferSize];
362    SkPoint                            fPositions[kWriteBufferSize];
363};
364
365////////////////////////////////////////////////////////////////////////////////////////////////////
366
367GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
368    : fStyle(fontAndStroke)
369    , fFont(fontAndStroke)
370    , fTotalGlyphCount(0)
371    , fFallbackGlyphCount(0)
372    , fDetachedGlyphCache(nullptr)
373    , fLastDrawnGlyphsID(SK_InvalidUniqueID) {
374    SkASSERT(fFont.getTextSize() > 0);
375    SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported.
376    SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported.
377
378    // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
379    // rendering API for stroking).
380    fFont.setStyle(SkPaint::kFill_Style);
381
382    if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) {
383        const SkStrokeRec& stroke = fStyle.strokeRec();
384        // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
385        SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
386                                                    kStdFakeBoldInterpKeys,
387                                                    kStdFakeBoldInterpValues,
388                                                    kStdFakeBoldInterpLength);
389        SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
390
391        SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
392        strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra,
393                                 true /*strokeAndFill*/);
394        fStyle = GrStyle(strokeRec, fStyle.pathEffect());
395        fFont.setFakeBoldText(false);
396    }
397
398    if (!fFont.getPathEffect() && !fStyle.isDashed()) {
399        const SkStrokeRec& stroke = fStyle.strokeRec();
400        // We can draw the glyphs from canonically sized paths.
401        fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
402        fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
403
404        // Compensate for the glyphs being scaled by fTextRatio.
405        if (!fStyle.isSimpleFill()) {
406            SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
407            strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio,
408                                     SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle());
409            fStyle = GrStyle(strokeRec, fStyle.pathEffect());
410        }
411
412        fFont.setLinearText(true);
413        fFont.setLCDRenderText(false);
414        fFont.setAutohinted(false);
415        fFont.setHinting(SkPaint::kNo_Hinting);
416        fFont.setSubpixelText(true);
417        fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
418
419        fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
420                              0 == fFont.getTextSkewX() &&
421                              !fFont.isFakeBoldText() &&
422                              !fFont.isVerticalText();
423    } else {
424        fTextRatio = fTextInverseRatio = 1.0f;
425        fUsingRawGlyphPaths = false;
426    }
427
428    // Generate the key that will be used to cache the GPU glyph path objects.
429    if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) {
430        static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
431
432        const SkTypeface* typeface = fFont.getTypeface();
433        GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
434        reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
435    } else {
436        static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
437
438        int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
439        // Key should be valid since we opted out of drawing arbitrary path effects.
440        SkASSERT(styleDataCount >= 0);
441        if (fUsingRawGlyphPaths) {
442            const SkTypeface* typeface = fFont.getTypeface();
443            GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount);
444            reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
445            reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount;
446            if (styleDataCount) {
447                write_style_key(&builder[2], fStyle);
448            }
449        } else {
450            SkGlyphCache* glyphCache = this->getGlyphCache();
451            const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
452            const SkDescriptor* desc = &glyphCache->getDescriptor();
453            int descDataCount = (desc->getLength() + 3) / 4;
454            GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
455                                         2 + styleDataCount + descDataCount);
456            reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
457            reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16);
458            if (styleDataCount) {
459                write_style_key(&builder[2], fStyle);
460            }
461            memcpy(&builder[2 + styleDataCount], desc, desc->getLength());
462        }
463    }
464}
465
466GrStencilAndCoverTextContext::TextRun::~TextRun() {
467    this->releaseGlyphCache();
468}
469
470void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
471                                                    SkScalar x, SkScalar y) {
472    SkASSERT(byteLength == 0 || text != nullptr);
473
474    SkGlyphCache* glyphCache = this->getGlyphCache();
475    SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
476                                                                        fFont.isDevKernText(),
477                                                                        true);
478
479    fTotalGlyphCount = fFont.countText(text, byteLength);
480    fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
481                                            fTotalGlyphCount));
482
483    const char* stop = text + byteLength;
484
485    // Measure first if needed.
486    if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
487        SkScalar   stopX = 0;
488        SkScalar   stopY = 0;
489
490        const char* textPtr = text;
491        while (textPtr < stop) {
492            // We don't need x, y here, since all subpixel variants will have the
493            // same advance.
494            const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
495
496            stopX += SkFloatToScalar(glyph.fAdvanceX);
497            stopY += SkFloatToScalar(glyph.fAdvanceY);
498        }
499        SkASSERT(textPtr == stop);
500
501        SkScalar alignX = stopX * fTextRatio;
502        SkScalar alignY = stopY * fTextRatio;
503
504        if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
505            alignX = SkScalarHalf(alignX);
506            alignY = SkScalarHalf(alignY);
507        }
508
509        x -= alignX;
510        y -= alignY;
511    }
512
513    SkAutoKern autokern;
514
515    FallbackBlobBuilder fallback;
516    while (text < stop) {
517        const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
518        x += autokern.adjust(glyph) * fTextRatio;
519        if (glyph.fWidth) {
520            this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
521        }
522
523        x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
524        y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
525    }
526
527    fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
528}
529
530void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
531                                                       const SkScalar pos[], int scalarsPerPosition,
532                                                       const SkPoint& offset) {
533    SkASSERT(byteLength == 0 || text != nullptr);
534    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
535
536    SkGlyphCache* glyphCache = this->getGlyphCache();
537    SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
538                                                                        fFont.isDevKernText(),
539                                                                        true);
540
541    fTotalGlyphCount = fFont.countText(text, byteLength);
542    fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
543                                            fTotalGlyphCount));
544
545    const char* stop = text + byteLength;
546
547    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
548    SkTextAlignProc alignProc(fFont.getTextAlign());
549    FallbackBlobBuilder fallback;
550    while (text < stop) {
551        const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
552        if (glyph.fWidth) {
553            SkPoint tmsLoc;
554            tmsProc(pos, &tmsLoc);
555            SkPoint loc;
556            alignProc(tmsLoc, glyph, &loc);
557
558            this->appendGlyph(glyph, loc, &fallback);
559        }
560        pos += scalarsPerPosition;
561    }
562
563    fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
564}
565
566GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
567    GrPathRange* glyphs = static_cast<GrPathRange*>(
568            ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
569    if (nullptr == glyphs) {
570        if (fUsingRawGlyphPaths) {
571            SkScalerContextEffects noeffects;
572            glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), noeffects,
573                                                           nullptr, fStyle);
574        } else {
575            SkGlyphCache* cache = this->getGlyphCache();
576            glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
577                                                           cache->getScalerContext()->getEffects(),
578                                                           &cache->getDescriptor(),
579                                                           fStyle);
580        }
581        ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
582    }
583    return glyphs;
584}
585
586inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
587                                                               const SkPoint& pos,
588                                                               FallbackBlobBuilder* fallback) {
589    // Stick the glyphs we can't draw into the fallback text blob.
590    if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
591        if (!fallback->isInitialized()) {
592            fallback->init(fFont, fTextRatio);
593        }
594        fallback->appendGlyph(glyph.getGlyphID(), pos);
595    } else {
596        fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
597                              fTextInverseRatio * pos.y());
598    }
599}
600
601void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
602                                                 GrDrawContext* drawContext,
603                                                 const GrPaint& grPaint,
604                                                 const GrClip& clip,
605                                                 const SkMatrix& viewMatrix,
606                                                 const SkSurfaceProps& props,
607                                                 SkScalar x, SkScalar y,
608                                                 const SkIRect& clipBounds,
609                                                 GrAtlasTextContext* fallbackTextContext,
610                                                 const SkPaint& originalSkPaint) const {
611    SkASSERT(fInstanceData);
612    SkASSERT(drawContext->isStencilBufferMultisampled() || !grPaint.isAntiAlias());
613
614    if (fInstanceData->count()) {
615        static constexpr GrUserStencilSettings kCoverPass(
616            GrUserStencilSettings::StaticInit<
617                0x0000,
618                GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
619                0xffff,
620                GrUserStencilOp::kZero,
621                GrUserStencilOp::kKeep,
622                0xffff>()
623        );
624
625        SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
626        if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
627            // Either this is the first draw or the glyphs object was purged since last draw.
628            glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
629            fLastDrawnGlyphsID = glyphs->getUniqueID();
630        }
631
632        // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
633        // the entire dst. Realistically this is a moot point, because any context that supports
634        // NV_path_rendering will also support NV_blend_equation_advanced.
635        // For clipping we'll just skip any optimizations based on the bounds. This does, however,
636        // hurt batching.
637        const SkRect bounds = SkRect::MakeIWH(drawContext->width(), drawContext->height());
638
639        SkAutoTUnref<GrDrawBatch> batch(
640            GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x,
641                                         fTextInverseRatio * y, grPaint.getColor(),
642                                         GrPathRendering::kWinding_FillType, glyphs, fInstanceData,
643                                         bounds));
644
645        GrPipelineBuilder pipelineBuilder(grPaint);
646        pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, grPaint.isAntiAlias());
647        pipelineBuilder.setUserStencil(&kCoverPass);
648
649        drawContext->drawBatch(pipelineBuilder, clip, batch);
650    }
651
652    if (fFallbackTextBlob) {
653        SkPaint fallbackSkPaint(originalSkPaint);
654        fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
655        if (!fStyle.isSimpleFill()) {
656            fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
657        }
658
659        fallbackTextContext->drawTextBlob(ctx, drawContext, clip, fallbackSkPaint, viewMatrix,
660                                          props, fFallbackTextBlob, x, y, nullptr, clipBounds);
661    }
662}
663
664SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
665    if (!fDetachedGlyphCache) {
666        fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
667                                                nullptr);
668    }
669    return fDetachedGlyphCache;
670}
671
672
673void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
674    if (fDetachedGlyphCache) {
675        SkGlyphCache::AttachCache(fDetachedGlyphCache);
676        fDetachedGlyphCache = nullptr;
677    }
678}
679
680size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
681    size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
682    // The instance data always reserves enough space for every glyph.
683    size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
684    if (fInstanceData) {
685        size += sizeof(InstanceData);
686    }
687    if (fFallbackTextBlob) {
688        size += sizeof(SkTextBlob);
689    }
690    return size;
691}
692
693////////////////////////////////////////////////////////////////////////////////////////////////////
694
695void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
696                                                             SkScalar textRatio) {
697    SkASSERT(!this->isInitialized());
698    fBuilder.reset(new SkTextBlobBuilder);
699    fFont = font;
700    fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
701    fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
702    // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
703    // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
704    fFont.setSubpixelText(false);
705    fFont.setTextSize(fFont.getTextSize() * textRatio);
706    fBuffIdx = 0;
707}
708
709void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
710                                                                    const SkPoint& pos) {
711    SkASSERT(this->isInitialized());
712    if (fBuffIdx >= kWriteBufferSize) {
713        this->flush();
714    }
715    fGlyphIds[fBuffIdx] = glyphId;
716    fPositions[fBuffIdx] = pos;
717    fBuffIdx++;
718    fCount++;
719}
720
721void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
722    SkASSERT(this->isInitialized());
723    SkASSERT(fBuffIdx <= kWriteBufferSize);
724    if (!fBuffIdx) {
725        return;
726    }
727    // This will automatically merge with previous runs since we use the same font.
728    const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
729    memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
730    memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
731    fBuffIdx = 0;
732}
733
734const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) {
735    *count = fCount;
736    if (fCount) {
737        this->flush();
738        return fBuilder->build();
739    }
740    return nullptr;
741}
742