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