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:
parent
0eafe79f42
commit
73ee677026
@ -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)
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user