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
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 = 78;
42 static const int kLargeMIP = 192;
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             SkDELETE(pathData);
57 #ifdef DF_PATH_TRACKING
58             ++g_NumFreedPaths;
59 #endif
60         }
61     }
62 }
63 
64 ////////////////////////////////////////////////////////////////////////////////
GrAADistanceFieldPathRenderer(GrContext * context)65 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context)
66     : fContext(context)
67     , fAtlas(NULL) {
68 }
69 
~GrAADistanceFieldPathRenderer()70 GrAADistanceFieldPathRenderer::~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 ////////////////////////////////////////////////////////////////////////////////
canDrawPath(const GrDrawTarget * target,const GrPipelineBuilder * pipelineBuilder,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool antiAlias) const87 bool 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 
115 GrPathRenderer::StencilSupport
onGetStencilSupport(const GrDrawTarget *,const GrPipelineBuilder *,const SkPath &,const GrStrokeInfo &) const116 GrAADistanceFieldPathRenderer::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
126 static const SkScalar kAntiAliasPad = 1.0f;
127 
128 class AADistanceFieldPathBatch : public GrBatch {
129 public:
130     typedef GrAADistanceFieldPathRenderer::PathData PathData;
131     typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
132     typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
133 
134     struct Geometry {
GeometryAADistanceFieldPathBatch::Geometry135         Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {}
136         SkPath fPath;
137         SkStrokeRec fStroke;
138         bool fAntiAlias;
139         PathData* fPathData;
140     };
141 
Create(const Geometry & geometry,GrColor color,const SkMatrix & viewMatrix,GrBatchAtlas * atlas,PathCache * pathCache,PathDataList * pathList)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 
name() const148     const char* name() const override { return "AADistanceFieldPathBatch"; }
149 
getInvariantOutputColor(GrInitInvariantOutput * out) const150     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
151         out->setKnownFourComponents(fBatch.fColor);
152     }
153 
getInvariantOutputCoverage(GrInitInvariantOutput * out) const154     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
155         out->setUnknownSingleComponent();
156     }
157 
initBatchTracker(const GrPipelineInfo & init)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 
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)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 
geoData()290     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
291 
292 private:
AADistanceFieldPathBatch(const Geometry & geometry,GrColor color,const SkMatrix & viewMatrix,GrBatchAtlas * atlas,PathCache * pathCache,PathDataList * pathList)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 
addPathToAtlas(GrBatchTarget * batchTarget,const GrGeometryProcessor * dfProcessor,const GrPipeline * pipeline,FlushInfo * flushInfo,GrBatchAtlas * atlas,PathData * pathData,const SkPath & path,const SkStrokeRec & stroke,bool antiAlias,uint32_t dimension,SkScalar scale)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 
writePathVertices(GrBatchTarget * target,GrBatchAtlas * atlas,const GrPipeline * pipeline,const GrGeometryProcessor * gp,SkPoint * positions,size_t vertexStride,const SkMatrix & viewMatrix,const SkPath & path,const PathData * pathData)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 
initDraw(GrBatchTarget * batchTarget,const GrGeometryProcessor * dfProcessor,const GrPipeline * pipeline)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 
flush(GrBatchTarget * batchTarget,FlushInfo * flushInfo)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 
color() const519     GrColor color() const { return fBatch.fColor; }
viewMatrix() const520     const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
usesLocalCoords() const521     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
522 
onCombineIfPossible(GrBatch * t)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 
create_atlas(GrContext * context,GrBatchAtlas::EvictionFunc func,void * data)556 static 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 
onDrawPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool antiAlias)578 bool 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 
615 struct PathTestStruct {
616     typedef GrAADistanceFieldPathRenderer::PathCache PathCache;
617     typedef GrAADistanceFieldPathRenderer::PathData PathData;
618     typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
PathTestStructPathTestStruct619     PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(NULL) {}
~PathTestStructPathTestStruct620     ~PathTestStruct() { this->reset(); }
621 
resetPathTestStruct622     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 
HandleEvictionPathTestStruct635     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 
BATCH_TEST_DEFINE(AADistanceFieldPathBatch)657 BATCH_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