aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/convex_all_line_paths.cpp2
-rw-r--r--gm/polygonoffset.cpp2
-rw-r--r--gn/utils.gni4
-rwxr-xr-xsrc/utils/SkPolyUtils.cpp (renamed from src/utils/SkOffsetPolygon.cpp)281
-rwxr-xr-xsrc/utils/SkPolyUtils.h (renamed from src/utils/SkOffsetPolygon.h)40
-rwxr-xr-xsrc/utils/SkShadowTessellator.cpp35
-rw-r--r--src/utils/SkShadowUtils.cpp8
-rw-r--r--tests/InsetConvexPolyTest.cpp2
-rw-r--r--tests/OffsetSimplePolyTest.cpp9
9 files changed, 302 insertions, 81 deletions
diff --git a/gm/convex_all_line_paths.cpp b/gm/convex_all_line_paths.cpp
index b1907bccb7..e21711b2bc 100644
--- a/gm/convex_all_line_paths.cpp
+++ b/gm/convex_all_line_paths.cpp
@@ -6,7 +6,7 @@
*/
#include "gm.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
#include "SkPathPriv.h"
static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
diff --git a/gm/polygonoffset.cpp b/gm/polygonoffset.cpp
index 48b6d1a4e4..44a313b285 100644
--- a/gm/polygonoffset.cpp
+++ b/gm/polygonoffset.cpp
@@ -7,7 +7,7 @@
#include "gm.h"
#include "sk_tool_utils.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
#include "SkPathPriv.h"
static void create_ngon(int n, SkPoint* pts, SkScalar w, SkScalar h, SkPath::Direction dir) {
diff --git a/gn/utils.gni b/gn/utils.gni
index 63b063bf46..95c4e937c7 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -47,8 +47,6 @@ skia_utils_sources = [
"$_src/utils/SkMultiPictureDocument.cpp",
"$_src/utils/SkNWayCanvas.cpp",
"$_src/utils/SkNullCanvas.cpp",
- "$_src/utils/SkOffsetPolygon.cpp",
- "$_src/utils/SkOffsetPolygon.h",
"$_src/utils/SkOSPath.cpp",
"$_src/utils/SkOSPath.h",
"$_src/utils/SkPaintFilterCanvas.cpp",
@@ -57,6 +55,8 @@ skia_utils_sources = [
"$_src/utils/SkParsePath.cpp",
"$_src/utils/SkPatchUtils.cpp",
"$_src/utils/SkPatchUtils.h",
+ "$_src/utils/SkPolyUtils.cpp",
+ "$_src/utils/SkPolyUtils.h",
"$_src/utils/SkShadowTessellator.cpp",
"$_src/utils/SkShadowTessellator.h",
"$_src/utils/SkShadowUtils.cpp",
diff --git a/src/utils/SkOffsetPolygon.cpp b/src/utils/SkPolyUtils.cpp
index c72f7d407b..ea9d649a5c 100755
--- a/src/utils/SkOffsetPolygon.cpp
+++ b/src/utils/SkPolyUtils.cpp
@@ -5,12 +5,16 @@
* found in the LICENSE file.
*/
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
#include "SkPointPriv.h"
#include "SkTArray.h"
#include "SkTemplates.h"
#include "SkTDPQueue.h"
+#include "SkTInternalLList.h"
+
+//////////////////////////////////////////////////////////////////////////////////
+// Helper data structures and functions
struct OffsetSegment {
SkPoint fP0;
@@ -33,23 +37,17 @@ static int compute_side(const SkPoint& s0, const SkPoint& s1, const SkPoint& p)
// returns 1 for ccw, -1 for cw and 0 if degenerate
static int get_winding(const SkPoint* polygonVerts, int polygonSize) {
- SkPoint p0 = polygonVerts[0];
- SkPoint p1 = polygonVerts[1];
-
- for (int i = 2; i < polygonSize; ++i) {
- SkPoint p2 = polygonVerts[i];
-
- // determine if cw or ccw
- int side = compute_side(p0, p1, p2);
- if (0 != side) {
- return ((side > 0) ? 1 : -1);
- }
-
- // if nearly collinear, treat as straight line and continue
- p1 = p2;
+ // compute area and use sign to determine winding
+ SkScalar quadArea = 0;
+ for (int curr = 0; curr < polygonSize; ++curr) {
+ int next = (curr + 1) % polygonSize;
+ quadArea += polygonVerts[curr].cross(polygonVerts[next]);
}
-
- return 0;
+ if (SkScalarNearlyZero(quadArea)) {
+ return 0;
+ }
+ // 1 == ccw, -1 == cw
+ return (quadArea > 0) ? 1 : -1;
}
// Helper function to compute the individual vector for non-equal offsets
@@ -261,6 +259,8 @@ struct EdgeData {
}
};
+//////////////////////////////////////////////////////////////////////////////////
+
// The objective here is to inset all of the edges by the given distance, and then
// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
// we should only be making left-hand turns (for cw polygons, we use the winding
@@ -280,6 +280,7 @@ bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize
return false;
}
+ // get winding direction
int winding = get_winding(inputPolygonVerts, inputPolygonSize);
if (0 == winding) {
return false;
@@ -397,9 +398,11 @@ bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize
return (insetPolygon->count() >= 3 && is_convex(*insetPolygon));
}
-// compute the number of points needed for a circular join when offsetting a reflex vertex
-static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
- SkScalar* rotSin, SkScalar* rotCos, int* n) {
+///////////////////////////////////////////////////////////////////////////////////////////
+
+// compute the number of points needed for a circular join when offsetting a reflex vertex
+void SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar r,
+ SkScalar* rotSin, SkScalar* rotCos, int* n) {
const SkScalar kRecipPixelsPerArcSegment = 0.25f;
SkScalar rCos = v1.dot(v2);
@@ -529,10 +532,10 @@ public:
// if we intersect with the edge above or below us
// then we know this polygon is not simple, so don't remove, just fail
- if (removeIndex > 0 && fEdges[removeIndex].intersect(fEdges[removeIndex-1])) {
+ if (removeIndex > 0 && fEdges[removeIndex].intersect(fEdges[removeIndex - 1])) {
return false;
}
- if (removeIndex < fEdges.count()-1) {
+ if (removeIndex < fEdges.count() - 1) {
if (fEdges[removeIndex].intersect(fEdges[removeIndex + 1])) {
return false;
}
@@ -555,7 +558,7 @@ private:
// Then as we pop the vertices from the queue we generate events which indicate that an edge
// should be added or removed from an edge list. If any intersections are detected in the edge
// list, then we know the polygon is self-intersecting and hence not simple.
-static bool is_simple_polygon(const SkPoint* polygon, int polygonSize) {
+bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
SkTDPQueue <Vertex, Vertex::Left> vertexQueue;
EdgeList sweepLine;
@@ -613,7 +616,8 @@ static bool is_simple_polygon(const SkPoint* polygon, int polygonSize) {
return (vertexQueue.count() == 0);
}
-// TODO: assuming a constant offset here -- do we want to support variable offset?
+///////////////////////////////////////////////////////////////////////////////////////////
+
bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
@@ -621,21 +625,11 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
return false;
}
- if (!is_simple_polygon(inputPolygonVerts, inputPolygonSize)) {
- return false;
- }
-
- // compute area and use sign to determine winding
- SkScalar quadArea = 0;
- for (int curr = 0; curr < inputPolygonSize; ++curr) {
- int next = (curr + 1) % inputPolygonSize;
- quadArea += inputPolygonVerts[curr].cross(inputPolygonVerts[next]);
- }
- if (SkScalarNearlyZero(quadArea)) {
+ // get winding direction
+ int winding = get_winding(inputPolygonVerts, inputPolygonSize);
+ if (0 == winding) {
return false;
}
- // 1 == ccw, -1 == cw
- int winding = (quadArea > 0) ? 1 : -1;
// build normals
SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
@@ -667,7 +661,7 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
SkScalar rotSin, rotCos;
int numSteps;
SkVector prevNormal = normal1[currIndex];
- compute_radial_steps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
+ SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
&rotSin, &rotCos, &numSteps);
for (int i = 0; i < numSteps - 1; ++i) {
SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
@@ -792,14 +786,211 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
}
}
- // compute signed area to check winding (it should be same as the original polygon)
- quadArea = 0;
- for (int curr = 0; curr < offsetPolygon->count(); ++curr) {
- int next = (curr + 1) % offsetPolygon->count();
- quadArea += (*offsetPolygon)[curr].cross((*offsetPolygon)[next]);
+ // check winding of offset polygon (it should be same as the original polygon)
+ SkScalar offsetWinding = get_winding(offsetPolygon->begin(), offsetPolygon->count());
+
+ return (winding*offsetWinding > 0 &&
+ SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+struct TriangulationVertex {
+ SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
+
+ enum class VertexType { kConvex, kReflex };
+
+ SkPoint fPosition;
+ VertexType fVertexType;
+ uint16_t fIndex;
+ uint16_t fPrevIndex;
+ uint16_t fNextIndex;
+};
+
+// test to see if point p is in triangle p0p1p2.
+// for now assuming strictly inside -- if on the edge it's outside
+static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
+ const SkPoint& p) {
+ SkVector v0 = p1 - p0;
+ SkVector v1 = p2 - p1;
+ SkScalar n = v0.cross(v1);
+
+ SkVector w0 = p - p0;
+ if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
+ return false;
+ }
+
+ SkVector w1 = p - p1;
+ if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
+ return false;
}
- return (winding*quadArea > 0 &&
- is_simple_polygon(offsetPolygon->begin(), offsetPolygon->count()));
+ SkVector v2 = p0 - p2;
+ SkVector w2 = p - p2;
+ if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
+ return false;
+ }
+
+ return true;
}
+// Data structure to track reflex vertices and check whether any are inside a given triangle
+class ReflexHash {
+public:
+ void add(TriangulationVertex* v) {
+ fReflexList.addToTail(v);
+ }
+
+ void remove(TriangulationVertex* v) {
+ fReflexList.remove(v);
+ }
+
+ bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
+ for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
+ reflexIter != fReflexList.end(); ++reflexIter) {
+ TriangulationVertex* reflexVertex = *reflexIter;
+ if (point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+private:
+ // TODO: switch to an actual spatial hash
+ SkTInternalLList<TriangulationVertex> fReflexList;
+};
+
+// Check to see if a reflex vertex has become a convex vertex after clipping an ear
+static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
+ int winding, ReflexHash* reflexHash,
+ SkTInternalLList<TriangulationVertex>* convexList) {
+ if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
+ SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
+ SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
+ if (winding*v0.cross(v1) > SK_ScalarNearlyZero) {
+ p->fVertexType = TriangulationVertex::VertexType::kConvex;
+ reflexHash->remove(p);
+ p->fPrev = p->fNext = nullptr;
+ convexList->addToTail(p);
+ }
+ }
+}
+
+bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
+ SkTDArray<uint16_t>* triangleIndices) {
+ if (polygonSize < 3) {
+ return false;
+ }
+ // need to be able to represent all the vertices in the 16-bit indices
+ if (polygonSize >= (1 << 16)) {
+ return false;
+ }
+
+ // get winding direction
+ // TODO: we do this for all the polygon routines -- might be better to have the client
+ // compute it and pass it in
+ int winding = get_winding(polygonVerts, polygonSize);
+ if (0 == winding) {
+ return false;
+ }
+
+ // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
+ // TODO: possibly sort the convexList in some way to get better triangles
+ SkTInternalLList<TriangulationVertex> convexList;
+ ReflexHash reflexHash;
+ SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
+ int prevIndex = polygonSize - 1;
+ int currIndex = 0;
+ int nextIndex = 1;
+ SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
+ SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
+ for (int i = 0; i < polygonSize; ++i) {
+ SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
+ triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
+ triangulationVertices[currIndex].fIndex = currIndex;
+ triangulationVertices[currIndex].fPrevIndex = prevIndex;
+ triangulationVertices[currIndex].fNextIndex = nextIndex;
+ if (winding*v0.cross(v1) > SK_ScalarNearlyZero) {
+ triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
+ convexList.addToTail(&triangulationVertices[currIndex]);
+ } else {
+ // We treat near collinear vertices as reflex
+ triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
+ reflexHash.add(&triangulationVertices[currIndex]);
+ }
+
+ prevIndex = currIndex;
+ currIndex = nextIndex;
+ nextIndex = (currIndex + 1) % polygonSize;
+ v0 = v1;
+ v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
+ }
+
+ // The general concept: We are trying to find three neighboring vertices where
+ // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
+ // that ear off, and then repeat on the new polygon. Once we get down to three vertices
+ // we have triangulated the entire polygon.
+ // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
+ // noting that only convex vertices can be potential ears, and we only need to check whether
+ // any reflex vertices lie inside the ear.
+ triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
+ int vertexCount = polygonSize;
+ while (vertexCount > 3) {
+ bool success = false;
+ TriangulationVertex* earVertex = nullptr;
+ TriangulationVertex* p0 = nullptr;
+ TriangulationVertex* p2 = nullptr;
+ // find a convex vertex to clip
+ for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
+ convexIter != convexList.end(); ++convexIter) {
+ earVertex = *convexIter;
+ SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
+
+ p0 = &triangulationVertices[earVertex->fPrevIndex];
+ p2 = &triangulationVertices[earVertex->fNextIndex];
+
+ // see if any reflex vertices are inside the ear
+ bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
+ p2->fPosition);
+ if (failed) {
+ continue;
+ }
+
+ // found one we can clip
+ success = true;
+ break;
+ }
+ // If we can't find any ears to clip, this probably isn't a simple polygon
+ if (!success) {
+ return false;
+ }
+
+ // add indices
+ auto indices = triangleIndices->append(3);
+ indices[0] = indexMap[p0->fIndex];
+ indices[1] = indexMap[earVertex->fIndex];
+ indices[2] = indexMap[p2->fIndex];
+
+ // clip the ear
+ convexList.remove(earVertex);
+ --vertexCount;
+
+ // reclassify reflex verts
+ p0->fNextIndex = earVertex->fNextIndex;
+ reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
+
+ p2->fPrevIndex = earVertex->fPrevIndex;
+ reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
+ }
+
+ // output indices
+ for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
+ vertexIter != convexList.end(); ++vertexIter) {
+ TriangulationVertex* vertex = *vertexIter;
+ *triangleIndices->push() = indexMap[vertex->fIndex];
+ }
+
+ return true;
+}
diff --git a/src/utils/SkOffsetPolygon.h b/src/utils/SkPolyUtils.h
index 4c98ac0706..ab5ebb8d40 100755
--- a/src/utils/SkOffsetPolygon.h
+++ b/src/utils/SkPolyUtils.h
@@ -49,6 +49,7 @@ inline bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPoly
/**
* 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.
@@ -66,6 +67,7 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
/**
* Generates a simple polygon (if possible) that is offset a constant distance 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.
@@ -100,4 +102,42 @@ inline bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPol
bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
int side, SkPoint* offset0, SkPoint* offset1);
+/**
+ * Compute the number of points needed for a circular join when offsetting a vertex.
+ * The lengths of offset0 and offset1 don't have to equal r -- only the direction matters.
+ * The segment lengths will be approximately four pixels.
+ *
+ * @param offset0 Starting offset vector direction.
+ * @param offset1 Ending offset vector direction.
+ * @param r Length of offset.
+ * @param rotSin Sine of rotation delta per step.
+ * @param rotCos Cosine of rotation delta per step.
+ * @param n Number of steps to fill out the arc.
+ */
+void SkComputeRadialSteps(const SkVector& offset0, const SkVector& offset1, SkScalar r,
+ SkScalar* rotSin, SkScalar* rotCos, int* n);
+
+/**
+ * Determine whether a polygon is simple (i.e., not self-intersecting) or not.
+ *
+ * @param polygonVerts Array of points representing the vertices of the polygon.
+ * @param polygonSize Number of vertices in the polygon.
+ * @return true if the polygon is simple, false otherwise.
+ */
+ bool SkIsSimplePolygon(const SkPoint* polygonVerts, int polygonSize);
+
+ /**
+ * Compute indices to triangulate the given polygon.
+ * The input polygon must be simple (i.e. it is not self-intersecting)
+ * and have no coincident vertices or collinear edges.
+ *
+ * @param polygonVerts Array of points representing the vertices of the polygon.
+ * @param indexMap Mapping from index in the given array to the final index in the triangulation.
+ * @param polygonSize Number of vertices in the polygon.
+ * @param triangleIndices Indices of the resulting triangulation.
+ * @return true if successful, false otherwise.
+ */
+ bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
+ SkTDArray<uint16_t>* triangleIndices);
+
#endif
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index b454e5f103..3d84e8fdad 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -9,7 +9,7 @@
#include "SkColorData.h"
#include "SkDrawShadowInfo.h"
#include "SkGeometry.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
#include "SkPath.h"
#include "SkPoint3.h"
#include "SkPointPriv.h"
@@ -127,21 +127,6 @@ static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir,
return true;
}
-static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
- SkScalar* rotSin, SkScalar* rotCos, int* n) {
- const SkScalar kRecipPixelsPerArcSegment = 0.125f;
-
- SkScalar rCos = v1.dot(v2);
- SkScalar rSin = v1.cross(v2);
- SkScalar theta = SkScalarATan2(rSin, rCos);
-
- int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
-
- SkScalar dTheta = theta / steps;
- *rotSin = SkScalarSinCos(dTheta, rotCos);
- *n = steps;
-}
-
static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
static constexpr SkScalar kClose = (SK_Scalar1 / 16);
static constexpr SkScalar kCloseSqd = kClose * kClose;
@@ -269,6 +254,9 @@ void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbra
SkTDArray<int>* umbraIndices,
const SkTDArray<SkPoint>& penumbraPolygon,
SkTDArray<int>* penumbraIndices) {
+ // TODO: only create and fill indexMap when fTransparent is true?
+ SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
+
// find minimum indices
int minIndex = 0;
int min = (*penumbraIndices)[0];
@@ -311,6 +299,7 @@ void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbra
*fPositions.push() = umbraPolygon[currUmbra];
*fColors.push() = fUmbraColor;
fPrevUmbraIndex = 1;
+ indexMap[currUmbra] = 1;
int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
@@ -326,6 +315,7 @@ void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbra
*fPositions.push() = umbraPolygon[nextUmbra];
*fColors.push() = fUmbraColor;
int currUmbraIndex = fPositions.count() - 1;
+ indexMap[nextUmbra] = currUmbraIndex;
this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
fPrevUmbraIndex, currUmbraIndex);
@@ -363,6 +353,7 @@ void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbra
*fPositions.push() = umbraPolygon[nextUmbra];
*fColors.push() = fUmbraColor;
int currUmbraIndex = fPositions.count() - 1;
+ indexMap[nextUmbra] = currUmbraIndex;
this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
@@ -381,12 +372,14 @@ void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbra
*fPositions.push() = umbraPolygon[nextUmbra];
*fColors.push() = fUmbraColor;
int currUmbraIndex = fPositions.count() - 1;
+ indexMap[nextUmbra] = currUmbraIndex;
this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
fPrevUmbraIndex, currUmbraIndex);
if (fTransparent) {
- // TODO: fill penumbra
+ SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
+ &fIndices);
}
}
@@ -508,7 +501,7 @@ bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc)
// fill in fan from previous quad
SkScalar rotSin, rotCos;
int numSteps;
- compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
+ SkComputeRadialSteps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
SkVector prevNormal = fPrevOutset;
for (int i = 0; i < numSteps-1; ++i) {
SkVector currNormal;
@@ -801,8 +794,7 @@ bool SkAmbientShadowTessellator::computeConvexShadow() {
}
bool SkAmbientShadowTessellator::computeConcaveShadow() {
- // TODO: remove when we support filling the penumbra
- if (fTransparent) {
+ if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
return false;
}
@@ -1418,8 +1410,7 @@ bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
}
bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
- // TODO: remove when we support filling the penumbra
- if (fTransparent) {
+ if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
return false;
}
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index b4fba5fcc1..6d5b7e16b4 100644
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -542,9 +542,11 @@ static bool validate_rec(const SkDrawShadowRec& rec) {
void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
SkScalar tx, SkScalar ty) {
- SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
- SkMatrix::MakeTrans(tx, ty)));
- this->drawVertices(vertices, mode, paint);
+ if (vertices->vertexCount()) {
+ SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
+ SkMatrix::MakeTrans(tx, ty)));
+ this->drawVertices(vertices, mode, paint);
+ }
};
if (!validate_rec(rec)) {
diff --git a/tests/InsetConvexPolyTest.cpp b/tests/InsetConvexPolyTest.cpp
index 433cf239e6..022060c0f4 100644
--- a/tests/InsetConvexPolyTest.cpp
+++ b/tests/InsetConvexPolyTest.cpp
@@ -5,7 +5,7 @@
* found in the LICENSE file.
*/
#include "Test.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
static bool is_convex(const SkTDArray<SkPoint>& poly) {
if (poly.count() < 3) {
diff --git a/tests/OffsetSimplePolyTest.cpp b/tests/OffsetSimplePolyTest.cpp
index de720b5cf2..547dc9e529 100644
--- a/tests/OffsetSimplePolyTest.cpp
+++ b/tests/OffsetSimplePolyTest.cpp
@@ -5,7 +5,7 @@
* found in the LICENSE file.
*/
#include "Test.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
static bool is_convex(const SkTDArray<SkPoint>& poly) {
if (poly.count() < 3) {
@@ -200,10 +200,7 @@ DEF_TEST(OffsetSimplePoly, reporter) {
*intersectingPoly.push() = SkPoint::Make(-43.30f, -25.0f);
*intersectingPoly.push() = SkPoint::Make(-14.43f, -25.0f);
- result = SkOffsetSimplePolygon(&intersectingPoly[0], intersectingPoly.count(), -100,
- &offsetPoly);
+ // SkOffsetSimplePolygon now assumes that the input is simple, so we'll just check for that
+ result = SkIsSimplePolygon(&intersectingPoly[0], intersectingPoly.count());
REPORTER_ASSERT(reporter, !result);
-
-
-
}