1 
2 /*
3  * Copyright 2014 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include "GrAADistanceFieldPathRenderer.h"
10 
11 #include "GrBatchFlushState.h"
12 #include "GrBatchTest.h"
13 #include "GrContext.h"
14 #include "GrPipelineBuilder.h"
15 #include "GrResourceProvider.h"
16 #include "GrSurfacePriv.h"
17 #include "GrSWMaskHelper.h"
18 #include "GrTexturePriv.h"
19 #include "GrVertexBuffer.h"
20 #include "batches/GrVertexBatch.h"
21 #include "effects/GrDistanceFieldGeoProc.h"
22 
23 #include "SkDistanceFieldGen.h"
24 #include "SkRTConf.h"
25 
26 #define ATLAS_TEXTURE_WIDTH 2048
27 #define ATLAS_TEXTURE_HEIGHT 2048
28 #define PLOT_WIDTH  512
29 #define PLOT_HEIGHT 256
30 
31 #define NUM_PLOTS_X   (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
32 #define NUM_PLOTS_Y   (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
33 
34 #ifdef DF_PATH_TRACKING
35 static int g_NumCachedPaths = 0;
36 static int g_NumFreedPaths = 0;
37 #endif
38 
39 // mip levels
40 static const int kSmallMIP = 32;
41 static const int kMediumMIP = 73;
42 static const int kLargeMIP = 162;
43 
44 // Callback to clear out internal path cache when eviction occurs
HandleEviction(GrBatchAtlas::AtlasID id,void * pr)45 void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
46     GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr;
47     // remove any paths that use this plot
48     PathDataList::Iter iter;
49     iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
50     PathData* pathData;
51     while ((pathData = iter.get())) {
52         iter.next();
53         if (id == pathData->fID) {
54             dfpr->fPathCache.remove(pathData->fKey);
55             dfpr->fPathList.remove(pathData);
56             delete pathData;
57 #ifdef DF_PATH_TRACKING
58             ++g_NumFreedPaths;
59 #endif
60         }
61     }
62 }
63 
64 ////////////////////////////////////////////////////////////////////////////////
GrAADistanceFieldPathRenderer()65 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer() : fAtlas(nullptr) {}
66 
~GrAADistanceFieldPathRenderer()67 GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
68     PathDataList::Iter iter;
69     iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
70     PathData* pathData;
71     while ((pathData = iter.get())) {
72         iter.next();
73         fPathList.remove(pathData);
74         delete pathData;
75     }
76     delete fAtlas;
77 
78 #ifdef DF_PATH_TRACKING
79     SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths);
80 #endif
81 }
82 
83 ////////////////////////////////////////////////////////////////////////////////
onCanDrawPath(const CanDrawPathArgs & args) const84 bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
85 
86     // TODO: Support inverse fill
87     if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias ||
88         SkStrokeRec::kHairline_Style == args.fStroke->getStyle() ||
89         args.fPath->isInverseFillType() || args.fPath->isVolatile() ||
90         // We don't currently apply the dash or factor it into the DF key. (skbug.com/5082)
91         args.fStroke->isDashed()) {
92         return false;
93     }
94 
95     // currently don't support perspective
96     if (args.fViewMatrix->hasPerspective()) {
97         return false;
98     }
99 
100     // only support paths with bounds within kMediumMIP by kMediumMIP,
101     // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP
102     // the goal is to accelerate rendering of lots of small paths that may be scaling
103     SkScalar maxScale = args.fViewMatrix->getMaxScale();
104     const SkRect& bounds = args.fPath->getBounds();
105     SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
106     // Approximate stroked size by adding the maximum of the stroke width or 2x the miter limit
107     if (!args.fStroke->isFillStyle()) {
108         SkScalar extraWidth = args.fStroke->getWidth();
109         if (SkPaint::kMiter_Join == args.fStroke->getJoin()) {
110             extraWidth = SkTMax(extraWidth, 2.0f*args.fStroke->getMiter());
111         }
112         maxDim += extraWidth;
113     }
114 
115     return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP;
116 }
117 
118 ////////////////////////////////////////////////////////////////////////////////
119 
120 // padding around path bounds to allow for antialiased pixels
121 static const SkScalar kAntiAliasPad = 1.0f;
122 
123 class AADistanceFieldPathBatch : public GrVertexBatch {
124 public:
125     DEFINE_BATCH_CLASS_ID
126 
127     typedef GrAADistanceFieldPathRenderer::PathData PathData;
128     typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
129     typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
130 
131     struct Geometry {
GeometryAADistanceFieldPathBatch::Geometry132         Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {
133             if (!stroke.needToApply()) {
134                 // purify unused values to ensure binary equality
135                 fStroke.setStrokeParams(SkPaint::kDefault_Cap, SkPaint::kDefault_Join,
136                                         SkIntToScalar(4));
137                 if (fStroke.getWidth() < 0) {
138                     fStroke.setStrokeStyle(-1.0f);
139                 }
140             }
141         }
142         SkPath fPath;
143         // The unique ID of the path involved in this draw. This may be different than the ID
144         // in fPath since that path may have resulted from a SkStrokeRec::applyToPath call.
145         uint32_t fGenID;
146         SkStrokeRec fStroke;
147         GrColor fColor;
148         bool fAntiAlias;
149     };
150 
Create(const Geometry & geometry,const SkMatrix & viewMatrix,GrBatchAtlas * atlas,PathCache * pathCache,PathDataList * pathList)151     static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
152                                GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) {
153         return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, pathCache, pathList);
154     }
155 
name() const156     const char* name() const override { return "AADistanceFieldPathBatch"; }
157 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const158     void computePipelineOptimizations(GrInitInvariantOutput* color,
159                                       GrInitInvariantOutput* coverage,
160                                       GrBatchToXPOverrides* overrides) const override {
161         color->setKnownFourComponents(fGeoData[0].fColor);
162         coverage->setUnknownSingleComponent();
163     }
164 
165 private:
initBatchTracker(const GrXPOverridesForBatch & overrides)166     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
167         // Handle any color overrides
168         if (!overrides.readsColor()) {
169             fGeoData[0].fColor = GrColor_ILLEGAL;
170         }
171         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
172 
173         // setup batch properties
174         fBatch.fColorIgnored = !overrides.readsColor();
175         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
176         fBatch.fCoverageIgnored = !overrides.readsCoverage();
177     }
178 
179     struct FlushInfo {
180         SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
181         SkAutoTUnref<const GrIndexBuffer>  fIndexBuffer;
182         int fVertexOffset;
183         int fInstancesToFlush;
184     };
185 
onPrepareDraws(Target * target) const186     void onPrepareDraws(Target* target) const override {
187         int instanceCount = fGeoData.count();
188 
189         SkMatrix invert;
190         if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
191             SkDebugf("Could not invert viewmatrix\n");
192             return;
193         }
194 
195         uint32_t flags = 0;
196         flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
197 
198         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
199 
200         // Setup GrGeometryProcessor
201         GrBatchAtlas* atlas = fAtlas;
202         SkAutoTUnref<GrGeometryProcessor> dfProcessor(
203                 GrDistanceFieldPathGeoProc::Create(this->color(),
204                                                    this->viewMatrix(),
205                                                    atlas->getTexture(),
206                                                    params,
207                                                    flags,
208                                                    this->usesLocalCoords()));
209 
210         target->initDraw(dfProcessor, this->pipeline());
211 
212         FlushInfo flushInfo;
213 
214         // allocate vertices
215         size_t vertexStride = dfProcessor->getVertexStride();
216         SkASSERT(vertexStride == 2 * sizeof(SkPoint) + sizeof(GrColor));
217 
218         const GrVertexBuffer* vertexBuffer;
219         void* vertices = target->makeVertexSpace(vertexStride,
220                                                  kVerticesPerQuad * instanceCount,
221                                                  &vertexBuffer,
222                                                  &flushInfo.fVertexOffset);
223         flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
224         flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
225         if (!vertices || !flushInfo.fIndexBuffer) {
226             SkDebugf("Could not allocate vertices\n");
227             return;
228         }
229 
230         flushInfo.fInstancesToFlush = 0;
231         for (int i = 0; i < instanceCount; i++) {
232             const Geometry& args = fGeoData[i];
233 
234             // get mip level
235             SkScalar maxScale = this->viewMatrix().getMaxScale();
236             const SkRect& bounds = args.fPath.getBounds();
237             SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
238             SkScalar size = maxScale * maxDim;
239             uint32_t desiredDimension;
240             if (size <= kSmallMIP) {
241                 desiredDimension = kSmallMIP;
242             } else if (size <= kMediumMIP) {
243                 desiredDimension = kMediumMIP;
244             } else {
245                 desiredDimension = kLargeMIP;
246             }
247 
248             // check to see if path is cached
249             PathData::Key key(args.fGenID, desiredDimension, args.fStroke);
250             PathData* pathData = fPathCache->find(key);
251             if (nullptr == pathData || !atlas->hasID(pathData->fID)) {
252                 // Remove the stale cache entry
253                 if (pathData) {
254                     fPathCache->remove(pathData->fKey);
255                     fPathList->remove(pathData);
256                     delete pathData;
257                 }
258                 SkScalar scale = desiredDimension/maxDim;
259                 pathData = new PathData;
260                 if (!this->addPathToAtlas(target,
261                                           dfProcessor,
262                                           this->pipeline(),
263                                           &flushInfo,
264                                           atlas,
265                                           pathData,
266                                           args.fPath,
267                                           args.fGenID,
268                                           args.fStroke,
269                                           args.fAntiAlias,
270                                           desiredDimension,
271                                           scale)) {
272                     SkDebugf("Can't rasterize path\n");
273                     return;
274                 }
275             }
276 
277             atlas->setLastUseToken(pathData->fID, target->currentToken());
278 
279             // Now set vertices
280             intptr_t offset = reinterpret_cast<intptr_t>(vertices);
281             offset += i * kVerticesPerQuad * vertexStride;
282             this->writePathVertices(target,
283                                     atlas,
284                                     this->pipeline(),
285                                     dfProcessor,
286                                     offset,
287                                     args.fColor,
288                                     vertexStride,
289                                     this->viewMatrix(),
290                                     args.fPath,
291                                     pathData);
292             flushInfo.fInstancesToFlush++;
293         }
294 
295         this->flush(target, &flushInfo);
296     }
297 
geoData()298     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
299 
AADistanceFieldPathBatch(const Geometry & geometry,const SkMatrix & viewMatrix,GrBatchAtlas * atlas,PathCache * pathCache,PathDataList * pathList)300     AADistanceFieldPathBatch(const Geometry& geometry,
301                              const SkMatrix& viewMatrix,
302                              GrBatchAtlas* atlas,
303                              PathCache* pathCache, PathDataList* pathList)
304         : INHERITED(ClassID()) {
305         fBatch.fViewMatrix = viewMatrix;
306         fGeoData.push_back(geometry);
307 
308         fAtlas = atlas;
309         fPathCache = pathCache;
310         fPathList = pathList;
311 
312         // Compute bounds
313         fBounds = geometry.fPath.getBounds();
314         viewMatrix.mapRect(&fBounds);
315     }
316 
addPathToAtlas(GrVertexBatch::Target * target,const GrGeometryProcessor * dfProcessor,const GrPipeline * pipeline,FlushInfo * flushInfo,GrBatchAtlas * atlas,PathData * pathData,const SkPath & path,uint32_t genID,const SkStrokeRec & stroke,bool antiAlias,uint32_t dimension,SkScalar scale) const317     bool addPathToAtlas(GrVertexBatch::Target* target,
318                         const GrGeometryProcessor* dfProcessor,
319                         const GrPipeline* pipeline,
320                         FlushInfo* flushInfo,
321                         GrBatchAtlas* atlas,
322                         PathData* pathData,
323                         const SkPath& path,
324                         uint32_t genID,
325                         const SkStrokeRec& stroke,
326                         bool antiAlias,
327                         uint32_t dimension,
328                         SkScalar scale) const {
329         const SkRect& bounds = path.getBounds();
330 
331         // generate bounding rect for bitmap draw
332         SkRect scaledBounds = bounds;
333         // scale to mip level size
334         scaledBounds.fLeft *= scale;
335         scaledBounds.fTop *= scale;
336         scaledBounds.fRight *= scale;
337         scaledBounds.fBottom *= scale;
338         // move the origin to an integer boundary (gives better results)
339         SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
340         SkScalar dy = SkScalarFraction(scaledBounds.fTop);
341         scaledBounds.offset(-dx, -dy);
342         // get integer boundary
343         SkIRect devPathBounds;
344         scaledBounds.roundOut(&devPathBounds);
345         // pad to allow room for antialiasing
346         const int intPad = SkScalarCeilToInt(kAntiAliasPad);
347         // pre-move origin (after outset, will be 0,0)
348         int width = devPathBounds.width();
349         int height = devPathBounds.height();
350         devPathBounds.fLeft = intPad;
351         devPathBounds.fTop = intPad;
352         devPathBounds.fRight = intPad + width;
353         devPathBounds.fBottom = intPad + height;
354         devPathBounds.outset(intPad, intPad);
355 
356         // draw path to bitmap
357         SkMatrix drawMatrix;
358         drawMatrix.setTranslate(-bounds.left(), -bounds.top());
359         drawMatrix.postScale(scale, scale);
360         drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
361 
362         // setup bitmap backing
363         SkASSERT(devPathBounds.fLeft == 0);
364         SkASSERT(devPathBounds.fTop == 0);
365         SkAutoPixmapStorage dst;
366         if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
367                                               devPathBounds.height()))) {
368             return false;
369         }
370         sk_bzero(dst.writable_addr(), dst.getSafeSize());
371 
372         // rasterize path
373         SkPaint paint;
374         paint.setStyle(SkPaint::kFill_Style);
375         paint.setAntiAlias(antiAlias);
376 
377         SkDraw draw;
378         sk_bzero(&draw, sizeof(draw));
379 
380         SkRasterClip rasterClip;
381         rasterClip.setRect(devPathBounds);
382         draw.fRC = &rasterClip;
383         draw.fClip = &rasterClip.bwRgn();
384         draw.fMatrix = &drawMatrix;
385         draw.fDst = dst;
386 
387         draw.drawPathCoverage(path, paint);
388 
389         // generate signed distance field
390         devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
391         width = devPathBounds.width();
392         height = devPathBounds.height();
393         // TODO We should really generate this directly into the plot somehow
394         SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
395 
396         // Generate signed distance field
397         SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
398                                            (const unsigned char*)dst.addr(),
399                                            dst.width(), dst.height(), dst.rowBytes());
400 
401         // add to atlas
402         SkIPoint16 atlasLocation;
403         GrBatchAtlas::AtlasID id;
404         bool success = atlas->addToAtlas(&id, target, width, height, dfStorage.get(),
405                                          &atlasLocation);
406         if (!success) {
407             this->flush(target, flushInfo);
408             target->initDraw(dfProcessor, pipeline);
409 
410             SkDEBUGCODE(success =) atlas->addToAtlas(&id, target, width, height,
411                                                      dfStorage.get(), &atlasLocation);
412             SkASSERT(success);
413 
414         }
415 
416         // add to cache
417         pathData->fKey = PathData::Key(genID, dimension, stroke);
418         pathData->fScale = scale;
419         pathData->fID = id;
420         // change the scaled rect to match the size of the inset distance field
421         scaledBounds.fRight = scaledBounds.fLeft +
422             SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
423         scaledBounds.fBottom = scaledBounds.fTop +
424             SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
425         // shift the origin to the correct place relative to the distance field
426         // need to also restore the fractional translation
427         scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
428                             -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
429         pathData->fBounds = scaledBounds;
430         // origin we render from is inset from distance field edge
431         atlasLocation.fX += SK_DistanceFieldInset;
432         atlasLocation.fY += SK_DistanceFieldInset;
433         pathData->fAtlasLocation = atlasLocation;
434 
435         fPathCache->add(pathData);
436         fPathList->addToTail(pathData);
437 #ifdef DF_PATH_TRACKING
438         ++g_NumCachedPaths;
439 #endif
440         return true;
441     }
442 
writePathVertices(GrDrawBatch::Target * target,GrBatchAtlas * atlas,const GrPipeline * pipeline,const GrGeometryProcessor * gp,intptr_t offset,GrColor color,size_t vertexStride,const SkMatrix & viewMatrix,const SkPath & path,const PathData * pathData) const443     void writePathVertices(GrDrawBatch::Target* target,
444                            GrBatchAtlas* atlas,
445                            const GrPipeline* pipeline,
446                            const GrGeometryProcessor* gp,
447                            intptr_t offset,
448                            GrColor color,
449                            size_t vertexStride,
450                            const SkMatrix& viewMatrix,
451                            const SkPath& path,
452                            const PathData* pathData) const {
453         GrTexture* texture = atlas->getTexture();
454 
455         SkScalar dx = pathData->fBounds.fLeft;
456         SkScalar dy = pathData->fBounds.fTop;
457         SkScalar width = pathData->fBounds.width();
458         SkScalar height = pathData->fBounds.height();
459 
460         SkScalar invScale = 1.0f / pathData->fScale;
461         dx *= invScale;
462         dy *= invScale;
463         width *= invScale;
464         height *= invScale;
465 
466         SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
467 
468         // vertex positions
469         // TODO make the vertex attributes a struct
470         SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
471         positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
472 
473         // colors
474         for (int i = 0; i < kVerticesPerQuad; i++) {
475             GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride);
476             *colorPtr = color;
477         }
478 
479         const SkScalar tx = SkIntToScalar(pathData->fAtlasLocation.fX);
480         const SkScalar ty = SkIntToScalar(pathData->fAtlasLocation.fY);
481 
482         // vertex texture coords
483         SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
484         textureCoords->setRectFan(tx / texture->width(),
485                                   ty / texture->height(),
486                                   (tx + pathData->fBounds.width()) / texture->width(),
487                                   (ty + pathData->fBounds.height())  / texture->height(),
488                                   vertexStride);
489     }
490 
flush(GrVertexBatch::Target * target,FlushInfo * flushInfo) const491     void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) const {
492         GrVertices vertices;
493         int maxInstancesPerDraw = flushInfo->fIndexBuffer->maxQuads();
494         vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
495             flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad,
496             kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
497         target->draw(vertices);
498         flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
499         flushInfo->fInstancesToFlush = 0;
500     }
501 
color() const502     GrColor color() const { return fGeoData[0].fColor; }
viewMatrix() const503     const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
usesLocalCoords() const504     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
505 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)506     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
507         AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
508         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
509                                     that->bounds(), caps)) {
510             return false;
511         }
512 
513         // TODO We can position on the cpu
514         if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
515             return false;
516         }
517 
518         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
519         this->joinBounds(that->bounds());
520         return true;
521     }
522 
523     struct BatchTracker {
524         SkMatrix fViewMatrix;
525         bool fUsesLocalCoords;
526         bool fColorIgnored;
527         bool fCoverageIgnored;
528     };
529 
530     BatchTracker fBatch;
531     SkSTArray<1, Geometry, true> fGeoData;
532     GrBatchAtlas* fAtlas;
533     PathCache* fPathCache;
534     PathDataList* fPathList;
535 
536     typedef GrVertexBatch INHERITED;
537 };
538 
onDrawPath(const DrawPathArgs & args)539 bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
540     GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),
541                               "GrAADistanceFieldPathRenderer::onDrawPath");
542     // we've already bailed on inverse filled paths, so this is safe
543     if (args.fPath->isEmpty()) {
544         return true;
545     }
546 
547     if (!fAtlas) {
548         fAtlas = args.fResourceProvider->createAtlas(kAlpha_8_GrPixelConfig,
549                                                      ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
550                                                      NUM_PLOTS_X, NUM_PLOTS_Y,
551                                                      &GrAADistanceFieldPathRenderer::HandleEviction,
552                                                      (void*)this);
553         if (!fAtlas) {
554             return false;
555         }
556     }
557 
558     AADistanceFieldPathBatch::Geometry geometry(*args.fStroke);
559     if (SkStrokeRec::kFill_Style == args.fStroke->getStyle()) {
560         geometry.fPath = *args.fPath;
561     } else {
562         args.fStroke->applyToPath(&geometry.fPath, *args.fPath);
563     }
564     geometry.fColor = args.fColor;
565     geometry.fAntiAlias = args.fAntiAlias;
566     // Note: this is the generation ID of the _original_ path. When a new path is
567     // generated due to stroking it is important that the original path's id is used
568     // for caching.
569     geometry.fGenID = args.fPath->getGenerationID();
570 
571     SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry,
572                                                                      *args.fViewMatrix, fAtlas,
573                                                                      &fPathCache, &fPathList));
574     args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
575 
576     return true;
577 }
578 
579 ///////////////////////////////////////////////////////////////////////////////////////////////////
580 
581 #ifdef GR_TEST_UTILS
582 
583 struct PathTestStruct {
584     typedef GrAADistanceFieldPathRenderer::PathCache PathCache;
585     typedef GrAADistanceFieldPathRenderer::PathData PathData;
586     typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
PathTestStructPathTestStruct587     PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
~PathTestStructPathTestStruct588     ~PathTestStruct() { this->reset(); }
589 
resetPathTestStruct590     void reset() {
591         PathDataList::Iter iter;
592         iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
593         PathData* pathData;
594         while ((pathData = iter.get())) {
595             iter.next();
596             fPathList.remove(pathData);
597             delete pathData;
598         }
599         delete fAtlas;
600         fPathCache.reset();
601     }
602 
HandleEvictionPathTestStruct603     static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
604         PathTestStruct* dfpr = (PathTestStruct*)pr;
605         // remove any paths that use this plot
606         PathDataList::Iter iter;
607         iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
608         PathData* pathData;
609         while ((pathData = iter.get())) {
610             iter.next();
611             if (id == pathData->fID) {
612                 dfpr->fPathCache.remove(pathData->fKey);
613                 dfpr->fPathList.remove(pathData);
614                 delete pathData;
615             }
616         }
617     }
618 
619     uint32_t fContextID;
620     GrBatchAtlas* fAtlas;
621     PathCache fPathCache;
622     PathDataList fPathList;
623 };
624 
DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch)625 DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
626     static PathTestStruct gTestStruct;
627 
628     if (context->uniqueID() != gTestStruct.fContextID) {
629         gTestStruct.fContextID = context->uniqueID();
630         gTestStruct.reset();
631         gTestStruct.fAtlas =
632                 context->resourceProvider()->createAtlas(kAlpha_8_GrPixelConfig,
633                                                      ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
634                                                      NUM_PLOTS_X, NUM_PLOTS_Y,
635                                                      &PathTestStruct::HandleEviction,
636                                                      (void*)&gTestStruct);
637     }
638 
639     SkMatrix viewMatrix = GrTest::TestMatrix(random);
640     GrColor color = GrRandomColor(random);
641 
642     AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
643     geometry.fColor = color;
644     geometry.fPath = GrTest::TestPath(random);
645     geometry.fAntiAlias = random->nextBool();
646     geometry.fGenID = random->nextU();
647 
648     return AADistanceFieldPathBatch::Create(geometry, viewMatrix,
649                                             gTestStruct.fAtlas,
650                                             &gTestStruct.fPathCache,
651                                             &gTestStruct.fPathList);
652 }
653 
654 #endif
655