• 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 "SkPointPriv.h"
24 #include "SkTextBlobRunIterator.h"
25 #include "SkTextFormatParams.h"
26 #include "SkTextMapStateProc.h"
27 
28 #include "ops/GrDrawPathOp.h"
29 
delete_hash_map_entry(const Key &,Val * val)30 template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
31     SkASSERT(*val);
32     delete *val;
33 }
34 
delete_hash_table_entry(T * val)35 template<typename T> static void delete_hash_table_entry(T* val) {
36     SkASSERT(*val);
37     delete *val;
38 }
39 
GrStencilAndCoverTextContext(GrAtlasTextContext * fallbackTextContext)40 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrAtlasTextContext* fallbackTextContext)
41     : fFallbackTextContext(fallbackTextContext)
42     , fCacheSize(0) {
43 }
44 
45 GrStencilAndCoverTextContext*
Create(GrAtlasTextContext * fallbackTextContext)46 GrStencilAndCoverTextContext::Create(GrAtlasTextContext* fallbackTextContext) {
47     return new GrStencilAndCoverTextContext(fallbackTextContext);;
48 }
49 
~GrStencilAndCoverTextContext()50 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
51     fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
52     fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
53 }
54 
internalCanDraw(const SkPaint & skPaint)55 bool 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 
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)68 void 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 
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)88 void 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 
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)111 void 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 
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)151 void 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 
style_key_cnt(const GrStyle & style)186 static 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 
write_style_key(uint32_t * dst,const GrStyle & style)193 static 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 
198 const GrStencilAndCoverTextContext::TextBlob&
findOrCreateTextBlob(const SkTextBlob * skBlob,const SkPaint & skPaint)199 GrStencilAndCoverTextContext::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 
purgeToFit(const TextBlob & blob)235 void 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 
init(const SkTextBlob * skBlob,const SkPaint & skPaint)255 void 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 
288 class GrStencilAndCoverTextContext::FallbackBlobBuilder {
289 public:
FallbackBlobBuilder()290     FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
291 
isInitialized() const292     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 
300 private:
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 
TextRun(const SkPaint & fontAndStroke)315 GrStencilAndCoverTextContext::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 
~TextRun()414 GrStencilAndCoverTextContext::TextRun::~TextRun() {
415     this->releaseGlyphCache();
416 }
417 
setText(const char text[],size_t byteLength,SkScalar x,SkScalar y)418 void 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 
setPosText(const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset)478 void 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 
createGlyphs(GrResourceProvider * resourceProvider) const514 sk_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 
appendGlyph(const SkGlyph & glyph,const SkPoint & pos,FallbackBlobBuilder * fallback)535 inline 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 
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) const550 void 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 
getGlyphCache() const606 SkGlyphCache* 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 
releaseGlyphCache() const616 void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
617     if (fDetachedGlyphCache) {
618         SkGlyphCache::AttachCache(fDetachedGlyphCache);
619         fDetachedGlyphCache = nullptr;
620     }
621 }
622 
computeSizeInCache() const623 size_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 
init(const SkPaint & font,SkScalar textRatio)638 void 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 
appendGlyph(uint16_t glyphId,const SkPoint & pos)652 void 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 
flush()664 void 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 
makeIfNeeded(int * count)677 sk_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