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
|
* 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)
|
||||||
|
@ -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));
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user