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>
This commit is contained in:
Jim Van Verth 2018-09-05 12:41:56 -04:00 committed by Skia Commit-Bot
parent 8e449ebec3
commit da58cac61d
4 changed files with 65 additions and 223 deletions

View File

@ -27,17 +27,10 @@ DEF_FUZZ(PolyUtils, fuzz) {
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) {

View File

@ -410,26 +410,17 @@ namespace skiagm {
// 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 @@ protected:
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 @@ private:
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);)
}

View File

@ -58,65 +58,14 @@ int SkGetPolygonWinding(const SkPoint* polygonVerts, int polygonSize) {
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 @@ bool SkIsConvexPolygon(const SkPoint* polygonVerts, int polygonSize) {
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 @@ struct OffsetEdge {
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 @@ struct OffsetEdge {
}
}
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 @@ static void remove_node(const OffsetEdge* node, OffsetEdge** head) {
// 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 @@ bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize
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 @@ bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize
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 @@ bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize
}
} 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 @@ bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
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 @@ static bool is_reflex_vertex(const SkPoint* inputPolygonVerts, int winding, SkSc
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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
return false;
}
if (!SkScalarIsFinite(offset)) {
return false;
}
// get winding direction
int winding = SkGetPolygonWinding(inputPolygonVerts, inputPolygonSize);
if (0 == winding) {
@ -1198,14 +1144,7 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
}
// 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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
}
}
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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
}
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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
// 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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
// 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) {

View File

@ -13,22 +13,6 @@
#include "SkTDArray.h"
#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.
*
@ -39,30 +23,8 @@ bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize
* @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 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
* @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.