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