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