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