• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
delete_hash_map_entry(const Key &,Val * val)29 template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
30     SkASSERT(*val);
31     delete *val;
32 }
33 
delete_hash_table_entry(T * val)34 template<typename T> static void delete_hash_table_entry(T* val) {
35     SkASSERT(*val);
36     delete *val;
37 }
38 
GrStencilAndCoverTextContext(GrAtlasTextContext * fallbackTextContext)39 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrAtlasTextContext* fallbackTextContext)
40     : fFallbackTextContext(fallbackTextContext)
41     , fCacheSize(0) {
42 }
43 
44 GrStencilAndCoverTextContext*
Create(GrAtlasTextContext * fallbackTextContext)45 GrStencilAndCoverTextContext::Create(GrAtlasTextContext* fallbackTextContext) {
46     return new GrStencilAndCoverTextContext(fallbackTextContext);;
47 }
48 
~GrStencilAndCoverTextContext()49 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
50     fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
51     fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
52 }
53 
internalCanDraw(const SkPaint & skPaint)54 bool 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 
drawText(GrContext * context,GrRenderTargetContext * rtc,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const char text[],size_t byteLength,SkScalar x,SkScalar y,const SkIRect & clipBounds)70 void 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 
drawPosText(GrContext * context,GrRenderTargetContext * rtc,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset,const SkIRect & clipBounds)97 void 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 
uncachedDrawTextBlob(GrContext * context,GrRenderTargetContext * rtc,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkTextBlob * blob,SkScalar x,SkScalar y,SkDrawFilter * drawFilter,const SkIRect & clipBounds)126 void 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 
drawTextBlob(GrContext * context,GrRenderTargetContext * rtc,const GrClip & clip,const SkPaint & skPaint,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkTextBlob * skBlob,SkScalar x,SkScalar y,SkDrawFilter * drawFilter,const SkIRect & clipBounds)166 void 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 
style_key_cnt(const GrStyle & style)201 static 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 
write_style_key(uint32_t * dst,const GrStyle & style)208 static 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 
213 const GrStencilAndCoverTextContext::TextBlob&
findOrCreateTextBlob(const SkTextBlob * skBlob,const SkPaint & skPaint)214 GrStencilAndCoverTextContext::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 
purgeToFit(const TextBlob & blob)250 void 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 
init(const SkTextBlob * skBlob,const SkPaint & skPaint)270 void 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 
303 class GrStencilAndCoverTextContext::FallbackBlobBuilder {
304 public:
FallbackBlobBuilder()305     FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
306 
isInitialized() const307     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 
315 private:
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 
TextRun(const SkPaint & fontAndStroke)330 GrStencilAndCoverTextContext::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 
~TextRun()429 GrStencilAndCoverTextContext::TextRun::~TextRun() {
430     this->releaseGlyphCache();
431 }
432 
setText(const char text[],size_t byteLength,SkScalar x,SkScalar y)433 void 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 
setPosText(const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset)493 void 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 
createGlyphs(GrResourceProvider * resourceProvider) const529 sk_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 
appendGlyph(const SkGlyph & glyph,const SkPoint & pos,FallbackBlobBuilder * fallback)552 inline 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 
draw(GrContext * ctx,GrRenderTargetContext * renderTargetContext,const GrClip & clip,const SkMatrix & viewMatrix,const SkSurfaceProps & props,SkScalar x,SkScalar y,const SkIRect & clipBounds,GrAtlasTextContext * fallbackTextContext,const SkPaint & originalSkPaint) const567 void 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 
getGlyphCache() const632 SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
633     if (!fDetachedGlyphCache) {
634         fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
635                                                 nullptr);
636     }
637     return fDetachedGlyphCache;
638 }
639 
640 
releaseGlyphCache() const641 void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
642     if (fDetachedGlyphCache) {
643         SkGlyphCache::AttachCache(fDetachedGlyphCache);
644         fDetachedGlyphCache = nullptr;
645     }
646 }
647 
computeSizeInCache() const648 size_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 
init(const SkPaint & font,SkScalar textRatio)663 void 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 
appendGlyph(uint16_t glyphId,const SkPoint & pos)677 void 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 
flush()689 void 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 
makeIfNeeded(int * count)702 sk_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