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 "GrBatch.h"
12#include "GrBatchTarget.h"
13#include "GrBatchTest.h"
14#include "GrContext.h"
15#include "GrPipelineBuilder.h"
16#include "GrResourceProvider.h"
17#include "GrSurfacePriv.h"
18#include "GrSWMaskHelper.h"
19#include "GrTexturePriv.h"
20#include "GrVertexBuffer.h"
21#include "effects/GrDistanceFieldGeoProc.h"
22
23#include "SkDistanceFieldGen.h"
24#include "SkRTConf.h"
25
26#define ATLAS_TEXTURE_WIDTH 1024
27#define ATLAS_TEXTURE_HEIGHT 2048
28#define PLOT_WIDTH  256
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
35static int g_NumCachedPaths = 0;
36static int g_NumFreedPaths = 0;
37#endif
38
39// mip levels
40static const int kSmallMIP = 32;
41static const int kMediumMIP = 78;
42static const int kLargeMIP = 192;
43
44// Callback to clear out internal path cache when eviction occurs
45void 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            SkDELETE(pathData);
57#ifdef DF_PATH_TRACKING
58            ++g_NumFreedPaths;
59#endif
60        }
61    }
62}
63
64////////////////////////////////////////////////////////////////////////////////
65GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context)
66    : fContext(context)
67    , fAtlas(NULL) {
68}
69
70GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
71    PathDataList::Iter iter;
72    iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
73    PathData* pathData;
74    while ((pathData = iter.get())) {
75        iter.next();
76        fPathList.remove(pathData);
77        SkDELETE(pathData);
78    }
79    SkDELETE(fAtlas);
80
81#ifdef DF_PATH_TRACKING
82    SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths);
83#endif
84}
85
86////////////////////////////////////////////////////////////////////////////////
87bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target,
88                                                const GrPipelineBuilder* pipelineBuilder,
89                                                const SkMatrix& viewMatrix,
90                                                const SkPath& path,
91                                                const GrStrokeInfo& stroke,
92                                                bool antiAlias) const {
93
94    // TODO: Support inverse fill
95    // TODO: Support strokes
96    if (!target->caps()->shaderCaps()->shaderDerivativeSupport() || !antiAlias
97        || path.isInverseFillType() || path.isVolatile() || !stroke.isFillStyle()) {
98        return false;
99    }
100
101    // currently don't support perspective
102    if (viewMatrix.hasPerspective()) {
103        return false;
104    }
105
106    // only support paths smaller than 64x64, scaled to less than 256x256
107    // the goal is to accelerate rendering of lots of small paths that may be scaling
108    SkScalar maxScale = viewMatrix.getMaxScale();
109    const SkRect& bounds = path.getBounds();
110    SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
111    return maxDim < 64.f && maxDim * maxScale < 256.f;
112}
113
114
115GrPathRenderer::StencilSupport
116GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*,
117                                                   const GrPipelineBuilder*,
118                                                   const SkPath&,
119                                                   const GrStrokeInfo&) const {
120    return GrPathRenderer::kNoSupport_StencilSupport;
121}
122
123////////////////////////////////////////////////////////////////////////////////
124
125// padding around path bounds to allow for antialiased pixels
126static const SkScalar kAntiAliasPad = 1.0f;
127
128class AADistanceFieldPathBatch : public GrBatch {
129public:
130    typedef GrAADistanceFieldPathRenderer::PathData PathData;
131    typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
132    typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
133
134    struct Geometry {
135        Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {}
136        SkPath fPath;
137        SkStrokeRec fStroke;
138        bool fAntiAlias;
139        PathData* fPathData;
140    };
141
142    static GrBatch* Create(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
143                           GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) {
144        return SkNEW_ARGS(AADistanceFieldPathBatch, (geometry, color, viewMatrix,
145                                                     atlas, pathCache, pathList));
146    }
147
148    const char* name() const override { return "AADistanceFieldPathBatch"; }
149
150    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
151        out->setKnownFourComponents(fBatch.fColor);
152    }
153
154    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
155        out->setUnknownSingleComponent();
156    }
157
158    void initBatchTracker(const GrPipelineInfo& init) override {
159        // Handle any color overrides
160        if (init.fColorIgnored) {
161            fBatch.fColor = GrColor_ILLEGAL;
162        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
163            fBatch.fColor = init.fOverrideColor;
164        }
165
166        // setup batch properties
167        fBatch.fColorIgnored = init.fColorIgnored;
168        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
169        fBatch.fCoverageIgnored = init.fCoverageIgnored;
170    }
171
172    struct FlushInfo {
173        SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
174        SkAutoTUnref<const GrIndexBuffer>  fIndexBuffer;
175        int fVertexOffset;
176        int fInstancesToFlush;
177    };
178
179    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
180        int instanceCount = fGeoData.count();
181
182        SkMatrix invert;
183        if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
184            SkDebugf("Could not invert viewmatrix\n");
185            return;
186        }
187
188        uint32_t flags = 0;
189        flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
190
191        GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
192
193        // Setup GrGeometryProcessor
194        GrBatchAtlas* atlas = fAtlas;
195        SkAutoTUnref<GrGeometryProcessor> dfProcessor(
196                GrDistanceFieldPathGeoProc::Create(this->color(),
197                                                   this->viewMatrix(),
198                                                   atlas->getTexture(),
199                                                   params,
200                                                   flags));
201
202        this->initDraw(batchTarget, dfProcessor, pipeline);
203
204        FlushInfo flushInfo;
205
206        // allocate vertices
207        size_t vertexStride = dfProcessor->getVertexStride();
208        SkASSERT(vertexStride == 2 * sizeof(SkPoint));
209
210        const GrVertexBuffer* vertexBuffer;
211        void* vertices = batchTarget->makeVertSpace(vertexStride,
212                                                    kVerticesPerQuad * instanceCount,
213                                                    &vertexBuffer,
214                                                    &flushInfo.fVertexOffset);
215        flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
216        flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
217        if (!vertices || !flushInfo.fIndexBuffer) {
218            SkDebugf("Could not allocate vertices\n");
219            return;
220        }
221
222        flushInfo.fInstancesToFlush = 0;
223        for (int i = 0; i < instanceCount; i++) {
224            Geometry& args = fGeoData[i];
225
226            // get mip level
227            SkScalar maxScale = this->viewMatrix().getMaxScale();
228            const SkRect& bounds = args.fPath.getBounds();
229            SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
230            SkScalar size = maxScale * maxDim;
231            uint32_t desiredDimension;
232            if (size <= kSmallMIP) {
233                desiredDimension = kSmallMIP;
234            } else if (size <= kMediumMIP) {
235                desiredDimension = kMediumMIP;
236            } else {
237                desiredDimension = kLargeMIP;
238            }
239
240            // check to see if path is cached
241            // TODO: handle stroked vs. filled version of same path
242            PathData::Key key = { args.fPath.getGenerationID(), desiredDimension };
243            args.fPathData = fPathCache->find(key);
244            if (NULL == args.fPathData || !atlas->hasID(args.fPathData->fID)) {
245                // Remove the stale cache entry
246                if (args.fPathData) {
247                    fPathCache->remove(args.fPathData->fKey);
248                    fPathList->remove(args.fPathData);
249                    SkDELETE(args.fPathData);
250                }
251                SkScalar scale = desiredDimension/maxDim;
252                args.fPathData = SkNEW(PathData);
253                if (!this->addPathToAtlas(batchTarget,
254                                          dfProcessor,
255                                          pipeline,
256                                          &flushInfo,
257                                          atlas,
258                                          args.fPathData,
259                                          args.fPath,
260                                          args.fStroke,
261                                          args.fAntiAlias,
262                                          desiredDimension,
263                                          scale)) {
264                    SkDebugf("Can't rasterize path\n");
265                    return;
266                }
267            }
268
269            atlas->setLastUseToken(args.fPathData->fID, batchTarget->currentToken());
270
271            // Now set vertices
272            intptr_t offset = reinterpret_cast<intptr_t>(vertices);
273            offset += i * kVerticesPerQuad * vertexStride;
274            SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
275            this->writePathVertices(batchTarget,
276                                    atlas,
277                                    pipeline,
278                                    dfProcessor,
279                                    positions,
280                                    vertexStride,
281                                    this->viewMatrix(),
282                                    args.fPath,
283                                    args.fPathData);
284            flushInfo.fInstancesToFlush++;
285        }
286
287        this->flush(batchTarget, &flushInfo);
288    }
289
290    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
291
292private:
293    AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
294                             GrBatchAtlas* atlas,
295                             PathCache* pathCache, PathDataList* pathList) {
296        this->initClassID<AADistanceFieldPathBatch>();
297        fBatch.fColor = color;
298        fBatch.fViewMatrix = viewMatrix;
299        fGeoData.push_back(geometry);
300        fGeoData.back().fPathData = NULL;
301
302        fAtlas = atlas;
303        fPathCache = pathCache;
304        fPathList = pathList;
305
306        // Compute bounds
307        fBounds = geometry.fPath.getBounds();
308        viewMatrix.mapRect(&fBounds);
309    }
310
311    bool addPathToAtlas(GrBatchTarget* batchTarget,
312                        const GrGeometryProcessor* dfProcessor,
313                        const GrPipeline* pipeline,
314                        FlushInfo* flushInfo,
315                        GrBatchAtlas* atlas,
316                        PathData* pathData,
317                        const SkPath& path,
318                        const SkStrokeRec&
319                        stroke, bool antiAlias,
320                        uint32_t dimension,
321                        SkScalar scale) {
322        const SkRect& bounds = path.getBounds();
323
324        // generate bounding rect for bitmap draw
325        SkRect scaledBounds = bounds;
326        // scale to mip level size
327        scaledBounds.fLeft *= scale;
328        scaledBounds.fTop *= scale;
329        scaledBounds.fRight *= scale;
330        scaledBounds.fBottom *= scale;
331        // move the origin to an integer boundary (gives better results)
332        SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
333        SkScalar dy = SkScalarFraction(scaledBounds.fTop);
334        scaledBounds.offset(-dx, -dy);
335        // get integer boundary
336        SkIRect devPathBounds;
337        scaledBounds.roundOut(&devPathBounds);
338        // pad to allow room for antialiasing
339        devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad));
340        // move origin to upper left corner
341        devPathBounds.offsetTo(0,0);
342
343        // draw path to bitmap
344        SkMatrix drawMatrix;
345        drawMatrix.setTranslate(-bounds.left(), -bounds.top());
346        drawMatrix.postScale(scale, scale);
347        drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
348
349        // setup bitmap backing
350        // Now translate so the bound's UL corner is at the origin
351        drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1,
352                                 -devPathBounds.fTop * SK_Scalar1);
353        SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(),
354                                             devPathBounds.height());
355
356        SkBitmap bmp;
357        const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(pathBounds.fRight,
358                                                            pathBounds.fBottom);
359        if (!bmp.tryAllocPixels(bmImageInfo)) {
360            return false;
361        }
362
363        sk_bzero(bmp.getPixels(), bmp.getSafeSize());
364
365        // rasterize path
366        SkPaint paint;
367        if (stroke.isHairlineStyle()) {
368            paint.setStyle(SkPaint::kStroke_Style);
369            paint.setStrokeWidth(SK_Scalar1);
370        } else {
371            if (stroke.isFillStyle()) {
372                paint.setStyle(SkPaint::kFill_Style);
373            } else {
374                paint.setStyle(SkPaint::kStroke_Style);
375                paint.setStrokeJoin(stroke.getJoin());
376                paint.setStrokeCap(stroke.getCap());
377                paint.setStrokeWidth(stroke.getWidth());
378            }
379        }
380        paint.setAntiAlias(antiAlias);
381
382        SkDraw draw;
383        sk_bzero(&draw, sizeof(draw));
384
385        SkRasterClip rasterClip;
386        rasterClip.setRect(pathBounds);
387        draw.fRC = &rasterClip;
388        draw.fClip = &rasterClip.bwRgn();
389        draw.fMatrix = &drawMatrix;
390        draw.fBitmap = &bmp;
391
392        draw.drawPathCoverage(path, paint);
393
394        // generate signed distance field
395        devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
396        int width = devPathBounds.width();
397        int height = devPathBounds.height();
398        // TODO We should really generate this directly into the plot somehow
399        SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
400
401        // Generate signed distance field
402        {
403            SkAutoLockPixels alp(bmp);
404
405            SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
406                                               (const unsigned char*)bmp.getPixels(),
407                                               bmp.width(), bmp.height(), bmp.rowBytes());
408        }
409
410        // add to atlas
411        SkIPoint16 atlasLocation;
412        GrBatchAtlas::AtlasID id;
413        bool success = atlas->addToAtlas(&id, batchTarget, width, height, dfStorage.get(),
414                                         &atlasLocation);
415        if (!success) {
416            this->flush(batchTarget, flushInfo);
417            this->initDraw(batchTarget, dfProcessor, pipeline);
418
419            SkDEBUGCODE(success =) atlas->addToAtlas(&id, batchTarget, width, height,
420                                                     dfStorage.get(), &atlasLocation);
421            SkASSERT(success);
422
423        }
424
425        // add to cache
426        pathData->fKey.fGenID = path.getGenerationID();
427        pathData->fKey.fDimension = dimension;
428        pathData->fScale = scale;
429        pathData->fID = id;
430        // change the scaled rect to match the size of the inset distance field
431        scaledBounds.fRight = scaledBounds.fLeft +
432            SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
433        scaledBounds.fBottom = scaledBounds.fTop +
434            SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
435        // shift the origin to the correct place relative to the distance field
436        // need to also restore the fractional translation
437        scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
438                            -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
439        pathData->fBounds = scaledBounds;
440        // origin we render from is inset from distance field edge
441        atlasLocation.fX += SK_DistanceFieldInset;
442        atlasLocation.fY += SK_DistanceFieldInset;
443        pathData->fAtlasLocation = atlasLocation;
444
445        fPathCache->add(pathData);
446        fPathList->addToTail(pathData);
447#ifdef DF_PATH_TRACKING
448        ++g_NumCachedPaths;
449#endif
450        return true;
451    }
452
453    void writePathVertices(GrBatchTarget* target,
454                           GrBatchAtlas* atlas,
455                           const GrPipeline* pipeline,
456                           const GrGeometryProcessor* gp,
457                           SkPoint* positions,
458                           size_t vertexStride,
459                           const SkMatrix& viewMatrix,
460                           const SkPath& path,
461                           const PathData* pathData) {
462        GrTexture* texture = atlas->getTexture();
463
464        SkScalar dx = pathData->fBounds.fLeft;
465        SkScalar dy = pathData->fBounds.fTop;
466        SkScalar width = pathData->fBounds.width();
467        SkScalar height = pathData->fBounds.height();
468
469        SkScalar invScale = 1.0f / pathData->fScale;
470        dx *= invScale;
471        dy *= invScale;
472        width *= invScale;
473        height *= invScale;
474
475        SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX);
476        SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY);
477        SkFixed tw = SkScalarToFixed(pathData->fBounds.width());
478        SkFixed th = SkScalarToFixed(pathData->fBounds.height());
479
480        // vertex positions
481        // TODO make the vertex attributes a struct
482        SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
483        positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
484
485        // vertex texture coords
486        SkPoint* textureCoords = positions + 1;
487        textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
488                                  SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
489                                  SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
490                                  SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
491                                  vertexStride);
492    }
493
494    void initDraw(GrBatchTarget* batchTarget,
495                  const GrGeometryProcessor* dfProcessor,
496                  const GrPipeline* pipeline) {
497        batchTarget->initDraw(dfProcessor, pipeline);
498
499        // TODO remove this when batch is everywhere
500        GrPipelineInfo init;
501        init.fColorIgnored = fBatch.fColorIgnored;
502        init.fOverrideColor = GrColor_ILLEGAL;
503        init.fCoverageIgnored = fBatch.fCoverageIgnored;
504        init.fUsesLocalCoords = this->usesLocalCoords();
505        dfProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
506    }
507
508    void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
509        GrVertices vertices;
510        int maxInstancesPerDraw = flushInfo->fIndexBuffer->maxQuads();
511        vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
512            flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad,
513            kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
514        batchTarget->draw(vertices);
515        flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
516        flushInfo->fInstancesToFlush = 0;
517    }
518
519    GrColor color() const { return fBatch.fColor; }
520    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
521    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
522
523    bool onCombineIfPossible(GrBatch* t) override {
524        AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
525
526        // TODO we could actually probably do a bunch of this work on the CPU, ie map viewMatrix,
527        // maybe upload color via attribute
528        if (this->color() != that->color()) {
529            return false;
530        }
531
532        if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
533            return false;
534        }
535
536        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
537        this->joinBounds(that->bounds());
538        return true;
539    }
540
541    struct BatchTracker {
542        GrColor fColor;
543        SkMatrix fViewMatrix;
544        bool fUsesLocalCoords;
545        bool fColorIgnored;
546        bool fCoverageIgnored;
547    };
548
549    BatchTracker fBatch;
550    SkSTArray<1, Geometry, true> fGeoData;
551    GrBatchAtlas* fAtlas;
552    PathCache* fPathCache;
553    PathDataList* fPathList;
554};
555
556static GrBatchAtlas* create_atlas(GrContext* context, GrBatchAtlas::EvictionFunc func, void* data) {
557    GrBatchAtlas* atlas;
558    // Create a new atlas
559    GrSurfaceDesc desc;
560    desc.fFlags = kNone_GrSurfaceFlags;
561    desc.fWidth = ATLAS_TEXTURE_WIDTH;
562    desc.fHeight = ATLAS_TEXTURE_HEIGHT;
563    desc.fConfig = kAlpha_8_GrPixelConfig;
564
565    // We don't want to flush the context so we claim we're in the middle of flushing so as to
566    // guarantee we do not recieve a texture with pending IO
567    GrTexture* texture = context->textureProvider()->refScratchTexture(
568        desc, GrTextureProvider::kApprox_ScratchTexMatch, true);
569    if (texture) {
570        atlas = SkNEW_ARGS(GrBatchAtlas, (texture, NUM_PLOTS_X, NUM_PLOTS_Y));
571    } else {
572        return NULL;
573    }
574    atlas->registerEvictionCallback(func, data);
575    return atlas;
576}
577
578bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target,
579                                               GrPipelineBuilder* pipelineBuilder,
580                                               GrColor color,
581                                               const SkMatrix& viewMatrix,
582                                               const SkPath& path,
583                                               const GrStrokeInfo& stroke,
584                                               bool antiAlias) {
585    // we've already bailed on inverse filled paths, so this is safe
586    if (path.isEmpty()) {
587        return true;
588    }
589
590    SkASSERT(fContext);
591
592    if (!fAtlas) {
593        fAtlas = create_atlas(fContext, &GrAADistanceFieldPathRenderer::HandleEviction,
594                              (void*)this);
595        if (!fAtlas) {
596            return false;
597        }
598    }
599
600    AADistanceFieldPathBatch::Geometry geometry(stroke.getStrokeRec());
601    geometry.fPath = path;
602    geometry.fAntiAlias = antiAlias;
603
604    SkAutoTUnref<GrBatch> batch(AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
605                                                                 fAtlas, &fPathCache, &fPathList));
606    target->drawBatch(pipelineBuilder, batch);
607
608    return true;
609}
610
611///////////////////////////////////////////////////////////////////////////////////////////////////
612
613#ifdef GR_TEST_UTILS
614
615struct PathTestStruct {
616    typedef GrAADistanceFieldPathRenderer::PathCache PathCache;
617    typedef GrAADistanceFieldPathRenderer::PathData PathData;
618    typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
619    PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(NULL) {}
620    ~PathTestStruct() { this->reset(); }
621
622    void reset() {
623        PathDataList::Iter iter;
624        iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
625        PathData* pathData;
626        while ((pathData = iter.get())) {
627            iter.next();
628            fPathList.remove(pathData);
629            SkDELETE(pathData);
630        }
631        SkDELETE(fAtlas);
632        fPathCache.reset();
633    }
634
635    static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
636        PathTestStruct* dfpr = (PathTestStruct*)pr;
637        // remove any paths that use this plot
638        PathDataList::Iter iter;
639        iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
640        PathData* pathData;
641        while ((pathData = iter.get())) {
642            iter.next();
643            if (id == pathData->fID) {
644                dfpr->fPathCache.remove(pathData->fKey);
645                dfpr->fPathList.remove(pathData);
646                SkDELETE(pathData);
647            }
648        }
649    }
650
651    uint32_t fContextID;
652    GrBatchAtlas* fAtlas;
653    PathCache fPathCache;
654    PathDataList fPathList;
655};
656
657BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
658    static PathTestStruct gTestStruct;
659
660    if (context->uniqueID() != gTestStruct.fContextID) {
661        gTestStruct.fContextID = context->uniqueID();
662        gTestStruct.reset();
663        gTestStruct.fAtlas = create_atlas(context, &PathTestStruct::HandleEviction,
664                                          (void*)&gTestStruct);
665    }
666
667    SkMatrix viewMatrix = GrTest::TestMatrix(random);
668    GrColor color = GrRandomColor(random);
669
670    AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
671    geometry.fPath = GrTest::TestPath(random);
672    geometry.fAntiAlias = random->nextBool();
673
674    return AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
675                                            gTestStruct.fAtlas,
676                                            &gTestStruct.fPathCache,
677                                            &gTestStruct.fPathList);
678}
679
680#endif
681