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