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
This commit is contained in:
jvanverth 2015-11-20 14:06:14 -08:00 committed by Commit bot
parent 0eafe79f42
commit 73ee677026
3 changed files with 70 additions and 41 deletions

View File

@ -115,6 +115,7 @@ public:
* fTarget The target that the path will be rendered to * fTarget The target that the path will be rendered to
* fResourceProvider The resource provider for creating gpu resources to render the path * fResourceProvider The resource provider for creating gpu resources to render the path
* fPipelineBuilder The pipelineBuilder * fPipelineBuilder The pipelineBuilder
* fColor Color to render with
* fViewMatrix The viewMatrix * fViewMatrix The viewMatrix
* fPath the path to draw. * fPath the path to draw.
* fStroke the stroke information (width, join, cap) * fStroke the stroke information (width, join, cap)

View File

@ -38,7 +38,7 @@ static int g_NumFreedPaths = 0;
// mip levels // mip levels
static const int kSmallMIP = 32; static const int kSmallMIP = 32;
static const int kMediumMIP = 72; static const int kMediumMIP = 73;
static const int kLargeMIP = 162; static const int kLargeMIP = 162;
// Callback to clear out internal path cache when eviction occurs // Callback to clear out internal path cache when eviction occurs
@ -84,10 +84,9 @@ GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
// TODO: Support inverse fill // TODO: Support inverse fill
// TODO: Support strokes
if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias || if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias ||
args.fPath->isInverseFillType() || args.fPath->isVolatile() || SkStrokeRec::kHairline_Style == args.fStroke->getStyle() ||
!args.fStroke->isFillStyle()) { args.fPath->isInverseFillType() || args.fPath->isVolatile()) {
return false; return false;
} }
@ -96,12 +95,22 @@ bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c
return false; 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 // the goal is to accelerate rendering of lots of small paths that may be scaling
SkScalar maxScale = args.fViewMatrix->getMaxScale(); SkScalar maxScale = args.fViewMatrix->getMaxScale();
const SkRect& bounds = args.fPath->getBounds(); const SkRect& bounds = args.fPath->getBounds();
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); 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; typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
struct Geometry { 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; 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; SkStrokeRec fStroke;
bool fAntiAlias; bool fAntiAlias;
PathData* fPathData; PathData* fPathData;
@ -225,8 +246,7 @@ private:
} }
// check to see if path is cached // check to see if path is cached
// TODO: handle stroked vs. filled version of same path PathData::Key key(args.fGenID, desiredDimension, args.fStroke);
PathData::Key key = { args.fPath.getGenerationID(), desiredDimension };
args.fPathData = fPathCache->find(key); args.fPathData = fPathCache->find(key);
if (nullptr == args.fPathData || !atlas->hasID(args.fPathData->fID)) { if (nullptr == args.fPathData || !atlas->hasID(args.fPathData->fID)) {
// Remove the stale cache entry // Remove the stale cache entry
@ -244,6 +264,7 @@ private:
atlas, atlas,
args.fPathData, args.fPathData,
args.fPath, args.fPath,
args.fGenID,
args.fStroke, args.fStroke,
args.fAntiAlias, args.fAntiAlias,
desiredDimension, desiredDimension,
@ -301,8 +322,9 @@ private:
GrBatchAtlas* atlas, GrBatchAtlas* atlas,
PathData* pathData, PathData* pathData,
const SkPath& path, const SkPath& path,
const SkStrokeRec& uint32_t genID,
stroke, bool antiAlias, const SkStrokeRec& stroke,
bool antiAlias,
uint32_t dimension, uint32_t dimension,
SkScalar scale) { SkScalar scale) {
const SkRect& bounds = path.getBounds(); const SkRect& bounds = path.getBounds();
@ -333,41 +355,25 @@ private:
drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
// setup bitmap backing // setup bitmap backing
// Now translate so the bound's UL corner is at the origin SkASSERT(devPathBounds.fLeft == 0);
drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1, SkASSERT(devPathBounds.fTop == 0);
-devPathBounds.fTop * SK_Scalar1);
SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(),
devPathBounds.height());
SkAutoPixmapStorage dst; SkAutoPixmapStorage dst;
if (!dst.tryAlloc(SkImageInfo::MakeA8(pathBounds.width(), if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
pathBounds.height()))) { devPathBounds.height()))) {
return false; return false;
} }
sk_bzero(dst.writable_addr(), dst.getSafeSize()); sk_bzero(dst.writable_addr(), dst.getSafeSize());
// rasterize path // rasterize path
SkPaint paint; SkPaint paint;
if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kFill_Style);
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.setAntiAlias(antiAlias); paint.setAntiAlias(antiAlias);
SkDraw draw; SkDraw draw;
sk_bzero(&draw, sizeof(draw)); sk_bzero(&draw, sizeof(draw));
SkRasterClip rasterClip; SkRasterClip rasterClip;
rasterClip.setRect(pathBounds); rasterClip.setRect(devPathBounds);
draw.fRC = &rasterClip; draw.fRC = &rasterClip;
draw.fClip = &rasterClip.bwRgn(); draw.fClip = &rasterClip.bwRgn();
draw.fMatrix = &drawMatrix; draw.fMatrix = &drawMatrix;
@ -403,8 +409,7 @@ private:
} }
// add to cache // add to cache
pathData->fKey.fGenID = path.getGenerationID(); pathData->fKey = PathData::Key(genID, dimension, stroke);
pathData->fKey.fDimension = dimension;
pathData->fScale = scale; pathData->fScale = scale;
pathData->fID = id; pathData->fID = id;
// change the scaled rect to match the size of the inset distance field // 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); 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; 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, SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry, args.fColor,
*args.fViewMatrix, fAtlas, *args.fViewMatrix, fAtlas,
&fPathCache, &fPathList)); &fPathCache, &fPathList));

View File

@ -33,13 +33,28 @@ private:
bool onDrawPath(const DrawPathArgs&) override; bool onDrawPath(const DrawPathArgs&) override;
struct PathData { 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; uint32_t fGenID;
// rendered size for stored path (32x32 max, 64x64 max, 128x128 max) // rendered size for stored path (32x32 max, 64x64 max, 128x128 max)
uint32_t fDimension; uint32_t fDimension;
bool operator==(const Key& other) const { // stroking information
return other.fGenID == fGenID && other.fDimension == fDimension; SkStrokeRec fStroke;
}
}; };
Key fKey; Key fKey;
SkScalar fScale; SkScalar fScale;