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