Make simplifyStroke optional on GrStyledShape

For now we simplify every stroke at the beginning of drawShape*, but in
the future we will give the tessellator a chance to claim stroked shapes
before simplifying the stroke.

Bug: chromium:1172543
Change-Id: Ie90c20a1d342661b9006b16ab1fdad3ebe290ba3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/362798
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
Chris Dalton 2021-02-02 11:53:19 -07:00 committed by Skia Commit-Bot
parent 8ca4626a4b
commit b8ed2bce50
3 changed files with 113 additions and 57 deletions

View File

@ -77,6 +77,8 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
using SimplifyStroke = GrStyledShape::SimplifyStroke;
class AutoCheckFlush { class AutoCheckFlush {
public: public:
AutoCheckFlush(GrDrawingManager* drawingManager) : fDrawingManager(drawingManager) { AutoCheckFlush(GrDrawingManager* drawingManager) : fDrawingManager(drawingManager) {
@ -721,7 +723,7 @@ void GrSurfaceDrawContext::drawRect(const GrClip* clip,
} }
assert_alive(paint); assert_alive(paint);
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix,
GrStyledShape(rect, *style)); GrStyledShape(rect, *style, SimplifyStroke::kNo));
} }
void GrSurfaceDrawContext::drawQuadSet(const GrClip* clip, void GrSurfaceDrawContext::drawQuadSet(const GrClip* clip,
@ -1015,7 +1017,7 @@ void GrSurfaceDrawContext::drawRRect(const GrClip* origClip,
assert_alive(paint); assert_alive(paint);
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix,
GrStyledShape(rrect, style)); GrStyledShape(rrect, style, SimplifyStroke::kNo));
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -1439,9 +1441,9 @@ void GrSurfaceDrawContext::drawOval(const GrClip* clip,
} }
assert_alive(paint); assert_alive(paint);
this->drawShapeUsingPathRenderer( this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix,
clip, std::move(paint), aa, viewMatrix, GrStyledShape(SkRRect::MakeOval(oval), SkPathDirection::kCW, 2,
GrStyledShape(SkRRect::MakeOval(oval), SkPathDirection::kCW, 2, false, style)); false, style, SimplifyStroke::kNo));
} }
void GrSurfaceDrawContext::drawArc(const GrClip* clip, void GrSurfaceDrawContext::drawArc(const GrClip* clip,
@ -1478,9 +1480,9 @@ void GrSurfaceDrawContext::drawArc(const GrClip* clip,
} }
assert_alive(paint); assert_alive(paint);
} }
this->drawShapeUsingPathRenderer( this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix,
clip, std::move(paint), aa, viewMatrix, GrStyledShape::MakeArc(oval, startAngle, sweepAngle, useCenter,
GrStyledShape::MakeArc(oval, startAngle, sweepAngle, useCenter, style)); style, SimplifyStroke::kNo));
} }
void GrSurfaceDrawContext::drawImageLattice(const GrClip* clip, void GrSurfaceDrawContext::drawImageLattice(const GrClip* clip,
@ -1558,7 +1560,7 @@ void GrSurfaceDrawContext::drawPath(const GrClip* clip,
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceDrawContext", "drawPath", fContext); GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceDrawContext", "drawPath", fContext);
GrStyledShape shape(path, style); GrStyledShape shape(path, style, SimplifyStroke::kNo);
this->drawShape(clip, std::move(paint), aa, viewMatrix, std::move(shape)); this->drawShape(clip, std::move(paint), aa, viewMatrix, std::move(shape));
} }
@ -1581,6 +1583,10 @@ void GrSurfaceDrawContext::drawShape(const GrClip* clip,
AutoCheckFlush acf(this->drawingManager()); AutoCheckFlush acf(this->drawingManager());
// Always simplify the stroke for now. In the future we will give the tessellator a chance to
// claim strokes before trying to simplify them.
shape.simplifyStroke();
if (!shape.style().hasPathEffect()) { if (!shape.style().hasPathEffect()) {
GrAAType aaType = this->chooseAAType(aa); GrAAType aaType = this->chooseAAType(aa);
SkPoint linePts[2]; SkPoint linePts[2];
@ -1780,6 +1786,10 @@ void GrSurfaceDrawContext::drawShapeUsingPathRenderer(const GrClip* clip,
return; return;
} }
// Always simplify the stroke for now. In the future we will give the tessellator a chance to
// claim strokes before trying to simplify them.
shape.simplifyStroke();
if (attemptShapeFallback && shape.simplified()) { if (attemptShapeFallback && shape.simplified()) {
// Usually we enter drawShapeUsingPathRenderer() because the shape+style was too // Usually we enter drawShapeUsingPathRenderer() because the shape+style was too
// complex for dedicated draw ops. However, if GrStyledShape was able to reduce something // complex for dedicated draw ops. However, if GrStyledShape was able to reduce something

View File

@ -42,7 +42,8 @@ static bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion in
return false; return false;
} }
GrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInversion inversion) { GrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInversion inversion,
SimplifyStroke simplifyStroke) {
bool newIsInverted = is_inverted(original.fShape.inverted(), inversion); bool newIsInverted = is_inverted(original.fShape.inverted(), inversion);
if (original.style().isSimpleFill() && newIsInverted == original.fShape.inverted()) { if (original.style().isSimpleFill() && newIsInverted == original.fShape.inverted()) {
// By returning the original rather than falling through we can preserve any inherited style // By returning the original rather than falling through we can preserve any inherited style
@ -63,7 +64,7 @@ GrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInver
// Going from a non-filled style to fill may allow additional simplifications (e.g. // Going from a non-filled style to fill may allow additional simplifications (e.g.
// closing an open rect that wasn't closed in the original shape because it had // closing an open rect that wasn't closed in the original shape because it had
// stroke style). // stroke style).
result.simplify(); result.simplify(simplifyStroke);
// The above simplify() call only sets simplified to true if its geometry was changed, // The above simplify() call only sets simplified to true if its geometry was changed,
// since it already sees its style as a simple fill. Since the original style was not a // since it already sees its style as a simple fill. Since the original style was not a
// simple fill, MakeFilled always simplifies. // simple fill, MakeFilled always simplifies.
@ -305,11 +306,11 @@ void GrStyledShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) c
GrStyledShape GrStyledShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, GrStyledShape GrStyledShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
SkScalar sweepAngleDegrees, bool useCenter, SkScalar sweepAngleDegrees, bool useCenter,
const GrStyle& style) { const GrStyle& style, SimplifyStroke simplifyStroke) {
GrStyledShape result; GrStyledShape result;
result.fShape.setArc({oval.makeSorted(), startAngleDegrees, sweepAngleDegrees, useCenter}); result.fShape.setArc({oval.makeSorted(), startAngleDegrees, sweepAngleDegrees, useCenter});
result.fStyle = style; result.fStyle = style;
result.simplify(); result.simplify(simplifyStroke);
return result; return result;
} }
@ -416,7 +417,7 @@ GrStyledShape::GrStyledShape(const GrStyledShape& parent, GrStyle::Apply apply,
} else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) { } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) {
fInheritedPathForListeners.set(parent.fShape.path()); fInheritedPathForListeners.set(parent.fShape.path());
} }
this->simplify(); this->simplify(SimplifyStroke::kYes);
this->setInheritedKey(*parentForKey, apply, scale); this->setInheritedKey(*parentForKey, apply, scale);
} }
@ -561,9 +562,25 @@ bool GrStyledShape::asNestedRects(SkRect rects[2]) const {
return allEq || allGoE1; return allEq || allGoE1;
} }
void GrStyledShape::simplify() { class AutoRestoreInverseness {
// Dashing ignores inverseness skbug.com/5421. public:
bool inverted = !fStyle.isDashed() && fShape.inverted(); AutoRestoreInverseness(GrShape* shape, const GrStyle& style)
// Dashing ignores inverseness skbug.com/5421.
: fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {}
~AutoRestoreInverseness() {
// Restore invertedness after any modifications were made to the shape type
fShape->setInverted(fInverted);
SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType());
}
private:
GrShape* fShape;
bool fInverted;
};
void GrStyledShape::simplify(SimplifyStroke simplifyStroke) {
AutoRestoreInverseness ari(&fShape, fStyle);
unsigned simplifyFlags = 0; unsigned simplifyFlags = 0;
if (fStyle.isSimpleFill()) { if (fStyle.isSimpleFill()) {
@ -579,7 +596,7 @@ void GrStyledShape::simplify() {
// Remember if the original shape was closed; in the event we simplify to a point or line // Remember if the original shape was closed; in the event we simplify to a point or line
// because of degenerate geometry, we need to update joins and caps. // because of degenerate geometry, we need to update joins and caps.
GrShape::Type oldType = fShape.type(); GrShape::Type oldType = fShape.type();
bool wasClosed = fShape.simplify(simplifyFlags); fClosed = fShape.simplify(simplifyFlags);
fSimplified = oldType != fShape.type(); fSimplified = oldType != fShape.type();
if (fShape.isPath()) { if (fShape.isPath()) {
@ -605,16 +622,16 @@ void GrStyledShape::simplify() {
// drawing simple shapes. // drawing simple shapes.
fInheritedPathForListeners.reset(); fInheritedPathForListeners.reset();
// Further simplifications to the shape based on the style if (simplifyStroke == SimplifyStroke::kYes) {
fSimplified |= this->simplifyStroke(wasClosed); // Further simplifications to the shape based on the style
this->simplifyStroke();
}
} }
// Restore invertedness after any modifications were made to the shape type
fShape.setInverted(inverted);
SkASSERT(!fShape.isPath() || inverted == fShape.path().isInverseFillType());
} }
bool GrStyledShape::simplifyStroke(bool originallyClosed) { void GrStyledShape::simplifyStroke() {
AutoRestoreInverseness ari(&fShape, fStyle);
// For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
// becomes a round rect. // becomes a round rect.
if (!fStyle.hasPathEffect() && fShape.isRect() && if (!fStyle.hasPathEffect() && fShape.isRect() &&
@ -623,7 +640,7 @@ bool GrStyledShape::simplifyStroke(bool originallyClosed) {
(fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join && (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) { fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
// Bevel-stroked rect needs path rendering // Bevel-stroked rect needs path rendering
return false; return;
} }
SkScalar r = fStyle.strokeRec().getWidth() / 2; SkScalar r = fStyle.strokeRec().getWidth() / 2;
@ -634,14 +651,15 @@ bool GrStyledShape::simplifyStroke(bool originallyClosed) {
fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r)); fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
} }
fStyle = GrStyle::SimpleFill(); fStyle = GrStyle::SimpleFill();
return true; fSimplified = true;
return;
} }
// Otherwise, if we're a point or a line, we might be able to explicitly apply some of the // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
// stroking (and even some of the dashing). Any other shape+style is too complicated to reduce. // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() || if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
fStyle.strokeRec().isHairlineStyle()) { fStyle.strokeRec().isHairlineStyle()) {
return false; return;
} }
// Tracks style simplifications, even if the geometry can't be further simplified. // Tracks style simplifications, even if the geometry can't be further simplified.
@ -665,13 +683,13 @@ bool GrStyledShape::simplifyStroke(bool originallyClosed) {
} }
if (!dropDash) { if (!dropDash) {
return false; return;
} }
// Fall through to modifying the shape to respect the new stroke geometry // Fall through to modifying the shape to respect the new stroke geometry
fStyle = GrStyle(fStyle.strokeRec(), nullptr); fStyle = GrStyle(fStyle.strokeRec(), nullptr);
// Since the reduced the line or point after dashing is dependent on the caps of the dashes, // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
// we reset to be unclosed so we don't override the style based on joins later. // we reset to be unclosed so we don't override the style based on joins later.
originallyClosed = false; fClosed = false;
styleSimplified = true; styleSimplified = true;
} }
@ -680,7 +698,8 @@ bool GrStyledShape::simplifyStroke(bool originallyClosed) {
bool strokeAndFilled = false; bool strokeAndFilled = false;
if (fStyle.isSimpleFill()) { if (fStyle.isSimpleFill()) {
fShape.reset(); fShape.reset();
return true; fSimplified = true;
return;
} else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
// Stroke only // Stroke only
SkStrokeRec rec = fStyle.strokeRec(); SkStrokeRec rec = fStyle.strokeRec();
@ -692,7 +711,7 @@ bool GrStyledShape::simplifyStroke(bool originallyClosed) {
// A point or line that was formed by a degenerate closed shape needs its style updated to // A point or line that was formed by a degenerate closed shape needs its style updated to
// reflect the fact that it doesn't actually produce caps. // reflect the fact that it doesn't actually produce caps.
if (originallyClosed) { if (fClosed) {
SkPaint::Cap cap; SkPaint::Cap cap;
if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) { if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
// As a closed shape, the line moves from a to b and back to a, producing a 180 degree // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
@ -761,7 +780,8 @@ bool GrStyledShape::simplifyStroke(bool originallyClosed) {
} else { } else {
// Geometrically can't apply the style and turn into a fill, but might still be simpler // Geometrically can't apply the style and turn into a fill, but might still be simpler
// than before based solely on changes to fStyle. // than before based solely on changes to fStyle.
return styleSimplified; fSimplified |= styleSimplified;
return;
} }
rect.outset(outset.fX, outset.fY); rect.outset(outset.fX, outset.fY);
if (rect.isEmpty()) { if (rect.isEmpty()) {
@ -775,5 +795,6 @@ bool GrStyledShape::simplifyStroke(bool originallyClosed) {
} }
// If we made it here, the stroke was fully applied to the new shape so we can become a fill. // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
fStyle = GrStyle::SimpleFill(); fStyle = GrStyle::SimpleFill();
return true; fSimplified = true;
return;
} }

View File

@ -44,44 +44,61 @@ public:
GrStyledShape() {} GrStyledShape() {}
explicit GrStyledShape(const SkPath& path) : GrStyledShape(path, GrStyle::SimpleFill()) {} enum class SimplifyStroke : bool { kNo = false, kYes };
explicit GrStyledShape(const SkRRect& rrect) : GrStyledShape(rrect, GrStyle::SimpleFill()) {} explicit GrStyledShape(const SkPath& path)
: GrStyledShape(path, GrStyle::SimpleFill(), SimplifyStroke::kNo) {}
explicit GrStyledShape(const SkRect& rect) : GrStyledShape(rect, GrStyle::SimpleFill()) {} explicit GrStyledShape(const SkRRect& rrect)
: GrStyledShape(rrect, GrStyle::SimpleFill(), SimplifyStroke::kNo) {}
GrStyledShape(const SkPath& path, const SkPaint& paint) : GrStyledShape(path, GrStyle(paint)) {} explicit GrStyledShape(const SkRect& rect)
: GrStyledShape(rect, GrStyle::SimpleFill(), SimplifyStroke::kNo) {}
GrStyledShape(const SkRRect& rrect, const SkPaint& paint) GrStyledShape(const SkPath& path, const SkPaint& paint,
: GrStyledShape(rrect, GrStyle(paint)) {} SimplifyStroke simplifyStroke = SimplifyStroke::kYes)
: GrStyledShape(path, GrStyle(paint), simplifyStroke) {}
GrStyledShape(const SkRect& rect, const SkPaint& paint) : GrStyledShape(rect, GrStyle(paint)) {} GrStyledShape(const SkRRect& rrect, const SkPaint& paint,
SimplifyStroke simplifyStroke = SimplifyStroke::kYes)
: GrStyledShape(rrect, GrStyle(paint), simplifyStroke) {}
GrStyledShape(const SkPath& path, const GrStyle& style) : fShape(path), fStyle(style) { GrStyledShape(const SkRect& rect, const SkPaint& paint,
this->simplify(); SimplifyStroke simplifyStroke = SimplifyStroke::kYes)
: GrStyledShape(rect, GrStyle(paint), simplifyStroke) {}
GrStyledShape(const SkPath& path, const GrStyle& style,
SimplifyStroke simplifyStroke = SimplifyStroke::kYes)
: fShape(path), fStyle(style) {
this->simplify(simplifyStroke);
} }
GrStyledShape(const SkRRect& rrect, const GrStyle& style) : fShape(rrect), fStyle(style) { GrStyledShape(const SkRRect& rrect, const GrStyle& style,
this->simplify(); SimplifyStroke simplifyStroke = SimplifyStroke::kYes)
: fShape(rrect), fStyle(style) {
this->simplify(simplifyStroke);
} }
GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted, GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted,
const GrStyle& style) const GrStyle& style, SimplifyStroke simplifyStroke = SimplifyStroke::kYes)
: fShape(rrect) : fShape(rrect)
, fStyle(style) { , fStyle(style) {
fShape.setPathWindingParams(dir, start); fShape.setPathWindingParams(dir, start);
fShape.setInverted(inverted); fShape.setInverted(inverted);
this->simplify(); this->simplify(simplifyStroke);
} }
GrStyledShape(const SkRect& rect, const GrStyle& style) : fShape(rect), fStyle(style) { GrStyledShape(const SkRect& rect, const GrStyle& style,
this->simplify(); SimplifyStroke simplifyStroke = SimplifyStroke::kYes)
: fShape(rect), fStyle(style) {
this->simplify(simplifyStroke);
} }
GrStyledShape(const GrStyledShape&); GrStyledShape(const GrStyledShape&);
static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees, static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style); SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style,
SimplifyStroke = SimplifyStroke::kYes);
GrStyledShape& operator=(const GrStyledShape& that); GrStyledShape& operator=(const GrStyledShape& that);
@ -103,7 +120,8 @@ public:
* made non-inverted since dashing ignores inverseness). * made non-inverted since dashing ignores inverseness).
*/ */
static GrStyledShape MakeFilled(const GrStyledShape& original, static GrStyledShape MakeFilled(const GrStyledShape& original,
FillInversion = FillInversion::kPreserve); FillInversion = FillInversion::kPreserve,
SimplifyStroke = SimplifyStroke::kYes);
const GrStyle& style() const { return fStyle; } const GrStyle& style() const { return fStyle; }
@ -249,6 +267,12 @@ public:
bool testingOnly_isPath() const; bool testingOnly_isPath() const;
bool testingOnly_isNonVolatilePath() const; bool testingOnly_isNonVolatilePath() const;
/**
* As an optional part of the simplification process, some shapes can have stroking trivially
* evaluated and form a new geometry with just a fill.
*/
void simplifyStroke();
private: private:
/** Constructor used by the applyStyle() function */ /** Constructor used by the applyStyle() function */
GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale);
@ -259,12 +283,12 @@ private:
*/ */
void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale);
// Similar to GrShape::simplify but also takes into account style and stroking, possibly /**
// applying the style explicitly to produce a new analytic shape with a simpler style. * Similar to GrShape::simplify but also takes into account style and, optionally, stroking.
void simplify(); * Some stroked shapes can be represented as a different filled primitive (e.g. a stroked line
// As part of the simplification process, some shapes can have stroking trivially evaluated * becomes a filled round rect). When SimplifyStroke is kYes, such optimizations are permitted.
// and form a new geometry with just a fill. */
bool simplifyStroke(bool originallyClosed); void simplify(SimplifyStroke);
/** Gets the path that gen id listeners should be added to. */ /** Gets the path that gen id listeners should be added to. */
const SkPath* originalPathForListeners() const; const SkPath* originalPathForListeners() const;
@ -273,6 +297,7 @@ private:
GrStyle fStyle; GrStyle fStyle;
// Gen ID of the original path (path may be modified or simplified away). // Gen ID of the original path (path may be modified or simplified away).
int32_t fGenID = 0; int32_t fGenID = 0;
bool fClosed = false;
bool fSimplified = false; bool fSimplified = false;
SkTLazy<SkPath> fInheritedPathForListeners; SkTLazy<SkPath> fInheritedPathForListeners;