Revert "Revert "Add arcs as a specialized geometry to GrShape.""
This reverts commit af88ec3712
.
Bug: skia:7794
Change-Id: I2d0e1d7b4e025481241d823b09f5de5d0f1a13eb
Reviewed-on: https://skia-review.googlesource.com/123627
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
cfd650e43f
commit
8a98bc9674
@ -1304,6 +1304,25 @@ void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
|
|||||||
|
|
||||||
SkPoint singlePt;
|
SkPoint singlePt;
|
||||||
|
|
||||||
|
// Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
|
||||||
|
// close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
|
||||||
|
// arcs from the same oval.
|
||||||
|
auto addPt = [&forceMoveTo, this](const SkPoint& pt) {
|
||||||
|
SkPoint lastPt;
|
||||||
|
#ifdef SK_DISABLE_ARC_TO_LINE_TO_CHECK
|
||||||
|
static constexpr bool kSkipLineToCheck = true;
|
||||||
|
#else
|
||||||
|
static constexpr bool kSkipLineToCheck = false;
|
||||||
|
#endif
|
||||||
|
if (forceMoveTo) {
|
||||||
|
this->moveTo(pt);
|
||||||
|
} else if (kSkipLineToCheck || !this->getLastPt(&lastPt) ||
|
||||||
|
!SkScalarNearlyEqual(lastPt.fX, pt.fX) ||
|
||||||
|
!SkScalarNearlyEqual(lastPt.fY, pt.fY)) {
|
||||||
|
this->lineTo(pt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// At this point, we know that the arc is not a lone point, but startV == stopV
|
// At this point, we know that the arc is not a lone point, but startV == stopV
|
||||||
// indicates that the sweepAngle is too small such that angles_to_unit_vectors
|
// indicates that the sweepAngle is too small such that angles_to_unit_vectors
|
||||||
// cannot handle it.
|
// cannot handle it.
|
||||||
@ -1318,7 +1337,7 @@ void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
|
|||||||
// make sin(endAngle) to be 0 which will then draw a dot.
|
// make sin(endAngle) to be 0 which will then draw a dot.
|
||||||
singlePt.set(oval.centerX() + radiusX * sk_float_cos(endAngle),
|
singlePt.set(oval.centerX() + radiusX * sk_float_cos(endAngle),
|
||||||
oval.centerY() + radiusY * sk_float_sin(endAngle));
|
oval.centerY() + radiusY * sk_float_sin(endAngle));
|
||||||
forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
|
addPt(singlePt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1327,12 +1346,12 @@ void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
|
|||||||
if (count) {
|
if (count) {
|
||||||
this->incReserve(count * 2 + 1);
|
this->incReserve(count * 2 + 1);
|
||||||
const SkPoint& pt = conics[0].fPts[0];
|
const SkPoint& pt = conics[0].fPts[0];
|
||||||
forceMoveTo ? this->moveTo(pt) : this->lineTo(pt);
|
addPt(pt);
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
|
this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
|
addPt(singlePt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3339,6 +3358,20 @@ bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Di
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
|
||||||
|
if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
|
||||||
|
// This gets converted to an oval.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (useCenter) {
|
||||||
|
// This is a pie wedge. It's convex if the angle is <= 180.
|
||||||
|
return SkScalarAbs(sweepAngle) <= 180.f;
|
||||||
|
}
|
||||||
|
// When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped
|
||||||
|
// to a secant, i.e. convex.
|
||||||
|
return SkScalarAbs(sweepAngle) <= 360.f;
|
||||||
|
}
|
||||||
|
|
||||||
void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
|
void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
|
||||||
SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
|
SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
|
||||||
SkASSERT(!oval.isEmpty());
|
SkASSERT(!oval.isEmpty());
|
||||||
@ -3349,11 +3382,15 @@ void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar st
|
|||||||
path->setFillType(SkPath::kWinding_FillType);
|
path->setFillType(SkPath::kWinding_FillType);
|
||||||
if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
|
if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
|
||||||
path->addOval(oval);
|
path->addOval(oval);
|
||||||
|
SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (useCenter) {
|
if (useCenter) {
|
||||||
path->moveTo(oval.centerX(), oval.centerY());
|
path->moveTo(oval.centerX(), oval.centerY());
|
||||||
}
|
}
|
||||||
|
auto firstDir =
|
||||||
|
sweepAngle > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
|
||||||
|
bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect);
|
||||||
// Arc to mods at 360 and drawArc is not supposed to.
|
// Arc to mods at 360 and drawArc is not supposed to.
|
||||||
bool forceMoveTo = !useCenter;
|
bool forceMoveTo = !useCenter;
|
||||||
while (sweepAngle <= -360.f) {
|
while (sweepAngle <= -360.f) {
|
||||||
@ -3376,6 +3413,8 @@ void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar st
|
|||||||
if (useCenter) {
|
if (useCenter) {
|
||||||
path->close();
|
path->close();
|
||||||
}
|
}
|
||||||
|
path->setConvexity(convex ? SkPath::kConvex_Convexity : SkPath::kConcave_Convexity);
|
||||||
|
path->fFirstDirection.store(firstDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -105,6 +105,12 @@ public:
|
|||||||
static void CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
|
static void CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
|
||||||
SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect);
|
SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty
|
||||||
|
* oval.
|
||||||
|
*/
|
||||||
|
static bool DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a C++11-iterable object that traverses a path's verbs in order. e.g:
|
* Returns a C++11-iterable object that traverses a path's verbs in order. e.g:
|
||||||
*
|
*
|
||||||
|
@ -136,6 +136,7 @@ void SkStrokeRec::applyToPaint(SkPaint* paint) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline SkScalar get_inflation_bounds(SkPaint::Join join,
|
static inline SkScalar get_inflation_bounds(SkPaint::Join join,
|
||||||
|
SkPaint::Cap cap,
|
||||||
SkScalar strokeWidth,
|
SkScalar strokeWidth,
|
||||||
SkScalar miterLimit) {
|
SkScalar miterLimit) {
|
||||||
if (strokeWidth < 0) { // fill
|
if (strokeWidth < 0) { // fill
|
||||||
@ -145,20 +146,25 @@ static inline SkScalar get_inflation_bounds(SkPaint::Join join,
|
|||||||
}
|
}
|
||||||
// since we're stroked, outset the rect by the radius (and join type)
|
// since we're stroked, outset the rect by the radius (and join type)
|
||||||
SkScalar radius = SkScalarHalf(strokeWidth);
|
SkScalar radius = SkScalarHalf(strokeWidth);
|
||||||
|
SkScalar inflation = radius;
|
||||||
if (SkPaint::kMiter_Join == join) {
|
if (SkPaint::kMiter_Join == join) {
|
||||||
if (miterLimit > SK_Scalar1) {
|
if (miterLimit > SK_Scalar1) {
|
||||||
radius *= miterLimit;
|
inflation *= miterLimit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return radius;
|
// A square cap at a 45 degree angle can add sqrt(2)*radius.
|
||||||
|
if (SkPaint::kSquare_Cap == cap) {
|
||||||
|
inflation = SkTMax(inflation, radius * SK_ScalarSqrt2);
|
||||||
|
}
|
||||||
|
return inflation;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkScalar SkStrokeRec::getInflationRadius() const {
|
SkScalar SkStrokeRec::getInflationRadius() const {
|
||||||
return get_inflation_bounds((SkPaint::Join)fJoin, fWidth, fMiterLimit);
|
return get_inflation_bounds((SkPaint::Join)fJoin, (SkPaint::Cap)fCap, fWidth, fMiterLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
SkScalar SkStrokeRec::GetInflationRadius(const SkPaint& paint, SkPaint::Style style) {
|
SkScalar SkStrokeRec::GetInflationRadius(const SkPaint& paint, SkPaint::Style style) {
|
||||||
SkScalar width = SkPaint::kFill_Style == style ? -SK_Scalar1 : paint.getStrokeWidth();
|
SkScalar width = SkPaint::kFill_Style == style ? -SK_Scalar1 : paint.getStrokeWidth();
|
||||||
return get_inflation_bounds(paint.getStrokeJoin(), width, paint.getStrokeMiter());
|
return get_inflation_bounds(paint.getStrokeJoin(), paint.getStrokeCap(), width,
|
||||||
|
paint.getStrokeMiter());
|
||||||
}
|
}
|
||||||
|
@ -1353,10 +1353,9 @@ void GrRenderTargetContext::drawArc(const GrClip& clip,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SkPath path;
|
this->drawShapeUsingPathRenderer(
|
||||||
SkPathPriv::CreateDrawArcPath(&path, oval, startAngle, sweepAngle, useCenter,
|
clip, std::move(paint), aa, viewMatrix,
|
||||||
style.isSimpleFill());
|
GrShape::MakeArc(oval, startAngle, sweepAngle, useCenter, style));
|
||||||
this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrShape(path, style));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrRenderTargetContext::drawImageLattice(const GrClip& clip,
|
void GrRenderTargetContext::drawImageLattice(const GrClip& clip,
|
||||||
|
@ -18,6 +18,9 @@ GrShape& GrShape::operator=(const GrShape& that) {
|
|||||||
case Type::kRRect:
|
case Type::kRRect:
|
||||||
fRRectData = that.fRRectData;
|
fRRectData = that.fRRectData;
|
||||||
break;
|
break;
|
||||||
|
case Type::kArc:
|
||||||
|
fArcData = that.fArcData;
|
||||||
|
break;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
fLineData = that.fLineData;
|
fLineData = that.fLineData;
|
||||||
break;
|
break;
|
||||||
@ -82,6 +85,14 @@ GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) {
|
|||||||
result.fRRectData.fStart = kDefaultRRectStart;
|
result.fRRectData.fStart = kDefaultRRectStart;
|
||||||
result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
|
result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
|
||||||
break;
|
break;
|
||||||
|
case Type::kArc:
|
||||||
|
result.fType = original.fType;
|
||||||
|
result.fArcData.fOval = original.fArcData.fOval;
|
||||||
|
result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees;
|
||||||
|
result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees;
|
||||||
|
result.fArcData.fUseCenter = original.fArcData.fUseCenter;
|
||||||
|
result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion);
|
||||||
|
break;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
// Lines don't fill.
|
// Lines don't fill.
|
||||||
if (is_inverted(original.fLineData.fInverted, inversion)) {
|
if (is_inverted(original.fLineData.fInverted, inversion)) {
|
||||||
@ -144,6 +155,9 @@ SkRect GrShape::bounds() const {
|
|||||||
}
|
}
|
||||||
case Type::kRRect:
|
case Type::kRRect:
|
||||||
return fRRectData.fRRect.getBounds();
|
return fRRectData.fRRect.getBounds();
|
||||||
|
case Type::kArc:
|
||||||
|
// Could make this less conservative by looking at angles.
|
||||||
|
return fArcData.fOval;
|
||||||
case Type::kPath:
|
case Type::kPath:
|
||||||
return this->path().getBounds();
|
return this->path().getBounds();
|
||||||
}
|
}
|
||||||
@ -215,9 +229,13 @@ int GrShape::unstyledKeySize() const {
|
|||||||
return 1;
|
return 1;
|
||||||
case Type::kRRect:
|
case Type::kRRect:
|
||||||
SkASSERT(!fInheritedKey.count());
|
SkASSERT(!fInheritedKey.count());
|
||||||
SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
|
GR_STATIC_ASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
|
||||||
// + 1 for the direction, start index, and inverseness.
|
// + 1 for the direction, start index, and inverseness.
|
||||||
return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
|
return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
|
||||||
|
case Type::kArc:
|
||||||
|
SkASSERT(!fInheritedKey.count());
|
||||||
|
GR_STATIC_ASSERT(0 == sizeof(fArcData) % sizeof(uint32_t));
|
||||||
|
return sizeof(fArcData) / sizeof(uint32_t);
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
|
GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
|
||||||
// 4 for the end points and 1 for the inverseness
|
// 4 for the end points and 1 for the inverseness
|
||||||
@ -260,6 +278,10 @@ void GrShape::writeUnstyledKey(uint32_t* key) const {
|
|||||||
*key++ |= fRRectData.fStart;
|
*key++ |= fRRectData.fStart;
|
||||||
SkASSERT(fRRectData.fStart < 8);
|
SkASSERT(fRRectData.fStart < 8);
|
||||||
break;
|
break;
|
||||||
|
case Type::kArc:
|
||||||
|
memcpy(key, &fArcData, sizeof(fArcData));
|
||||||
|
key += sizeof(fArcData) / sizeof(uint32_t);
|
||||||
|
break;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
|
memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
|
||||||
key += 4;
|
key += 4;
|
||||||
@ -349,6 +371,29 @@ void GrShape::addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GrShape GrShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees,
|
||||||
|
bool useCenter, const GrStyle& style) {
|
||||||
|
#ifdef SK_DISABLE_ARC_TO_LINE_TO_CHECK
|
||||||
|
// When this flag is set the segment mask of the path won't match GrShape's segment mask for
|
||||||
|
// paths. Represent this shape as a path.
|
||||||
|
SkPath path;
|
||||||
|
SkPathPriv::CreateDrawArcPath(&path, oval, startAngleDegrees, sweepAngleDegrees, useCenter,
|
||||||
|
style.isSimpleFill());
|
||||||
|
return GrShape(path, style);
|
||||||
|
#else
|
||||||
|
GrShape result;
|
||||||
|
result.changeType(Type::kArc);
|
||||||
|
result.fArcData.fOval = oval;
|
||||||
|
result.fArcData.fStartAngleDegrees = startAngleDegrees;
|
||||||
|
result.fArcData.fSweepAngleDegrees = sweepAngleDegrees;
|
||||||
|
result.fArcData.fUseCenter = useCenter;
|
||||||
|
result.fArcData.fInverted = false;
|
||||||
|
result.fStyle = style;
|
||||||
|
result.attemptToSimplifyArc();
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
|
GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
|
||||||
const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
|
const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
|
||||||
this->initType(that.fType, thatPath);
|
this->initType(that.fType, thatPath);
|
||||||
@ -360,6 +405,9 @@ GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
|
|||||||
case Type::kRRect:
|
case Type::kRRect:
|
||||||
fRRectData = that.fRRectData;
|
fRRectData = that.fRRectData;
|
||||||
break;
|
break;
|
||||||
|
case Type::kArc:
|
||||||
|
fArcData = that.fArcData;
|
||||||
|
break;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
fLineData = that.fLineData;
|
fLineData = that.fLineData;
|
||||||
break;
|
break;
|
||||||
@ -588,6 +636,7 @@ void GrShape::attemptToSimplifyRRect() {
|
|||||||
} else if (fStyle.isDashed()) {
|
} else if (fStyle.isDashed()) {
|
||||||
// Dashing ignores the inverseness (currently). skbug.com/5421
|
// Dashing ignores the inverseness (currently). skbug.com/5421
|
||||||
fRRectData.fInverted = false;
|
fRRectData.fInverted = false;
|
||||||
|
// Possible TODO here: Check whether the dash results in a single arc or line.
|
||||||
}
|
}
|
||||||
// Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
|
// Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
|
||||||
if (!fStyle.hasPathEffect() &&
|
if (!fStyle.hasPathEffect() &&
|
||||||
@ -640,6 +689,46 @@ void GrShape::attemptToSimplifyLine() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GrShape::attemptToSimplifyArc() {
|
||||||
|
SkASSERT(fType == Type::kArc);
|
||||||
|
SkASSERT(!fArcData.fInverted);
|
||||||
|
if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) {
|
||||||
|
this->changeType(Type::kEmpty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses
|
||||||
|
// the full circle and doesn't use the center point is an oval. Unless it has square or round
|
||||||
|
// caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're
|
||||||
|
// ignoring that for now.
|
||||||
|
if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter &&
|
||||||
|
fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) {
|
||||||
|
if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) {
|
||||||
|
auto oval = fArcData.fOval;
|
||||||
|
this->changeType(Type::kRRect);
|
||||||
|
this->fRRectData.fRRect.setOval(oval);
|
||||||
|
this->fRRectData.fDir = kDefaultRRectDir;
|
||||||
|
this->fRRectData.fStart = kDefaultRRectStart;
|
||||||
|
this->fRRectData.fInverted = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fStyle.pathEffect()) {
|
||||||
|
// Canonicalize the arc such that the start is always in [0, 360) and the sweep is always
|
||||||
|
// positive.
|
||||||
|
if (fArcData.fSweepAngleDegrees < 0) {
|
||||||
|
fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees;
|
||||||
|
fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) {
|
||||||
|
this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f);
|
||||||
|
}
|
||||||
|
// Possible TODOs here: Look at whether dash pattern results in a single dash and convert to
|
||||||
|
// non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke
|
||||||
|
// could as well if the stroke fills the center.
|
||||||
|
}
|
||||||
|
|
||||||
bool GrShape::attemptToSimplifyStrokedLineToRRect() {
|
bool GrShape::attemptToSimplifyStrokedLineToRRect() {
|
||||||
SkASSERT(Type::kLine == fType);
|
SkASSERT(Type::kLine == fType);
|
||||||
SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
|
SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
|
||||||
|
@ -116,6 +116,9 @@ public:
|
|||||||
this->attemptToSimplifyRRect();
|
this->attemptToSimplifyRRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
|
||||||
|
SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
|
||||||
|
|
||||||
GrShape(const GrShape&);
|
GrShape(const GrShape&);
|
||||||
GrShape& operator=(const GrShape& that);
|
GrShape& operator=(const GrShape& that);
|
||||||
|
|
||||||
@ -209,6 +212,16 @@ public:
|
|||||||
out->setFillType(kDefaultPathFillType);
|
out->setFillType(kDefaultPathFillType);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Type::kArc:
|
||||||
|
SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
|
||||||
|
fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
|
||||||
|
fStyle.isSimpleFill());
|
||||||
|
if (fArcData.fInverted) {
|
||||||
|
out->setFillType(kDefaultPathInverseFillType);
|
||||||
|
} else {
|
||||||
|
out->setFillType(kDefaultPathFillType);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
out->reset();
|
out->reset();
|
||||||
out->moveTo(fLineData.fPts[0]);
|
out->moveTo(fLineData.fPts[0]);
|
||||||
@ -256,6 +269,10 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
case Type::kRRect:
|
case Type::kRRect:
|
||||||
return true;
|
return true;
|
||||||
|
case Type::kArc:
|
||||||
|
return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
|
||||||
|
SkToBool(fArcData.fUseCenter),
|
||||||
|
fStyle.isSimpleFill());
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
return true;
|
return true;
|
||||||
case Type::kPath:
|
case Type::kPath:
|
||||||
@ -282,6 +299,9 @@ public:
|
|||||||
case Type::kRRect:
|
case Type::kRRect:
|
||||||
ret = fRRectData.fInverted;
|
ret = fRRectData.fInverted;
|
||||||
break;
|
break;
|
||||||
|
case Type::kArc:
|
||||||
|
ret = fArcData.fInverted;
|
||||||
|
break;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
ret = fLineData.fInverted;
|
ret = fLineData.fInverted;
|
||||||
break;
|
break;
|
||||||
@ -320,6 +340,8 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
case Type::kRRect:
|
case Type::kRRect:
|
||||||
return true;
|
return true;
|
||||||
|
case Type::kArc:
|
||||||
|
return fArcData.fUseCenter;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
return false;
|
return false;
|
||||||
case Type::kPath:
|
case Type::kPath:
|
||||||
@ -343,6 +365,11 @@ public:
|
|||||||
return SkPath::kLine_SegmentMask;
|
return SkPath::kLine_SegmentMask;
|
||||||
}
|
}
|
||||||
return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
|
return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
|
||||||
|
case Type::kArc:
|
||||||
|
if (fArcData.fUseCenter) {
|
||||||
|
return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
|
||||||
|
}
|
||||||
|
return SkPath::kConic_SegmentMask;
|
||||||
case Type::kLine:
|
case Type::kLine:
|
||||||
return SkPath::kLine_SegmentMask;
|
return SkPath::kLine_SegmentMask;
|
||||||
case Type::kPath:
|
case Type::kPath:
|
||||||
@ -387,6 +414,7 @@ private:
|
|||||||
kEmpty,
|
kEmpty,
|
||||||
kInvertedEmpty,
|
kInvertedEmpty,
|
||||||
kRRect,
|
kRRect,
|
||||||
|
kArc,
|
||||||
kLine,
|
kLine,
|
||||||
kPath,
|
kPath,
|
||||||
};
|
};
|
||||||
@ -438,6 +466,7 @@ private:
|
|||||||
void attemptToSimplifyPath();
|
void attemptToSimplifyPath();
|
||||||
void attemptToSimplifyRRect();
|
void attemptToSimplifyRRect();
|
||||||
void attemptToSimplifyLine();
|
void attemptToSimplifyLine();
|
||||||
|
void attemptToSimplifyArc();
|
||||||
|
|
||||||
bool attemptToSimplifyStrokedLineToRRect();
|
bool attemptToSimplifyStrokedLineToRRect();
|
||||||
|
|
||||||
@ -494,26 +523,33 @@ private:
|
|||||||
return kPathRRectStartIdx;
|
return kPathRRectStartIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type fType;
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
SkRRect fRRect;
|
SkRRect fRRect;
|
||||||
SkPath::Direction fDir;
|
SkPath::Direction fDir;
|
||||||
unsigned fStart;
|
unsigned fStart;
|
||||||
bool fInverted;
|
bool fInverted;
|
||||||
} fRRectData;
|
} fRRectData;
|
||||||
struct {
|
struct {
|
||||||
SkPath fPath;
|
SkRect fOval;
|
||||||
|
SkScalar fStartAngleDegrees;
|
||||||
|
SkScalar fSweepAngleDegrees;
|
||||||
|
int16_t fUseCenter;
|
||||||
|
int16_t fInverted;
|
||||||
|
} fArcData;
|
||||||
|
struct {
|
||||||
|
SkPath fPath;
|
||||||
// Gen ID of the original path (fPath may be modified)
|
// Gen ID of the original path (fPath may be modified)
|
||||||
int32_t fGenID;
|
int32_t fGenID;
|
||||||
} fPathData;
|
} fPathData;
|
||||||
struct {
|
struct {
|
||||||
SkPoint fPts[2];
|
SkPoint fPts[2];
|
||||||
bool fInverted;
|
bool fInverted;
|
||||||
} fLineData;
|
} fLineData;
|
||||||
};
|
};
|
||||||
GrStyle fStyle;
|
GrStyle fStyle;
|
||||||
SkTLazy<SkPath> fInheritedPathForListeners;
|
SkTLazy<SkPath> fInheritedPathForListeners;
|
||||||
SkAutoSTArray<8, uint32_t> fInheritedKey;
|
SkAutoSTArray<8, uint32_t> fInheritedKey;
|
||||||
|
Type fType;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -60,7 +60,7 @@ static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds)
|
|||||||
// everything got clipped out.
|
// everything got clipped out.
|
||||||
static constexpr int kRes = 2000;
|
static constexpr int kRes = 2000;
|
||||||
// This tolerance is in units of 1/kRes fractions of the bounds width/height.
|
// This tolerance is in units of 1/kRes fractions of the bounds width/height.
|
||||||
static constexpr int kTol = 0;
|
static constexpr int kTol = 2;
|
||||||
GR_STATIC_ASSERT(kRes % 4 == 0);
|
GR_STATIC_ASSERT(kRes % 4 == 0);
|
||||||
SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
|
SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
|
||||||
sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
|
sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
|
||||||
@ -389,6 +389,34 @@ private:
|
|||||||
SkRRect fRRect;
|
SkRRect fRRect;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ArcGeo : public Geo {
|
||||||
|
public:
|
||||||
|
ArcGeo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter)
|
||||||
|
: fOval(oval)
|
||||||
|
, fStartAngle(startAngle)
|
||||||
|
, fSweepAngle(sweepAngle)
|
||||||
|
, fUseCenter(useCenter) {}
|
||||||
|
|
||||||
|
SkPath path() const override {
|
||||||
|
SkPath path;
|
||||||
|
SkPathPriv::CreateDrawArcPath(&path, fOval, fStartAngle, fSweepAngle, fUseCenter, false);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrShape makeShape(const SkPaint& paint) const override {
|
||||||
|
return GrShape::MakeArc(fOval, fStartAngle, fSweepAngle, fUseCenter, GrStyle(paint));
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrShape specializes when created from arc params but it doesn't recognize arcs from SkPath.
|
||||||
|
bool isNonPath(const SkPaint& paint) const override { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SkRect fOval;
|
||||||
|
SkScalar fStartAngle;
|
||||||
|
SkScalar fSweepAngle;
|
||||||
|
bool fUseCenter;
|
||||||
|
};
|
||||||
|
|
||||||
class PathGeo : public Geo {
|
class PathGeo : public Geo {
|
||||||
public:
|
public:
|
||||||
enum class Invert { kNo, kYes };
|
enum class Invert { kNo, kYes };
|
||||||
@ -2121,6 +2149,10 @@ DEF_TEST(GrShape, reporter) {
|
|||||||
PathGeo::Invert::kNo));
|
PathGeo::Invert::kNo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Arcs
|
||||||
|
geos.emplace_back(new ArcGeo(SkRect::MakeWH(200, 100), 12.f, 110.f, false));
|
||||||
|
geos.emplace_back(new ArcGeo(SkRect::MakeWH(200, 100), 12.f, 110.f, true));
|
||||||
|
|
||||||
{
|
{
|
||||||
SkPath openRectPath;
|
SkPath openRectPath;
|
||||||
openRectPath.moveTo(0, 0);
|
openRectPath.moveTo(0, 0);
|
||||||
@ -2221,4 +2253,80 @@ DEF_TEST(GrShape, reporter) {
|
|||||||
test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
|
test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF_TEST(GrShape_arcs, reporter) {
|
||||||
|
SkStrokeRec roundStroke(SkStrokeRec::kFill_InitStyle);
|
||||||
|
roundStroke.setStrokeStyle(2.f);
|
||||||
|
roundStroke.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 1.f);
|
||||||
|
|
||||||
|
SkStrokeRec squareStroke(roundStroke);
|
||||||
|
squareStroke.setStrokeParams(SkPaint::kSquare_Cap, SkPaint::kRound_Join, 1.f);
|
||||||
|
|
||||||
|
SkStrokeRec roundStrokeAndFill(roundStroke);
|
||||||
|
roundStrokeAndFill.setStrokeStyle(2.f, true);
|
||||||
|
|
||||||
|
static constexpr SkScalar kIntervals[] = {1, 2};
|
||||||
|
auto dash = SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), 1.5f);
|
||||||
|
|
||||||
|
SkTArray<GrStyle> styles;
|
||||||
|
styles.push_back(GrStyle::SimpleFill());
|
||||||
|
styles.push_back(GrStyle::SimpleHairline());
|
||||||
|
styles.push_back(GrStyle(roundStroke, nullptr));
|
||||||
|
styles.push_back(GrStyle(squareStroke, nullptr));
|
||||||
|
styles.push_back(GrStyle(roundStrokeAndFill, nullptr));
|
||||||
|
styles.push_back(GrStyle(roundStroke, dash));
|
||||||
|
|
||||||
|
for (const auto& style : styles) {
|
||||||
|
// An empty rect never draws anything according to SkCanvas::drawArc() docs.
|
||||||
|
TestCase emptyArc(GrShape::MakeArc(SkRect::MakeEmpty(), 0, 90.f, false, style), reporter);
|
||||||
|
TestCase emptyPath(reporter, SkPath(), style);
|
||||||
|
emptyArc.compare(reporter, emptyPath, TestCase::kAllSame_ComparisonExpecation);
|
||||||
|
|
||||||
|
static constexpr SkRect kOval1{0, 0, 50, 50};
|
||||||
|
static constexpr SkRect kOval2{50, 0, 100, 50};
|
||||||
|
// Test that swapping starting and ending angle doesn't change the shape unless the arc
|
||||||
|
// has a path effect. Also test that different ovals produce different shapes.
|
||||||
|
TestCase arc1CW(GrShape::MakeArc(kOval1, 0, 90.f, false, style), reporter);
|
||||||
|
TestCase arc1CCW(GrShape::MakeArc(kOval1, 90.f, -90.f, false, style), reporter);
|
||||||
|
|
||||||
|
TestCase arc1CWWithCenter(GrShape::MakeArc(kOval1, 0, 90.f, true, style), reporter);
|
||||||
|
TestCase arc1CCWWithCenter(GrShape::MakeArc(kOval1, 90.f, -90.f, true, style), reporter);
|
||||||
|
|
||||||
|
TestCase arc2CW(GrShape::MakeArc(kOval2, 0, 90.f, false, style), reporter);
|
||||||
|
TestCase arc2CWWithCenter(GrShape::MakeArc(kOval2, 0, 90.f, true, style), reporter);
|
||||||
|
|
||||||
|
auto reversedExepectations = style.hasPathEffect()
|
||||||
|
? TestCase::kAllDifferent_ComparisonExpecation
|
||||||
|
: TestCase::kAllSame_ComparisonExpecation;
|
||||||
|
arc1CW.compare(reporter, arc1CCW, reversedExepectations);
|
||||||
|
arc1CWWithCenter.compare(reporter, arc1CCWWithCenter, reversedExepectations);
|
||||||
|
arc1CW.compare(reporter, arc2CW, TestCase::kAllDifferent_ComparisonExpecation);
|
||||||
|
arc1CW.compare(reporter, arc1CWWithCenter, TestCase::kAllDifferent_ComparisonExpecation);
|
||||||
|
arc1CWWithCenter.compare(reporter, arc2CWWithCenter,
|
||||||
|
TestCase::kAllDifferent_ComparisonExpecation);
|
||||||
|
|
||||||
|
// Test that two arcs that start at the same angle but specified differently are equivalent.
|
||||||
|
TestCase arc3A(GrShape::MakeArc(kOval1, 224.f, 73.f, false, style), reporter);
|
||||||
|
TestCase arc3B(GrShape::MakeArc(kOval1, 224.f - 360.f, 73.f, false, style), reporter);
|
||||||
|
arc3A.compare(reporter, arc3B, TestCase::kAllDifferent_ComparisonExpecation);
|
||||||
|
|
||||||
|
// Test that an arc that traverses the entire oval (and then some) is equivalent to the
|
||||||
|
// oval itself unless there is a path effect.
|
||||||
|
TestCase ovalArc(GrShape::MakeArc(kOval1, 150.f, -790.f, false, style), reporter);
|
||||||
|
TestCase oval(GrShape(SkRRect::MakeOval(kOval1)), reporter);
|
||||||
|
auto ovalExpectations = style.hasPathEffect() ? TestCase::kAllDifferent_ComparisonExpecation
|
||||||
|
: TestCase::kAllSame_ComparisonExpecation;
|
||||||
|
if (style.strokeRec().getWidth() >= 0 && style.strokeRec().getCap() != SkPaint::kButt_Cap) {
|
||||||
|
ovalExpectations = TestCase::kAllDifferent_ComparisonExpecation;
|
||||||
|
}
|
||||||
|
ovalArc.compare(reporter, oval, ovalExpectations);
|
||||||
|
|
||||||
|
// If the the arc starts/ends at the center then it is then equivalent to the oval only for
|
||||||
|
// simple fills.
|
||||||
|
TestCase ovalArcWithCenter(GrShape::MakeArc(kOval1, 304.f, 1225.f, true, style), reporter);
|
||||||
|
ovalExpectations = style.isSimpleFill() ? TestCase::kAllSame_ComparisonExpecation
|
||||||
|
: TestCase::kAllDifferent_ComparisonExpecation;
|
||||||
|
ovalArcWithCenter.compare(reporter, oval, ovalExpectations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user