OLD | NEW |
| (Empty) |
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 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 | |
45 void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, voi
d* 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 //////////////////////////////////////////////////////////////////////////////// | |
65 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer() : fAtlas(nullptr)
{} | |
66 | |
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_NumFreed
Paths); | |
80 #endif | |
81 } | |
82 | |
83 //////////////////////////////////////////////////////////////////////////////// | |
84 bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c
onst { | |
85 | |
86 // TODO: Support inverse fill | |
87 // TODO: Support strokes | |
88 if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias || | |
89 args.fPath->isInverseFillType() || args.fPath->isVolatile() || | |
90 !args.fStroke->isFillStyle()) { | |
91 return false; | |
92 } | |
93 | |
94 // currently don't support perspective | |
95 if (args.fViewMatrix->hasPerspective()) { | |
96 return false; | |
97 } | |
98 | |
99 // only support paths smaller than 64x64, scaled to less than 256x256 | |
100 // the goal is to accelerate rendering of lots of small paths that may be sc
aling | |
101 SkScalar maxScale = args.fViewMatrix->getMaxScale(); | |
102 const SkRect& bounds = args.fPath->getBounds(); | |
103 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); | |
104 return maxDim < 64.f && maxDim * maxScale < 256.f; | |
105 } | |
106 | |
107 //////////////////////////////////////////////////////////////////////////////// | |
108 | |
109 // padding around path bounds to allow for antialiased pixels | |
110 static const SkScalar kAntiAliasPad = 1.0f; | |
111 | |
112 class AADistanceFieldPathBatch : public GrVertexBatch { | |
113 public: | |
114 typedef GrAADistanceFieldPathRenderer::PathData PathData; | |
115 typedef SkTDynamicHash<PathData, PathData::Key> PathCache; | |
116 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList; | |
117 | |
118 struct Geometry { | |
119 Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {} | |
120 SkPath fPath; | |
121 SkStrokeRec fStroke; | |
122 bool fAntiAlias; | |
123 PathData* fPathData; | |
124 }; | |
125 | |
126 static GrDrawBatch* Create(const Geometry& geometry, GrColor color, const Sk
Matrix& viewMatrix, | |
127 GrBatchAtlas* atlas, PathCache* pathCache, PathDa
taList* pathList) { | |
128 return new AADistanceFieldPathBatch(geometry, color, viewMatrix, atlas,
pathCache, | |
129 pathList); | |
130 } | |
131 | |
132 const char* name() const override { return "AADistanceFieldPathBatch"; } | |
133 | |
134 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { | |
135 out->setKnownFourComponents(fBatch.fColor); | |
136 } | |
137 | |
138 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { | |
139 out->setUnknownSingleComponent(); | |
140 } | |
141 | |
142 private: | |
143 void initBatchTracker(const GrPipelineOptimizations& opt) override { | |
144 // Handle any color overrides | |
145 if (!opt.readsColor()) { | |
146 fBatch.fColor = GrColor_ILLEGAL; | |
147 } | |
148 opt.getOverrideColorIfSet(&fBatch.fColor); | |
149 | |
150 // setup batch properties | |
151 fBatch.fColorIgnored = !opt.readsColor(); | |
152 fBatch.fUsesLocalCoords = opt.readsLocalCoords(); | |
153 fBatch.fCoverageIgnored = !opt.readsCoverage(); | |
154 } | |
155 | |
156 struct FlushInfo { | |
157 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer; | |
158 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer; | |
159 int fVertexOffset; | |
160 int fInstancesToFlush; | |
161 }; | |
162 | |
163 void onPrepareDraws(Target* target) override { | |
164 int instanceCount = fGeoData.count(); | |
165 | |
166 SkMatrix invert; | |
167 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { | |
168 SkDebugf("Could not invert viewmatrix\n"); | |
169 return; | |
170 } | |
171 | |
172 uint32_t flags = 0; | |
173 flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEf
fectFlag : 0; | |
174 | |
175 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBil
erp_FilterMode); | |
176 | |
177 // Setup GrGeometryProcessor | |
178 GrBatchAtlas* atlas = fAtlas; | |
179 SkAutoTUnref<GrGeometryProcessor> dfProcessor( | |
180 GrDistanceFieldPathGeoProc::Create(this->color(), | |
181 this->viewMatrix(), | |
182 atlas->getTexture(), | |
183 params, | |
184 flags, | |
185 this->usesLocalCoords())); | |
186 | |
187 target->initDraw(dfProcessor, this->pipeline()); | |
188 | |
189 FlushInfo flushInfo; | |
190 | |
191 // allocate vertices | |
192 size_t vertexStride = dfProcessor->getVertexStride(); | |
193 SkASSERT(vertexStride == 2 * sizeof(SkPoint)); | |
194 | |
195 const GrVertexBuffer* vertexBuffer; | |
196 void* vertices = target->makeVertexSpace(vertexStride, | |
197 kVerticesPerQuad * instanceCoun
t, | |
198 &vertexBuffer, | |
199 &flushInfo.fVertexOffset); | |
200 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer)); | |
201 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuf
fer()); | |
202 if (!vertices || !flushInfo.fIndexBuffer) { | |
203 SkDebugf("Could not allocate vertices\n"); | |
204 return; | |
205 } | |
206 | |
207 flushInfo.fInstancesToFlush = 0; | |
208 for (int i = 0; i < instanceCount; i++) { | |
209 Geometry& args = fGeoData[i]; | |
210 | |
211 // get mip level | |
212 SkScalar maxScale = this->viewMatrix().getMaxScale(); | |
213 const SkRect& bounds = args.fPath.getBounds(); | |
214 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); | |
215 SkScalar size = maxScale * maxDim; | |
216 uint32_t desiredDimension; | |
217 if (size <= kSmallMIP) { | |
218 desiredDimension = kSmallMIP; | |
219 } else if (size <= kMediumMIP) { | |
220 desiredDimension = kMediumMIP; | |
221 } else { | |
222 desiredDimension = kLargeMIP; | |
223 } | |
224 | |
225 // check to see if path is cached | |
226 // TODO: handle stroked vs. filled version of same path | |
227 PathData::Key key = { args.fPath.getGenerationID(), desiredDimension
}; | |
228 args.fPathData = fPathCache->find(key); | |
229 if (nullptr == args.fPathData || !atlas->hasID(args.fPathData->fID))
{ | |
230 // Remove the stale cache entry | |
231 if (args.fPathData) { | |
232 fPathCache->remove(args.fPathData->fKey); | |
233 fPathList->remove(args.fPathData); | |
234 delete args.fPathData; | |
235 } | |
236 SkScalar scale = desiredDimension/maxDim; | |
237 args.fPathData = new PathData; | |
238 if (!this->addPathToAtlas(target, | |
239 dfProcessor, | |
240 this->pipeline(), | |
241 &flushInfo, | |
242 atlas, | |
243 args.fPathData, | |
244 args.fPath, | |
245 args.fStroke, | |
246 args.fAntiAlias, | |
247 desiredDimension, | |
248 scale)) { | |
249 SkDebugf("Can't rasterize path\n"); | |
250 return; | |
251 } | |
252 } | |
253 | |
254 atlas->setLastUseToken(args.fPathData->fID, target->currentToken()); | |
255 | |
256 // Now set vertices | |
257 intptr_t offset = reinterpret_cast<intptr_t>(vertices); | |
258 offset += i * kVerticesPerQuad * vertexStride; | |
259 SkPoint* positions = reinterpret_cast<SkPoint*>(offset); | |
260 this->writePathVertices(target, | |
261 atlas, | |
262 this->pipeline(), | |
263 dfProcessor, | |
264 positions, | |
265 vertexStride, | |
266 this->viewMatrix(), | |
267 args.fPath, | |
268 args.fPathData); | |
269 flushInfo.fInstancesToFlush++; | |
270 } | |
271 | |
272 this->flush(target, &flushInfo); | |
273 } | |
274 | |
275 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
276 | |
277 AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMa
trix& viewMatrix, | |
278 GrBatchAtlas* atlas, | |
279 PathCache* pathCache, PathDataList* pathList) { | |
280 this->initClassID<AADistanceFieldPathBatch>(); | |
281 fBatch.fColor = color; | |
282 fBatch.fViewMatrix = viewMatrix; | |
283 fGeoData.push_back(geometry); | |
284 fGeoData.back().fPathData = nullptr; | |
285 | |
286 fAtlas = atlas; | |
287 fPathCache = pathCache; | |
288 fPathList = pathList; | |
289 | |
290 // Compute bounds | |
291 fBounds = geometry.fPath.getBounds(); | |
292 viewMatrix.mapRect(&fBounds); | |
293 } | |
294 | |
295 bool addPathToAtlas(GrVertexBatch::Target* target, | |
296 const GrGeometryProcessor* dfProcessor, | |
297 const GrPipeline* pipeline, | |
298 FlushInfo* flushInfo, | |
299 GrBatchAtlas* atlas, | |
300 PathData* pathData, | |
301 const SkPath& path, | |
302 const SkStrokeRec& | |
303 stroke, bool antiAlias, | |
304 uint32_t dimension, | |
305 SkScalar scale) { | |
306 const SkRect& bounds = path.getBounds(); | |
307 | |
308 // generate bounding rect for bitmap draw | |
309 SkRect scaledBounds = bounds; | |
310 // scale to mip level size | |
311 scaledBounds.fLeft *= scale; | |
312 scaledBounds.fTop *= scale; | |
313 scaledBounds.fRight *= scale; | |
314 scaledBounds.fBottom *= scale; | |
315 // move the origin to an integer boundary (gives better results) | |
316 SkScalar dx = SkScalarFraction(scaledBounds.fLeft); | |
317 SkScalar dy = SkScalarFraction(scaledBounds.fTop); | |
318 scaledBounds.offset(-dx, -dy); | |
319 // get integer boundary | |
320 SkIRect devPathBounds; | |
321 scaledBounds.roundOut(&devPathBounds); | |
322 // pad to allow room for antialiasing | |
323 devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt
(kAntiAliasPad)); | |
324 // move origin to upper left corner | |
325 devPathBounds.offsetTo(0,0); | |
326 | |
327 // draw path to bitmap | |
328 SkMatrix drawMatrix; | |
329 drawMatrix.setTranslate(-bounds.left(), -bounds.top()); | |
330 drawMatrix.postScale(scale, scale); | |
331 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); | |
332 | |
333 // setup bitmap backing | |
334 // Now translate so the bound's UL corner is at the origin | |
335 drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1, | |
336 -devPathBounds.fTop * SK_Scalar1); | |
337 SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(), | |
338 devPathBounds.height()); | |
339 | |
340 SkAutoPixmapStorage dst; | |
341 if (!dst.tryAlloc(SkImageInfo::MakeA8(pathBounds.width(), | |
342 pathBounds.height()))) { | |
343 return false; | |
344 } | |
345 sk_bzero(dst.writable_addr(), dst.getSafeSize()); | |
346 | |
347 // rasterize path | |
348 SkPaint paint; | |
349 if (stroke.isHairlineStyle()) { | |
350 paint.setStyle(SkPaint::kStroke_Style); | |
351 paint.setStrokeWidth(SK_Scalar1); | |
352 } else { | |
353 if (stroke.isFillStyle()) { | |
354 paint.setStyle(SkPaint::kFill_Style); | |
355 } else { | |
356 paint.setStyle(SkPaint::kStroke_Style); | |
357 paint.setStrokeJoin(stroke.getJoin()); | |
358 paint.setStrokeCap(stroke.getCap()); | |
359 paint.setStrokeWidth(stroke.getWidth()); | |
360 } | |
361 } | |
362 paint.setAntiAlias(antiAlias); | |
363 | |
364 SkDraw draw; | |
365 sk_bzero(&draw, sizeof(draw)); | |
366 | |
367 SkRasterClip rasterClip; | |
368 rasterClip.setRect(pathBounds); | |
369 draw.fRC = &rasterClip; | |
370 draw.fClip = &rasterClip.bwRgn(); | |
371 draw.fMatrix = &drawMatrix; | |
372 draw.fDst = dst; | |
373 | |
374 draw.drawPathCoverage(path, paint); | |
375 | |
376 // generate signed distance field | |
377 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); | |
378 int width = devPathBounds.width(); | |
379 int height = devPathBounds.height(); | |
380 // TODO We should really generate this directly into the plot somehow | |
381 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); | |
382 | |
383 // Generate signed distance field | |
384 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(), | |
385 (const unsigned char*)dst.addr(), | |
386 dst.width(), dst.height(), dst.rowByt
es()); | |
387 | |
388 // add to atlas | |
389 SkIPoint16 atlasLocation; | |
390 GrBatchAtlas::AtlasID id; | |
391 bool success = atlas->addToAtlas(&id, target, width, height, dfStorage.g
et(), | |
392 &atlasLocation); | |
393 if (!success) { | |
394 this->flush(target, flushInfo); | |
395 target->initDraw(dfProcessor, pipeline); | |
396 | |
397 SkDEBUGCODE(success =) atlas->addToAtlas(&id, target, width, height, | |
398 dfStorage.get(), &atlasLoca
tion); | |
399 SkASSERT(success); | |
400 | |
401 } | |
402 | |
403 // add to cache | |
404 pathData->fKey.fGenID = path.getGenerationID(); | |
405 pathData->fKey.fDimension = dimension; | |
406 pathData->fScale = scale; | |
407 pathData->fID = id; | |
408 // change the scaled rect to match the size of the inset distance field | |
409 scaledBounds.fRight = scaledBounds.fLeft + | |
410 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); | |
411 scaledBounds.fBottom = scaledBounds.fTop + | |
412 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); | |
413 // shift the origin to the correct place relative to the distance field | |
414 // need to also restore the fractional translation | |
415 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa
d + dx, | |
416 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa
d + dy); | |
417 pathData->fBounds = scaledBounds; | |
418 // origin we render from is inset from distance field edge | |
419 atlasLocation.fX += SK_DistanceFieldInset; | |
420 atlasLocation.fY += SK_DistanceFieldInset; | |
421 pathData->fAtlasLocation = atlasLocation; | |
422 | |
423 fPathCache->add(pathData); | |
424 fPathList->addToTail(pathData); | |
425 #ifdef DF_PATH_TRACKING | |
426 ++g_NumCachedPaths; | |
427 #endif | |
428 return true; | |
429 } | |
430 | |
431 void writePathVertices(GrDrawBatch::Target* target, | |
432 GrBatchAtlas* atlas, | |
433 const GrPipeline* pipeline, | |
434 const GrGeometryProcessor* gp, | |
435 SkPoint* positions, | |
436 size_t vertexStride, | |
437 const SkMatrix& viewMatrix, | |
438 const SkPath& path, | |
439 const PathData* pathData) { | |
440 GrTexture* texture = atlas->getTexture(); | |
441 | |
442 SkScalar dx = pathData->fBounds.fLeft; | |
443 SkScalar dy = pathData->fBounds.fTop; | |
444 SkScalar width = pathData->fBounds.width(); | |
445 SkScalar height = pathData->fBounds.height(); | |
446 | |
447 SkScalar invScale = 1.0f / pathData->fScale; | |
448 dx *= invScale; | |
449 dy *= invScale; | |
450 width *= invScale; | |
451 height *= invScale; | |
452 | |
453 SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX); | |
454 SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY); | |
455 SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); | |
456 SkFixed th = SkScalarToFixed(pathData->fBounds.height()); | |
457 | |
458 // vertex positions | |
459 // TODO make the vertex attributes a struct | |
460 SkRect r = SkRect::MakeXYWH(dx, dy, width, height); | |
461 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexSt
ride); | |
462 | |
463 // vertex texture coords | |
464 SkPoint* textureCoords = positions + 1; | |
465 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normaliz
eFixedX(tx)), | |
466 SkFixedToFloat(texture->texturePriv().normaliz
eFixedY(ty)), | |
467 SkFixedToFloat(texture->texturePriv().normaliz
eFixedX(tx + tw)), | |
468 SkFixedToFloat(texture->texturePriv().normaliz
eFixedY(ty + th)), | |
469 vertexStride); | |
470 } | |
471 | |
472 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) { | |
473 GrVertices vertices; | |
474 int maxInstancesPerDraw = flushInfo->fIndexBuffer->maxQuads(); | |
475 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuf
fer, | |
476 flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad, | |
477 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw); | |
478 target->draw(vertices); | |
479 flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFl
ush; | |
480 flushInfo->fInstancesToFlush = 0; | |
481 } | |
482 | |
483 GrColor color() const { return fBatch.fColor; } | |
484 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } | |
485 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } | |
486 | |
487 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { | |
488 AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>(); | |
489 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pi
peline(), | |
490 that->bounds(), caps)) { | |
491 return false; | |
492 } | |
493 | |
494 // TODO we could actually probably do a bunch of this work on the CPU, i
e map viewMatrix, | |
495 // maybe upload color via attribute | |
496 if (this->color() != that->color()) { | |
497 return false; | |
498 } | |
499 | |
500 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { | |
501 return false; | |
502 } | |
503 | |
504 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin())
; | |
505 this->joinBounds(that->bounds()); | |
506 return true; | |
507 } | |
508 | |
509 struct BatchTracker { | |
510 GrColor fColor; | |
511 SkMatrix fViewMatrix; | |
512 bool fUsesLocalCoords; | |
513 bool fColorIgnored; | |
514 bool fCoverageIgnored; | |
515 }; | |
516 | |
517 BatchTracker fBatch; | |
518 SkSTArray<1, Geometry, true> fGeoData; | |
519 GrBatchAtlas* fAtlas; | |
520 PathCache* fPathCache; | |
521 PathDataList* fPathList; | |
522 }; | |
523 | |
524 bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) { | |
525 // we've already bailed on inverse filled paths, so this is safe | |
526 if (args.fPath->isEmpty()) { | |
527 return true; | |
528 } | |
529 | |
530 if (!fAtlas) { | |
531 fAtlas = args.fResourceProvider->createAtlas(kAlpha_8_GrPixelConfig, | |
532 ATLAS_TEXTURE_WIDTH, ATLAS_
TEXTURE_HEIGHT, | |
533 NUM_PLOTS_X, NUM_PLOTS_Y, | |
534 &GrAADistanceFieldPathRende
rer::HandleEviction, | |
535 (void*)this); | |
536 if (!fAtlas) { | |
537 return false; | |
538 } | |
539 } | |
540 | |
541 AADistanceFieldPathBatch::Geometry geometry(*args.fStroke); | |
542 geometry.fPath = *args.fPath; | |
543 geometry.fAntiAlias = args.fAntiAlias; | |
544 | |
545 SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry, a
rgs.fColor, | |
546 *args.fView
Matrix, fAtlas, | |
547 &fPathCache
, &fPathList)); | |
548 args.fTarget->drawBatch(*args.fPipelineBuilder, batch); | |
549 | |
550 return true; | |
551 } | |
552 | |
553 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | |
554 | |
555 #ifdef GR_TEST_UTILS | |
556 | |
557 struct PathTestStruct { | |
558 typedef GrAADistanceFieldPathRenderer::PathCache PathCache; | |
559 typedef GrAADistanceFieldPathRenderer::PathData PathData; | |
560 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList; | |
561 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {} | |
562 ~PathTestStruct() { this->reset(); } | |
563 | |
564 void reset() { | |
565 PathDataList::Iter iter; | |
566 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | |
567 PathData* pathData; | |
568 while ((pathData = iter.get())) { | |
569 iter.next(); | |
570 fPathList.remove(pathData); | |
571 delete pathData; | |
572 } | |
573 delete fAtlas; | |
574 fPathCache.reset(); | |
575 } | |
576 | |
577 static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) { | |
578 PathTestStruct* dfpr = (PathTestStruct*)pr; | |
579 // remove any paths that use this plot | |
580 PathDataList::Iter iter; | |
581 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart); | |
582 PathData* pathData; | |
583 while ((pathData = iter.get())) { | |
584 iter.next(); | |
585 if (id == pathData->fID) { | |
586 dfpr->fPathCache.remove(pathData->fKey); | |
587 dfpr->fPathList.remove(pathData); | |
588 delete pathData; | |
589 } | |
590 } | |
591 } | |
592 | |
593 uint32_t fContextID; | |
594 GrBatchAtlas* fAtlas; | |
595 PathCache fPathCache; | |
596 PathDataList fPathList; | |
597 }; | |
598 | |
599 DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) { | |
600 static PathTestStruct gTestStruct; | |
601 | |
602 if (context->uniqueID() != gTestStruct.fContextID) { | |
603 gTestStruct.fContextID = context->uniqueID(); | |
604 gTestStruct.reset(); | |
605 gTestStruct.fAtlas = | |
606 context->resourceProvider()->createAtlas(kAlpha_8_GrPixelConfig, | |
607 ATLAS_TEXTURE_WIDTH, ATLAS_
TEXTURE_HEIGHT, | |
608 NUM_PLOTS_X, NUM_PLOTS_Y, | |
609 &PathTestStruct::HandleEvic
tion, | |
610 (void*)&gTestStruct); | |
611 } | |
612 | |
613 SkMatrix viewMatrix = GrTest::TestMatrix(random); | |
614 GrColor color = GrRandomColor(random); | |
615 | |
616 AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random)); | |
617 geometry.fPath = GrTest::TestPath(random); | |
618 geometry.fAntiAlias = random->nextBool(); | |
619 | |
620 return AADistanceFieldPathBatch::Create(geometry, color, viewMatrix, | |
621 gTestStruct.fAtlas, | |
622 &gTestStruct.fPathCache, | |
623 &gTestStruct.fPathList); | |
624 } | |
625 | |
626 #endif | |
OLD | NEW |