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