replace arcto quads with a conic
also, remove code used only for
the quad generation
R=reed@google.com
BUG=578885
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1612543003
Review URL: https://codereview.chromium.org/1612543003
diff --git a/samplecode/SampleQuadStroker.cpp b/samplecode/SampleQuadStroker.cpp
index 88f7dfd..eae683f 100644
--- a/samplecode/SampleQuadStroker.cpp
+++ b/samplecode/SampleQuadStroker.cpp
@@ -77,7 +77,10 @@
}
static void erase(SkSurface* surface) {
- surface->getCanvas()->clear(SK_ColorTRANSPARENT);
+ SkCanvas* canvas = surface->getCanvas();
+ if (canvas) {
+ canvas->clear(SK_ColorTRANSPARENT);
+ }
}
struct StrokeTypeButton {
@@ -97,10 +100,11 @@
};
enum {
- kCount = 15
+ kCount = 18
};
SkPoint fPts[kCount];
SkRect fWeightControl;
+ SkRect fRadiusControl;
SkRect fErrorControl;
SkRect fWidthControl;
SkRect fBounds;
@@ -111,12 +115,14 @@
StrokeTypeButton fCubicButton;
StrokeTypeButton fConicButton;
StrokeTypeButton fQuadButton;
+ StrokeTypeButton fArcButton;
StrokeTypeButton fRRectButton;
CircleTypeButton fCircleButton;
StrokeTypeButton fTextButton;
SkString fText;
SkScalar fTextSize;
SkScalar fWeight;
+ SkScalar fRadius;
SkScalar fWidth, fDWidth;
SkScalar fWidthScale;
int fW, fH, fZoom;
@@ -147,32 +153,39 @@
fPts[8].set(150, 200);
fPts[9].set(250, 150);
- fPts[10].set(200, 200); // rrect
- fPts[11].set(400, 400);
+ fPts[10].set(250, 200); // arc
+ fPts[11].set(250, 300);
+ fPts[12].set(150, 350);
- fPts[12].set(250, 250); // oval
- fPts[13].set(450, 450);
+ fPts[13].set(200, 200); // rrect
+ fPts[14].set(400, 400);
+
+ fPts[15].set(250, 250); // oval
+ fPts[16].set(450, 450);
fText = "a";
fTextSize = 12;
fWidth = 50;
fDWidth = 0.25f;
fWeight = 1;
+ fRadius = 150;
fCubicButton.fLabel = 'C';
fCubicButton.fEnabled = false;
fConicButton.fLabel = 'K';
- fConicButton.fEnabled = true;
+ fConicButton.fEnabled = false;
fQuadButton.fLabel = 'Q';
fQuadButton.fEnabled = false;
+ fArcButton.fLabel = 'A';
+ fArcButton.fEnabled = true;
fRRectButton.fLabel = 'R';
fRRectButton.fEnabled = false;
fCircleButton.fLabel = 'O';
- fCircleButton.fEnabled = false;
- fCircleButton.fFill = false;
+ fCircleButton.fEnabled = true;
+ fCircleButton.fFill = true;
fTextButton.fLabel = 'T';
fTextButton.fEnabled = false;
- fAnimate = true;
+ fAnimate = false;
setAsNeeded();
}
@@ -205,6 +218,7 @@
}
void onSizeChange() override {
+ fRadiusControl.setXYWH(this->width() - 200, 30, 30, 400);
fWeightControl.setXYWH(this->width() - 150, 30, 30, 400);
fErrorControl.setXYWH(this->width() - 100, 30, 30, 400);
fWidthControl.setXYWH(this->width() - 50, 30, 30, 400);
@@ -215,6 +229,8 @@
buttonOffset += 50;
fQuadButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
buttonOffset += 50;
+ fArcButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
+ buttonOffset += 50;
fRRectButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
buttonOffset += 50;
fCircleButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
@@ -519,13 +535,47 @@
void setAsNeeded() {
if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled) {
setForSingles();
- } else if (fRRectButton.fEnabled || fCircleButton.fEnabled) {
+ } else if (fRRectButton.fEnabled || fCircleButton.fEnabled || fArcButton.fEnabled) {
setForGeometry();
} else {
setForText();
}
}
+ bool arcCenter(SkPoint* center) {
+ SkPath path;
+ path.moveTo(fPts[10]);
+ path.arcTo(fPts[11], fPts[12], fRadius);
+ SkPath::Iter iter(path, false);
+ SkPoint pts[4];
+ iter.next(pts);
+ if (SkPath::kLine_Verb == iter.next(pts)) {
+ iter.next(pts);
+ }
+ SkVector before = pts[0] - pts[1];
+ SkVector after = pts[1] - pts[2];
+ before.setLength(fRadius);
+ after.setLength(fRadius);
+ SkVector beforeCCW, afterCCW;
+ before.rotateCCW(&beforeCCW);
+ after.rotateCCW(&afterCCW);
+ beforeCCW += pts[0];
+ afterCCW += pts[2];
+ *center = beforeCCW;
+ if (SkScalarNearlyEqual(beforeCCW.fX, afterCCW.fX)
+ && SkScalarNearlyEqual(beforeCCW.fY, afterCCW.fY)) {
+ return true;
+ }
+ SkVector beforeCW, afterCW;
+ before.rotateCW(&beforeCW);
+ after.rotateCW(&afterCW);
+ beforeCW += pts[0];
+ afterCW += pts[2];
+ *center = beforeCW;
+ return SkScalarNearlyEqual(beforeCW.fX, afterCW.fX)
+ && SkScalarNearlyEqual(beforeCCW.fY, afterCW.fY);
+ }
+
void onDrawContent(SkCanvas* canvas) override {
SkPath path;
SkScalar width = fWidth;
@@ -553,10 +603,23 @@
draw_stroke(canvas, path, width, 950, false);
}
+ if (fArcButton.fEnabled) {
+ path.reset();
+ path.moveTo(fPts[10]);
+ path.arcTo(fPts[11], fPts[12], fRadius);
+ setForGeometry();
+ draw_stroke(canvas, path, width, 950, false);
+ SkPath pathPts;
+ pathPts.moveTo(fPts[10]);
+ pathPts.lineTo(fPts[11]);
+ pathPts.lineTo(fPts[12]);
+ draw_points(canvas, pathPts, SK_ColorDKGRAY, true);
+ }
+
if (fRRectButton.fEnabled) {
SkScalar rad = 32;
SkRect r;
- r.set(&fPts[10], 2);
+ r.set(&fPts[13], 2);
path.reset();
SkRRect rr;
rr.setRectXY(r, rad, rad);
@@ -579,10 +642,17 @@
if (fCircleButton.fEnabled) {
path.reset();
SkRect r;
- r.set(&fPts[12], 2);
+ r.set(&fPts[15], 2);
path.addOval(r);
setForGeometry();
if (fCircleButton.fFill) {
+ if (fArcButton.fEnabled) {
+ SkPoint center;
+ if (arcCenter(¢er)) {
+ r.set(center.fX - fRadius, center.fY - fRadius, center.fX + fRadius,
+ center.fY + fRadius);
+ }
+ }
draw_fill(canvas, r, width);
} else {
draw_stroke(canvas, path, width, 950, false);
@@ -611,6 +681,9 @@
if (fConicButton.fEnabled) {
draw_control(canvas, fWeightControl, fWeight, 0, 5, "weight");
}
+ if (fArcButton.fEnabled) {
+ draw_control(canvas, fRadiusControl, fRadius, 0, 500, "radius");
+ }
#ifdef SK_DEBUG
draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax,
"error");
@@ -620,6 +693,7 @@
draw_button(canvas, fQuadButton);
draw_button(canvas, fCubicButton);
draw_button(canvas, fConicButton);
+ draw_button(canvas, fArcButton);
draw_button(canvas, fRRectButton);
draw_button(canvas, fCircleButton);
draw_button(canvas, fTextButton);
@@ -643,39 +717,46 @@
if (fWeightControl.contains(rectPt)) {
return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1);
}
+ if (fRadiusControl.contains(rectPt)) {
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2);
+ }
#ifdef SK_DEBUG
if (fErrorControl.contains(rectPt)) {
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3);
}
#endif
if (fWidthControl.contains(rectPt)) {
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4);
}
if (fCubicButton.fBounds.contains(rectPt)) {
fCubicButton.fEnabled ^= true;
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5);
}
if (fConicButton.fBounds.contains(rectPt)) {
fConicButton.fEnabled ^= true;
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6);
}
if (fQuadButton.fBounds.contains(rectPt)) {
fQuadButton.fEnabled ^= true;
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7);
+ }
+ if (fArcButton.fBounds.contains(rectPt)) {
+ fArcButton.fEnabled ^= true;
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8);
}
if (fRRectButton.fBounds.contains(rectPt)) {
fRRectButton.fEnabled ^= true;
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9);
}
if (fCircleButton.fBounds.contains(rectPt)) {
bool wasEnabled = fCircleButton.fEnabled;
fCircleButton.fEnabled = !fCircleButton.fFill;
fCircleButton.fFill = wasEnabled && !fCircleButton.fFill;
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 10);
}
if (fTextButton.fBounds.contains(rectPt)) {
fTextButton.fEnabled ^= true;
- return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9);
+ return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 11);
}
return this->INHERITED::onFindClickHandler(x, y, modi);
}
@@ -693,15 +774,17 @@
this->inval(nullptr);
} else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) {
fWeight = MapScreenYtoValue(click->fICurr.fY, fWeightControl, 0, 5);
+ } else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) {
+ fRadius = MapScreenYtoValue(click->fICurr.fY, fRadiusControl, 0, 500);
}
#ifdef SK_DEBUG
- else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) {
+ else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY,
fErrorControl, kStrokerErrorMin, kStrokerErrorMax));
gDebugStrokerErrorSet = true;
}
#endif
- else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
+ else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) {
fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, fWidthControl,
kWidthMin, kWidthMax));
fAnimate = fWidth <= kWidthMin;
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
index 2ea3095..04e396c 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -951,6 +951,7 @@
///////////////////////////////////////////////////////////////////////////////
+#ifdef SK_SUPPORT_LEGACY_ARCTO
/* Find t value for quadratic [a, b, c] = d.
Return 0 if there is no solution within [0, 1)
*/
@@ -1120,7 +1121,7 @@
matrix.mapPoints(quadPoints, pointCount);
return pointCount;
}
-
+#endif
///////////////////////////////////////////////////////////////////////////////
//
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
index 0fd5a61..6a8546a 100644
--- a/src/core/SkGeometry.h
+++ b/src/core/SkGeometry.h
@@ -194,6 +194,7 @@
kCCW_SkRotationDirection
};
+#ifdef SK_SUPPORT_LEGACY_ARCTO
/** Maximum number of points needed in the quadPoints[] parameter for
SkBuildQuadArc()
*/
@@ -207,6 +208,7 @@
*/
int SkBuildQuadArc(const SkVector& unitStart, const SkVector& unitStop,
SkRotationDirection, const SkMatrix*, SkPoint quadPoints[]);
+#endif
struct SkConic {
SkConic() {}
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 4c148c5..c77a25b 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1301,13 +1301,16 @@
return;
}
- SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
- if (dist < 0) {
- dist = -dist;
- }
+ SkScalar dist = SkScalarAbs(SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh));
SkScalar xx = x1 - SkScalarMul(dist, before.fX);
SkScalar yy = y1 - SkScalarMul(dist, before.fY);
+#ifndef SK_SUPPORT_LEGACY_ARCTO
+ after.setLength(dist);
+ this->lineTo(xx, yy);
+ SkScalar weight = SkScalarSqrt(SK_ScalarHalf + cosh * SK_ScalarHalf);
+ this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
+#else
SkRotationDirection arcDir;
// now turn before/after into normals
@@ -1336,6 +1339,7 @@
for (int i = 1; i < count; i += 2) {
this->quadTo(pts[i], pts[i+1]);
}
+#endif
}
///////////////////////////////////////////////////////////////////////////////