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