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