From 73ee6770260dbeeabc4a78eee4f13533f0031211 Mon Sep 17 00:00:00 2001 From: jvanverth Date: Fri, 20 Nov 2015 14:06:14 -0800 Subject: [PATCH] Add stroking support to distance field path renderer Also slightly increases sizes of paths accepted for distance field caching. Committed: https://skia.googlesource.com/skia/+/5ce76efd1c847308c7bcac17bd87d567c42cd786 Review URL: https://codereview.chromium.org/1460873002 --- src/gpu/GrPathRenderer.h | 1 + .../batches/GrAADistanceFieldPathRenderer.cpp | 87 +++++++++++-------- .../batches/GrAADistanceFieldPathRenderer.h | 23 ++++- 3 files changed, 70 insertions(+), 41 deletions(-) diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h index 25a2a68abb..7ada6d10f7 100644 --- a/src/gpu/GrPathRenderer.h +++ b/src/gpu/GrPathRenderer.h @@ -115,6 +115,7 @@ public: * fTarget The target that the path will be rendered to * fResourceProvider The resource provider for creating gpu resources to render the path * fPipelineBuilder The pipelineBuilder + * fColor Color to render with * fViewMatrix The viewMatrix * fPath the path to draw. * fStroke the stroke information (width, join, cap) diff --git a/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp b/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp index ee5fdb8a80..4eff0c4079 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 batch(AADistanceFieldPathBatch::Create(geometry, args.fColor, *args.fViewMatrix, fAtlas, &fPathCache, &fPathList)); diff --git a/src/gpu/batches/GrAADistanceFieldPathRenderer.h b/src/gpu/batches/GrAADistanceFieldPathRenderer.h index 469aeeb981..1210cee331 100755 --- a/src/gpu/batches/GrAADistanceFieldPathRenderer.h +++ b/src/gpu/batches/GrAADistanceFieldPathRenderer.h @@ -33,13 +33,28 @@ private: bool onDrawPath(const DrawPathArgs&) override; struct PathData { - struct Key { + class Key { + public: + // default ctor needed for new of uninitialized PathData + // since fStroke has no default ctor + Key() : fStroke(SkStrokeRec::kFill_InitStyle) {} + Key(uint32_t genID, uint32_t dim, const SkStrokeRec& stroke) + : fGenID(genID) + , fDimension(dim) + , fStroke(stroke) { + } + + bool operator==(const Key& other) const { + return other.fGenID == fGenID && other.fDimension == fDimension && + fStroke.hasEqualEffect(other.fStroke); + } + + private: uint32_t fGenID; // rendered size for stored path (32x32 max, 64x64 max, 128x128 max) uint32_t fDimension; - bool operator==(const Key& other) const { - return other.fGenID == fGenID && other.fDimension == fDimension; - } + // stroking information + SkStrokeRec fStroke; }; Key fKey; SkScalar fScale;