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