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