| Index: src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
|
| diff --git a/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp b/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
|
| index ee5fdb8a804f23fc88d88e4c96e8d02542fdb866..bb388d7af9ae8f8c89df87d14654fd619ecc362a 100644
|
| --- a/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
|
| +++ b/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
|
| @@ -38,7 +38,7 @@ static int g_NumFreedPaths = 0;
|
|
|
| // mip levels
|
| static const int kSmallMIP = 32;
|
| -static const int kMediumMIP = 72;
|
| +static const int kMediumMIP = 73;
|
| static const int kLargeMIP = 162;
|
|
|
| // Callback to clear out internal path cache when eviction occurs
|
| @@ -84,10 +84,9 @@ GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
|
| bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
|
|
|
| // TODO: Support inverse fill
|
| - // TODO: Support strokes
|
| if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias ||
|
| - args.fPath->isInverseFillType() || args.fPath->isVolatile() ||
|
| - !args.fStroke->isFillStyle()) {
|
| + SkStrokeRec::kHairline_Style == args.fStroke->getStyle() ||
|
| + args.fPath->isInverseFillType() || args.fPath->isVolatile()) {
|
| return false;
|
| }
|
|
|
| @@ -96,12 +95,22 @@ bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c
|
| return false;
|
| }
|
|
|
| - // only support paths smaller than 64x64, scaled to less than 256x256
|
| + // only support paths with bounds within kMediumMIP by kMediumMIP,
|
| + // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP
|
| // the goal is to accelerate rendering of lots of small paths that may be scaling
|
| SkScalar maxScale = args.fViewMatrix->getMaxScale();
|
| const SkRect& bounds = args.fPath->getBounds();
|
| SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
|
| - return maxDim < 64.f && maxDim * maxScale < 256.f;
|
| + // Approximate stroked size by adding the maximum of the stroke width or 2x the miter limit
|
| + if (!args.fStroke->isFillStyle()) {
|
| + SkScalar extraWidth = args.fStroke->getWidth();
|
| + if (SkPaint::kMiter_Join == args.fStroke->getJoin()) {
|
| + extraWidth = SkTMax(extraWidth, 2.0f*args.fStroke->getMiter());
|
| + }
|
| + maxDim += extraWidth;
|
| + }
|
| +
|
| + return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP;
|
| }
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
| @@ -118,8 +127,20 @@ public:
|
| typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
|
|
|
| struct Geometry {
|
| - Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {}
|
| + Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {
|
| + if (!stroke.needToApply()) {
|
| + // purify unused values to ensure binary equality
|
| + fStroke.setStrokeParams(SkPaint::kDefault_Cap, SkPaint::kDefault_Join,
|
| + SkIntToScalar(4));
|
| + if (fStroke.getWidth() < 0) {
|
| + fStroke.setStrokeStyle(-1.0f);
|
| + }
|
| + }
|
| + }
|
| SkPath fPath;
|
| + // The unique ID of the path involved in this draw. This may be different than the ID
|
| + // in fPath since that path may have resulted from a SkStrokeRec::applyToPath call.
|
| + uint32_t fGenID;
|
| SkStrokeRec fStroke;
|
| bool fAntiAlias;
|
| PathData* fPathData;
|
| @@ -225,8 +246,7 @@ private:
|
| }
|
|
|
| // check to see if path is cached
|
| - // TODO: handle stroked vs. filled version of same path
|
| - PathData::Key key = { args.fPath.getGenerationID(), desiredDimension };
|
| + PathData::Key key(args.fGenID, desiredDimension, args.fStroke);
|
| args.fPathData = fPathCache->find(key);
|
| if (nullptr == args.fPathData || !atlas->hasID(args.fPathData->fID)) {
|
| // Remove the stale cache entry
|
| @@ -244,6 +264,7 @@ private:
|
| atlas,
|
| args.fPathData,
|
| args.fPath,
|
| + args.fGenID,
|
| args.fStroke,
|
| args.fAntiAlias,
|
| desiredDimension,
|
| @@ -301,8 +322,9 @@ private:
|
| GrBatchAtlas* atlas,
|
| PathData* pathData,
|
| const SkPath& path,
|
| - const SkStrokeRec&
|
| - stroke, bool antiAlias,
|
| + uint32_t genID,
|
| + const SkStrokeRec& stroke,
|
| + bool antiAlias,
|
| uint32_t dimension,
|
| SkScalar scale) {
|
| const SkRect& bounds = path.getBounds();
|
| @@ -333,41 +355,25 @@ private:
|
| drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
|
|
|
| // setup bitmap backing
|
| - // Now translate so the bound's UL corner is at the origin
|
| - drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1,
|
| - -devPathBounds.fTop * SK_Scalar1);
|
| - SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(),
|
| - devPathBounds.height());
|
| -
|
| + SkASSERT(devPathBounds.fLeft == 0);
|
| + SkASSERT(devPathBounds.fTop == 0);
|
| SkAutoPixmapStorage dst;
|
| - if (!dst.tryAlloc(SkImageInfo::MakeA8(pathBounds.width(),
|
| - pathBounds.height()))) {
|
| + if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
|
| + devPathBounds.height()))) {
|
| return false;
|
| }
|
| sk_bzero(dst.writable_addr(), dst.getSafeSize());
|
|
|
| // rasterize path
|
| SkPaint paint;
|
| - if (stroke.isHairlineStyle()) {
|
| - paint.setStyle(SkPaint::kStroke_Style);
|
| - paint.setStrokeWidth(SK_Scalar1);
|
| - } else {
|
| - if (stroke.isFillStyle()) {
|
| - paint.setStyle(SkPaint::kFill_Style);
|
| - } else {
|
| - paint.setStyle(SkPaint::kStroke_Style);
|
| - paint.setStrokeJoin(stroke.getJoin());
|
| - paint.setStrokeCap(stroke.getCap());
|
| - paint.setStrokeWidth(stroke.getWidth());
|
| - }
|
| - }
|
| + paint.setStyle(SkPaint::kFill_Style);
|
| paint.setAntiAlias(antiAlias);
|
|
|
| SkDraw draw;
|
| sk_bzero(&draw, sizeof(draw));
|
|
|
| SkRasterClip rasterClip;
|
| - rasterClip.setRect(pathBounds);
|
| + rasterClip.setRect(devPathBounds);
|
| draw.fRC = &rasterClip;
|
| draw.fClip = &rasterClip.bwRgn();
|
| draw.fMatrix = &drawMatrix;
|
| @@ -403,8 +409,7 @@ private:
|
| }
|
|
|
| // add to cache
|
| - pathData->fKey.fGenID = path.getGenerationID();
|
| - pathData->fKey.fDimension = dimension;
|
| + pathData->fKey = PathData::Key(genID, dimension, stroke);
|
| pathData->fScale = scale;
|
| pathData->fID = id;
|
| // change the scaled rect to match the size of the inset distance field
|
| @@ -543,9 +548,17 @@ bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
|
| }
|
|
|
| AADistanceFieldPathBatch::Geometry geometry(*args.fStroke);
|
| - geometry.fPath = *args.fPath;
|
| + if (SkStrokeRec::kFill_Style == args.fStroke->getStyle()) {
|
| + geometry.fPath = *args.fPath;
|
| + } else {
|
| + args.fStroke->applyToPath(&geometry.fPath, *args.fPath);
|
| + }
|
| geometry.fAntiAlias = args.fAntiAlias;
|
| -
|
| + // Note: this is the generation ID of the _original_ path. When a new path is
|
| + // generated due to stroking it is important that the original path's id is used
|
| + // for caching.
|
| + geometry.fGenID = args.fPath->getGenerationID();
|
| +
|
| SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry, args.fColor,
|
| *args.fViewMatrix, fAtlas,
|
| &fPathCache, &fPathList));
|
| @@ -620,6 +633,7 @@ DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
|
| AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
|
| geometry.fPath = GrTest::TestPath(random);
|
| geometry.fAntiAlias = random->nextBool();
|
| + geometry.fGenID = random->nextU();
|
|
|
| return AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
|
| gTestStruct.fAtlas,
|
|
|