Rip out variable offsetting
Bug: skia:
Change-Id: I0ca1f3b60d05ce02d1e53bb8b989c9dc1babddd1
Reviewed-on: https://skia-review.googlesource.com/150915
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/fuzz/FuzzPolyUtils.cpp b/fuzz/FuzzPolyUtils.cpp
index fda5b5f..8082668 100644
--- a/fuzz/FuzzPolyUtils.cpp
+++ b/fuzz/FuzzPolyUtils.cpp
@@ -27,17 +27,10 @@
fuzz->next(&inset);
SkTDArray<SkPoint> output;
ignoreResult(SkInsetConvexPolygon(polygon, count, inset, &output));
- std::function<SkScalar(const SkPoint&)> distanceFunc = [fuzz](const SkPoint& p) {
- SkScalar retVal;
- fuzz->next(&retVal);
- return retVal;
- };
- ignoreResult(SkInsetConvexPolygon(polygon, count, distanceFunc, &output));
SkScalar offset;
fuzz->next(&offset);
ignoreResult(SkOffsetSimplePolygon(polygon, count, offset, &output));
- ignoreResult(SkOffsetSimplePolygon(polygon, count, distanceFunc, &output));
SkAutoSTMalloc<64, uint16_t> indexMap(count);
for (int index = 0; index < count; ++index) {
diff --git a/gm/polygonoffset.cpp b/gm/polygonoffset.cpp
index bffb351..705eeda 100644
--- a/gm/polygonoffset.cpp
+++ b/gm/polygonoffset.cpp
@@ -410,26 +410,17 @@
// to test perspective and other variable offset functions
class PolygonOffsetGM : public GM {
public:
- PolygonOffsetGM(bool convexOnly, bool variableOffset)
- : fConvexOnly(convexOnly)
- , fVariableOffset(variableOffset) {
+ PolygonOffsetGM(bool convexOnly)
+ : fConvexOnly(convexOnly) {
this->setBGColor(0xFFFFFFFF);
}
protected:
SkString onShortName() override {
if (fConvexOnly) {
- if (fVariableOffset) {
- return SkString("convex-polygon-inset-v");
- } else {
- return SkString("convex-polygon-inset");
- }
+ return SkString("convex-polygon-inset");
} else {
- if (fVariableOffset) {
- return SkString("simple-polygon-offset-v");
- } else {
- return SkString("simple-polygon-offset");
- }
+ return SkString("simple-polygon-offset");
}
}
SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
@@ -563,25 +554,15 @@
SkTDArray<SkPoint> offsetPoly;
size_t count = fConvexOnly ? SK_ARRAY_COUNT(insets) : SK_ARRAY_COUNT(offsets);
- SkScalar localCenterX = bounds.centerX();
for (size_t i = 0; i < count; ++i) {
SkScalar offset = fConvexOnly ? insets[i] : offsets[i];
std::function<SkScalar(const SkPoint&)> offsetFunc;
- if (fVariableOffset) {
- offsetFunc = [offset, localCenterX](const SkPoint& p) {
- return offset + 0.04f*(p.fX - localCenterX);
- };
- } else {
- offsetFunc = [offset](const SkPoint& p) {
- return offset;
- };
- }
bool result;
if (fConvexOnly) {
- result = SkInsetConvexPolygon(data.get(), numPts, offsetFunc, &offsetPoly);
+ result = SkInsetConvexPolygon(data.get(), numPts, offset, &offsetPoly);
} else {
- result = SkOffsetSimplePolygon(data.get(), numPts, offsetFunc, &offsetPoly);
+ result = SkOffsetSimplePolygon(data.get(), numPts, offset, &offsetPoly);
}
if (result) {
SkPath path;
@@ -620,15 +601,12 @@
static constexpr int kGMHeight = 512;
bool fConvexOnly;
- bool fVariableOffset;
typedef GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
-DEF_GM(return new PolygonOffsetGM(true, false);)
-DEF_GM(return new PolygonOffsetGM(true, true);)
-DEF_GM(return new PolygonOffsetGM(false, false);)
-DEF_GM(return new PolygonOffsetGM(false, true);)
+DEF_GM(return new PolygonOffsetGM(true);)
+DEF_GM(return new PolygonOffsetGM(false);)
}
diff --git a/src/utils/SkPolyUtils.cpp b/src/utils/SkPolyUtils.cpp
index 8d378f5..06e8546 100644
--- a/src/utils/SkPolyUtils.cpp
+++ b/src/utils/SkPolyUtils.cpp
@@ -58,65 +58,14 @@
return (quadArea > 0) ? 1 : -1;
}
-// Helper function to compute the individual vector for non-equal offsets
-inline void compute_offset(SkScalar d, const SkPoint& polyPoint, int side,
- const SkPoint& outerTangentIntersect, SkVector* v) {
- SkScalar dsq = d * d;
- SkVector dP = outerTangentIntersect - polyPoint;
- SkScalar dPlenSq = SkPointPriv::LengthSqd(dP);
- if (SkScalarNearlyZero(dPlenSq, SK_ScalarNearlyZero*SK_ScalarNearlyZero)) {
- v->set(0, 0);
- } else {
- SkScalar discrim = SkScalarSqrt(dPlenSq - dsq);
- v->fX = (dsq*dP.fX - side * d*dP.fY*discrim) / dPlenSq;
- v->fY = (dsq*dP.fY + side * d*dP.fX*discrim) / dPlenSq;
- }
-}
-
-// Compute difference vector to offset p0-p1 'd0' and 'd1' units in direction specified by 'side'
-bool compute_offset_vectors(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
- int side, SkPoint* vector0, SkPoint* vector1) {
+// Compute difference vector to offset p0-p1 'offset' units in direction specified by 'side'
+void compute_offset_vector(const SkPoint& p0, const SkPoint& p1, SkScalar offset, int side,
+ SkPoint* vector) {
SkASSERT(side == -1 || side == 1);
- if (SkScalarNearlyEqual(d0, d1)) {
- // if distances are equal, can just outset by the perpendicular
- SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
- perp.setLength(d0*side);
- *vector0 = perp;
- *vector1 = perp;
- } else {
- SkScalar d0abs = SkTAbs(d0);
- SkScalar d1abs = SkTAbs(d1);
- // Otherwise we need to compute the outer tangent.
- // See: http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm
- if (d0abs < d1abs) {
- side = -side;
- }
- SkScalar dD = d0abs - d1abs;
- // if one circle is inside another, we can't compute an offset
- if (dD*dD >= SkPointPriv::DistanceToSqd(p0, p1)) {
- return false;
- }
- SkPoint outerTangentIntersect = SkPoint::Make((p1.fX*d0abs - p0.fX*d1abs) / dD,
- (p1.fY*d0abs - p0.fY*d1abs) / dD);
-
- compute_offset(d0, p0, side, outerTangentIntersect, vector0);
- compute_offset(d1, p1, side, outerTangentIntersect, vector1);
- }
-
- return true;
-}
-
-// Offset line segment p0-p1 'd0' and 'd1' units in the direction specified by 'side'
-bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
- int side, SkPoint* offset0, SkPoint* offset1) {
- SkVector v0, v1;
- if (!compute_offset_vectors(p0, p1, d0, d1, side, &v0, &v1)) {
- return false;
- }
- *offset0 = p0 + v0;
- *offset1 = p1 + v1;
-
- return true;
+ // if distances are equal, can just outset by the perpendicular
+ SkVector perp = SkVector::Make(p0.fY - p1.fY, p1.fX - p0.fX);
+ perp.setLength(offset*side);
+ *vector = perp;
}
// check interval to see if intersection is in segment
@@ -269,14 +218,14 @@
struct OffsetEdge {
OffsetEdge* fPrev;
OffsetEdge* fNext;
- OffsetSegment fInset;
+ OffsetSegment fOffset;
SkPoint fIntersection;
SkScalar fTValue;
uint16_t fIndex;
uint16_t fEnd;
void init(uint16_t start = 0, uint16_t end = 0) {
- fIntersection = fInset.fP0;
+ fIntersection = fOffset.fP0;
fTValue = SK_ScalarMin;
fIndex = start;
fEnd = end;
@@ -286,8 +235,8 @@
bool checkIntersection(const OffsetEdge* that,
SkPoint* p, SkScalar* s, SkScalar* t) {
if (this->fEnd == that->fIndex) {
- SkPoint p1 = this->fInset.fP0 + this->fInset.fV;
- if (SkPointPriv::EqualsWithinTolerance(p1, that->fInset.fP0)) {
+ SkPoint p1 = this->fOffset.fP0 + this->fOffset.fV;
+ if (SkPointPriv::EqualsWithinTolerance(p1, that->fOffset.fP0)) {
*p = p1;
*s = SK_Scalar1;
*t = 0;
@@ -295,15 +244,15 @@
}
}
- return compute_intersection(this->fInset, that->fInset, p, s, t);
+ return compute_intersection(this->fOffset, that->fOffset, p, s, t);
}
// computes the line intersection and then the "distance" from that to this
// this is really a signed squared distance, where negative means that
- // the intersection lies inside this->fInset
+ // the intersection lies inside this->fOffset
SkScalar computeCrossingDistance(const OffsetEdge* that) {
- const OffsetSegment& s0 = this->fInset;
- const OffsetSegment& s1 = that->fInset;
+ const OffsetSegment& s0 = this->fOffset;
+ const OffsetSegment& s1 = that->fOffset;
const SkVector& v0 = s0.fV;
const SkVector& v1 = s1.fV;
@@ -353,8 +302,7 @@
// Note: the assumption is that inputPolygon is convex and has no coincident points.
//
bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
- std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
- SkTDArray<SkPoint>* insetPolygon) {
+ SkScalar inset, SkTDArray<SkPoint>* insetPolygon) {
if (inputPolygonSize < 3) {
return false;
}
@@ -384,18 +332,13 @@
inputPolygonVerts[next])*winding < 0) {
return false;
}
- SkPoint p0, p1;
- if (!SkOffsetSegment(inputPolygonVerts[curr], inputPolygonVerts[next],
- insetDistanceFunc(inputPolygonVerts[curr]),
- insetDistanceFunc(inputPolygonVerts[next]),
- winding,
- &p0, &p1)) {
- return false;
- }
+ SkVector v = inputPolygonVerts[next] - inputPolygonVerts[curr];
+ SkVector perp = SkVector::Make(-v.fY, v.fX);
+ perp.setLength(inset*winding);
edgeData[curr].fPrev = &edgeData[prev];
edgeData[curr].fNext = &edgeData[next];
- edgeData[curr].fInset.fP0 = p0;
- edgeData[curr].fInset.fV = p1 - p0;
+ edgeData[curr].fOffset.fP0 = inputPolygonVerts[curr] + perp;
+ edgeData[curr].fOffset.fV = v;
edgeData[curr].init();
}
@@ -413,7 +356,7 @@
SkScalar s, t;
SkPoint intersection;
- if (compute_intersection(prevEdge->fInset, currEdge->fInset,
+ if (compute_intersection(prevEdge->fOffset, currEdge->fOffset,
&intersection, &s, &t)) {
// if new intersection is further back on previous inset from the prior intersection
if (s < prevEdge->fTValue) {
@@ -439,13 +382,13 @@
}
} else {
// if prev to right side of curr
- int side = winding*compute_side(currEdge->fInset.fP0,
- currEdge->fInset.fV,
- prevEdge->fInset.fP0);
+ int side = winding*compute_side(currEdge->fOffset.fP0,
+ currEdge->fOffset.fV,
+ prevEdge->fOffset.fP0);
if (side < 0 &&
- side == winding*compute_side(currEdge->fInset.fP0,
- currEdge->fInset.fV,
- prevEdge->fInset.fP0 + prevEdge->fInset.fV)) {
+ side == winding*compute_side(currEdge->fOffset.fP0,
+ currEdge->fOffset.fV,
+ prevEdge->fOffset.fP0 + prevEdge->fOffset.fV)) {
// no point in considering this one again
remove_node(prevEdge, &head);
--insetVertexCount;
@@ -1165,8 +1108,8 @@
static void setup_offset_edge(OffsetEdge* currEdge,
const SkPoint& endpoint0, const SkPoint& endpoint1,
uint16_t startIndex, uint16_t endIndex) {
- currEdge->fInset.fP0 = endpoint0;
- currEdge->fInset.fV = endpoint1 - endpoint0;
+ currEdge->fOffset.fP0 = endpoint0;
+ currEdge->fOffset.fV = endpoint1 - endpoint0;
currEdge->init(startIndex, endIndex);
}
@@ -1179,8 +1122,7 @@
return (side*winding*offset < 0);
}
-bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
- std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
+bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize, SkScalar offset,
SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
if (inputPolygonSize < 3) {
return false;
@@ -1191,6 +1133,10 @@
return false;
}
+ if (!SkScalarIsFinite(offset)) {
+ return false;
+ }
+
// get winding direction
int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
if (0 == winding) {
@@ -1198,14 +1144,7 @@
}
// build normals
- SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
- SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
- SkAutoSTMalloc<64, SkScalar> offset(inputPolygonSize);
- SkScalar currOffset = offsetDistanceFunc(inputPolygonVerts[0]);
- if (!SkScalarIsFinite(currOffset)) {
- return false;
- }
- offset[0] = currOffset;
+ SkAutoSTMalloc<64, SkVector> normals(inputPolygonSize);
int numEdges = 0;
for (int currIndex = 0, prevIndex = inputPolygonSize - 1;
currIndex < inputPolygonSize;
@@ -1214,23 +1153,15 @@
return false;
}
int nextIndex = (currIndex + 1) % inputPolygonSize;
- SkScalar nextOffset = offsetDistanceFunc(inputPolygonVerts[nextIndex]);
- if (!SkScalarIsFinite(nextOffset)) {
- return false;
- }
- offset[nextIndex] = nextOffset;
- if (!compute_offset_vectors(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
- currOffset, nextOffset, winding,
- &normal0[currIndex], &normal1[nextIndex])) {
- return false;
- }
+ compute_offset_vector(inputPolygonVerts[currIndex], inputPolygonVerts[nextIndex],
+ offset, winding, &normals[currIndex]);
if (currIndex > 0) {
// if reflex point, we need to add extra edges
- if (is_reflex_vertex(inputPolygonVerts, winding, currOffset,
+ if (is_reflex_vertex(inputPolygonVerts, winding, offset,
prevIndex, currIndex, nextIndex)) {
SkScalar rotSin, rotCos;
int numSteps;
- if (!SkComputeRadialSteps(normal1[currIndex], normal0[currIndex], currOffset,
+ if (!SkComputeRadialSteps(normals[prevIndex], normals[currIndex], offset,
&rotSin, &rotCos, &numSteps)) {
return false;
}
@@ -1238,13 +1169,12 @@
}
}
numEdges++;
- currOffset = nextOffset;
}
// finish up the edge counting
- if (is_reflex_vertex(inputPolygonVerts, winding, currOffset, inputPolygonSize-1, 0, 1)) {
+ if (is_reflex_vertex(inputPolygonVerts, winding, offset, inputPolygonSize-1, 0, 1)) {
SkScalar rotSin, rotCos;
int numSteps;
- if (!SkComputeRadialSteps(normal1[0], normal0[0], currOffset,
+ if (!SkComputeRadialSteps(normals[inputPolygonSize-1], normals[0], offset,
&rotSin, &rotCos, &numSteps)) {
return false;
}
@@ -1259,12 +1189,12 @@
prevIndex = currIndex, ++currIndex) {
int nextIndex = (currIndex + 1) % inputPolygonSize;
// if reflex point, fill in curve
- if (is_reflex_vertex(inputPolygonVerts, winding, offset[currIndex],
+ if (is_reflex_vertex(inputPolygonVerts, winding, offset,
prevIndex, currIndex, nextIndex)) {
SkScalar rotSin, rotCos;
int numSteps;
- SkVector prevNormal = normal1[currIndex];
- if (!SkComputeRadialSteps(prevNormal, normal0[currIndex], offset[currIndex],
+ SkVector prevNormal = normals[prevIndex];
+ if (!SkComputeRadialSteps(prevNormal, normals[currIndex], offset,
&rotSin, &rotCos, &numSteps)) {
return false;
}
@@ -1286,7 +1216,7 @@
}
setup_offset_edge(currEdge,
inputPolygonVerts[currIndex] + prevNormal,
- inputPolygonVerts[currIndex] + normal0[currIndex],
+ inputPolygonVerts[currIndex] + normals[currIndex],
currIndex, currIndex);
currEdge->fPrev = prevEdge;
if (prevEdge) {
@@ -1298,8 +1228,8 @@
// Add the edge
auto currEdge = edgeData.push_back_n(1);
setup_offset_edge(currEdge,
- inputPolygonVerts[currIndex] + normal0[currIndex],
- inputPolygonVerts[nextIndex] + normal1[nextIndex],
+ inputPolygonVerts[currIndex] + normals[currIndex],
+ inputPolygonVerts[nextIndex] + normals[currIndex],
currIndex, nextIndex);
currEdge->fPrev = prevEdge;
if (prevEdge) {
@@ -1361,12 +1291,12 @@
// if both lead to direct collision
if (dist0 < 0 && dist1 < 0) {
// check first to see if either represent parts of one contour
- SkPoint p1 = prevPrevEdge->fInset.fP0 + prevPrevEdge->fInset.fV;
+ SkPoint p1 = prevPrevEdge->fOffset.fP0 + prevPrevEdge->fOffset.fV;
bool prevSameContour = SkPointPriv::EqualsWithinTolerance(p1,
- prevEdge->fInset.fP0);
- p1 = currEdge->fInset.fP0 + currEdge->fInset.fV;
+ prevEdge->fOffset.fP0);
+ p1 = currEdge->fOffset.fP0 + currEdge->fOffset.fV;
bool currSameContour = SkPointPriv::EqualsWithinTolerance(p1,
- currNextEdge->fInset.fP0);
+ currNextEdge->fOffset.fP0);
// want to step along contour to find intersections rather than jump to new one
if (currSameContour && !prevSameContour) {
diff --git a/src/utils/SkPolyUtils.h b/src/utils/SkPolyUtils.h
index a79ec38..7263eca 100644
--- a/src/utils/SkPolyUtils.h
+++ b/src/utils/SkPolyUtils.h
@@ -14,22 +14,6 @@
#include "SkPoint.h"
/**
- * Generates a polygon that is inset a variable distance (controlled by offsetDistanceFunc)
- * from the boundary of a given convex polygon.
- *
- * @param inputPolygonVerts Array of points representing the vertices of the original polygon.
- * It should be convex and have no coincident points.
- * @param inputPolygonSize Number of vertices in the original polygon.
- * @param insetDistanceFunc How far we wish to inset the polygon for a given position.
- * This should return a positive value.
- * @param insetPolygon The resulting inset polygon, if any.
- * @return true if an inset polygon exists, false otherwise.
- */
-bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
- std::function<SkScalar(const SkPoint&)> insetDistanceFunc,
- SkTDArray<SkPoint>* insetPolygon);
-
-/**
* Generates a polygon that is inset a constant from the boundary of a given convex polygon.
*
* @param inputPolygonVerts Array of points representing the vertices of the original polygon.
@@ -39,30 +23,8 @@
* @param insetPolygon The resulting inset polygon, if any.
* @return true if an inset polygon exists, false otherwise.
*/
-inline bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
- SkScalar inset, SkTDArray<SkPoint>* insetPolygon) {
- return SkInsetConvexPolygon(inputPolygonVerts, inputPolygonSize,
- [inset](const SkPoint&) { return inset; },
- insetPolygon);
-}
-
-/**
- * Generates a simple polygon (if possible) that is offset a variable distance (controlled by
- * offsetDistanceFunc) from the boundary of a given simple polygon.
- * The input polygon must be simple and have no coincident vertices or collinear edges.
- *
- * @param inputPolygonVerts Array of points representing the vertices of the original polygon.
- * @param inputPolygonSize Number of vertices in the original polygon.
- * @param offsetDistanceFunc How far we wish to offset the polygon for a given position.
- * Positive values indicate insetting, negative values outsetting.
- * @param offsetPolgon The resulting offset polygon, if any.
- * @param polygonIndices The indices of the original polygon that map to the new one.
- * @return true if an offset simple polygon exists, false otherwise.
- */
-bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
- std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
- SkTDArray<SkPoint>* offsetPolygon,
- SkTDArray<int>* polygonIndices = nullptr);
+bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
+ SkScalar inset, SkTDArray<SkPoint>* insetPolygon);
/**
* Generates a simple polygon (if possible) that is offset a constant distance from the boundary
@@ -77,30 +39,9 @@
* @param polygonIndices The indices of the original polygon that map to the new one.
* @return true if an offset simple polygon exists, false otherwise.
*/
-inline bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
- SkScalar offset, SkTDArray<SkPoint>* offsetPolygon,
- SkTDArray<int>* polygonIndices = nullptr) {
- return SkOffsetSimplePolygon(inputPolygonVerts, inputPolygonSize,
- [offset](const SkPoint&) { return offset; },
- offsetPolygon, polygonIndices);
-}
-
-/**
- * Offset a segment by the given distance at each point.
- * Uses the outer tangents of two circles centered on each endpoint.
- * See: https://en.wikipedia.org/wiki/Tangent_lines_to_circles
- *
- * @param p0 First endpoint.
- * @param p1 Second endpoint.
- * @param d0 Offset distance from first endpoint.
- * @param d1 Offset distance from second endpoint.
- * @param side Indicates whether we want to offset to the left (1) or right (-1) side of segment.
- * @param offset0 First endpoint of offset segment.
- * @param offset1 Second endpoint of offset segment.
- * @return true if an offset segment exists, false otherwise.
- */
-bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
- int side, SkPoint* offset0, SkPoint* offset1);
+bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
+ SkScalar offset, SkTDArray<SkPoint>* offsetPolygon,
+ SkTDArray<int>* polygonIndices = nullptr);
/**
* Compute the number of points needed for a circular join when offsetting a vertex.