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