aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--samplecode/SampleAndroidShadows.cpp12
-rw-r--r--src/core/SkDrawShadowInfo.h2
-rwxr-xr-xsrc/utils/SkShadowTessellator.cpp114
3 files changed, 87 insertions, 41 deletions
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index a5fb6fcb9f..ee833d7939 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -281,6 +281,16 @@ protected:
this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
lightPos, kLightWidth, .5f);
+ // path ops bug
+ SkPath tmpClipPathBug;
+ tmpClipPathBug.addCircle(88.0344925f, 0, 60);
+ Op(fSquareRRectPath, tmpClipPathBug, kIntersect_SkPathOp, &tmpPath);
+
+ canvas->translate(250, 0);
+ zPlaneParams.fZ = SkTMax(1.0f, 32 + fZDelta);
+ this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
+ lightPos, kLightWidth, .5f);
+
// perspective paths
SkPoint pivot = SkPoint::Make(fWideRectPath.getBounds().width()/2,
fWideRectPath.getBounds().height()/2);
@@ -318,7 +328,7 @@ protected:
}
bool onAnimate(const SkAnimTimer& timer) override {
- fAnimTranslate = timer.pingPong(30, 0, 200, -200);
+ fAnimTranslate = timer.pingPong(30, 0, 125, -125);
fAnimAngle = timer.pingPong(15, 0, 0, 20);
if (fDoAlphaAnimation) {
fAnimAlpha = timer.pingPong(5, 0, 1, 0);
diff --git a/src/core/SkDrawShadowInfo.h b/src/core/SkDrawShadowInfo.h
index 47799e5c4a..da63a08104 100644
--- a/src/core/SkDrawShadowInfo.h
+++ b/src/core/SkDrawShadowInfo.h
@@ -47,7 +47,7 @@ inline void GetSpotParams(SkScalar occluderZ, SkScalar lightX, SkScalar lightY,
SkScalar* blurRadius, SkScalar* scale, SkVector* translate) {
SkScalar zRatio = SkTPin(occluderZ / (lightZ - occluderZ), 0.0f, 0.95f);
*blurRadius = lightRadius*zRatio;
- *scale = SkTMax(lightZ / (lightZ - occluderZ), 1.0f);
+ *scale = SkTPin(lightZ / (lightZ - occluderZ), 1.0f, 1.95f);
*translate = SkVector::Make(-zRatio * lightX, -zRatio * lightY);
}
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index 0bf1adaaa4..4990db92e7 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -88,6 +88,7 @@ protected:
bool fSucceeded;
bool fTransparent;
+ bool fIsConvex;
SkColor fUmbraColor;
SkColor fPenumbraColor;
@@ -128,12 +129,27 @@ static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScala
*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;
+
+ SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
+ return distSq < kCloseSqd;
+}
+
+static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
+ SkVector v0 = p1 - p0;
+ SkVector v1 = p2 - p0;
+ return v0.cross(v1);
+}
+
SkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, bool transparent)
: fZPlaneParams(zPlaneParams)
, fZOffset(0)
, fFirstVertexIndex(-1)
, fSucceeded(false)
, fTransparent(transparent)
+ , fIsConvex(true)
, fDirection(1)
, fPrevUmbraIndex(-1) {
fInitPoints.setReserve(3);
@@ -398,9 +414,6 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
: INHERITED(zPlaneParams, transparent)
, fSplitFirstEdge(false)
, fSplitPreviousEdge(false) {
- // TODO: support some concave paths
- SkASSERT(path.isConvex());
-
// Set base colors
SkScalar umbraAlpha = SkScalarInvert(SkDrawShadowMetrics::AmbientRecipAlpha(heightFunc(0, 0)));
// umbraColor is the interior value, penumbraColor the exterior value.
@@ -454,12 +467,22 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
case SkPath::kDone_Verb:
break;
}
+ // TODO: add support for concave paths
+ if (!fIsConvex) {
+ return;
+ }
}
if (!this->indexCount()) {
return;
}
+ // final convexity check
+ // TODO: add support for concave paths
+ if (fDirection*perp_dot(fInitPoints[1], fInitPoints[2], fFirstPoint) > 0) {
+ return;
+ }
+
// Finish up
SkVector normal;
if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
@@ -560,6 +583,11 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
}
void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
+ // skip duplicate points
+ if (!fInitPoints.isEmpty() && duplicate_pt(p, fInitPoints[fInitPoints.count() - 1])) {
+ return;
+ }
+
if (fInitPoints.count() < 2) {
*fInitPoints.push() = p;
return;
@@ -567,9 +595,7 @@ void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
if (fInitPoints.count() == 2) {
// determine if cw or ccw
- SkVector v0 = fInitPoints[1] - fInitPoints[0];
- SkVector v1 = p - fInitPoints[0];
- SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
+ SkScalar perpDot = perp_dot(fInitPoints[0], fInitPoints[1], p);
if (SkScalarNearlyZero(perpDot)) {
// nearly parallel, just treat as straight line and continue
fInitPoints[1] = p;
@@ -614,6 +640,15 @@ void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
// to ensure we skip this block next time
*fInitPoints.push() = p;
+ } else {
+ // reuse fInitPoints to track last three points
+ fInitPoints[0] = fInitPoints[1];
+ fInitPoints[1] = fInitPoints[2];
+ fInitPoints[2] = p;
+ // convexity check
+ if (fDirection*perp_dot(fInitPoints[0], fInitPoints[1], p) > 0) {
+ fIsConvex = false;
+ }
}
SkVector normal;
@@ -740,6 +775,7 @@ private:
void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
bool addInnerPoint(const SkPoint& pathPoint);
void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
+ void addToClip(const SkVector& nextPoint);
SkScalar offset(SkScalar z) {
float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f);
@@ -835,8 +871,11 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
return;
}
+ // compute vectors for clip tests
+ this->computeClipVectorsAndTestCentroid();
+
// check to see if umbra collapses
- if (path.isConvex()) {
+ if (fIsConvex) {
SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
fPathPolygon[0],
fPathPolygon[1]);
@@ -867,9 +906,6 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
}
}
- // compute vectors for clip tests
- this->computeClipVectorsAndTestCentroid();
-
if (ctm.hasPerspective()) {
for (int i = 0; i < fPositions.count(); ++i) {
SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
@@ -878,14 +914,16 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
}
}
- if (path.isConvex()) {
+ if (fIsConvex) {
if (!this->computeConvexShadow(radius)) {
return;
}
} else {
- if (!this->computeConcaveShadow(radius)) {
- return;
- }
+ // For now
+ return;
+ //if (!this->computeConcaveShadow(radius)) {
+ // return;
+ //}
}
if (ctm.hasPerspective()) {
@@ -919,6 +957,12 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
fSucceeded = true;
}
+void SkSpotShadowTessellator::addToClip(const SkPoint& point) {
+ if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count()-1])) {
+ *fClipPolygon.push() = point;
+ }
+}
+
void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
const SkMatrix& shadowTransform) {
@@ -949,7 +993,7 @@ void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con
switch (verb) {
case SkPath::kLine_Verb:
ctm.mapPoints(&pts[1], 1);
- *fClipPolygon.push() = pts[1];
+ this->addToClip(pts[1]);
this->INHERITED::handleLine(shadowTransform, &pts[1]);
break;
case SkPath::kQuad_Verb:
@@ -957,8 +1001,8 @@ void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con
// point at t = 1/2
curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
- *fClipPolygon.push() = curvePoint;
- *fClipPolygon.push() = pts[2];
+ this->addToClip(curvePoint);
+ this->addToClip(pts[2]);
this->handleQuad(shadowTransform, pts);
break;
case SkPath::kConic_Verb:
@@ -968,8 +1012,8 @@ void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con
curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
- *fClipPolygon.push() = curvePoint;
- *fClipPolygon.push() = pts[2];
+ this->addToClip(curvePoint);
+ this->addToClip(pts[2]);
this->handleConic(shadowTransform, pts, w);
break;
case SkPath::kCubic_Verb:
@@ -977,12 +1021,12 @@ void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con
// point at t = 5/16
curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
- *fClipPolygon.push() = curvePoint;
+ this->addToClip(curvePoint);
// point at t = 11/16
curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
- *fClipPolygon.push() = curvePoint;
- *fClipPolygon.push() = pts[3];
+ this->addToClip(curvePoint);
+ this->addToClip(pts[3]);
this->handleCubic(shadowTransform, pts);
break;
case SkPath::kMove_Verb:
@@ -1013,17 +1057,23 @@ void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
// init clip vectors
SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
+ SkVector v1 = fClipPolygon[2] - fClipPolygon[0];
+ SkScalar winding = v0.cross(v1);
*fClipVectors.push() = v0;
// init centroid check
bool hiddenCentroid = true;
- SkVector v1 = fCentroid - fClipPolygon[0];
+ v1 = fCentroid - fClipPolygon[0];
SkScalar initCross = v0.cross(v1);
for (int p = 1; p < fClipPolygon.count(); ++p) {
// add to clip vectors
v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
*fClipVectors.push() = v0;
+ v1 = fClipPolygon[(p + 2) % fClipPolygon.count()] - fClipPolygon[p];
+ if (winding*v0.cross(v1) < 0) {
+ fIsConvex = false;
+ }
// Determine if transformed centroid is inside clipPolygon.
v1 = fCentroid - fClipPolygon[p];
if (initCross*v0.cross(v1) <= 0) {
@@ -1343,20 +1393,6 @@ void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
}
}
-static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
- static constexpr SkScalar kClose = (SK_Scalar1 / 16);
- static constexpr SkScalar kCloseSqd = kClose*kClose;
-
- SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1);
- return distSq < kCloseSqd;
-}
-
-static SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
- SkVector v0 = p1 - p0;
- SkVector v1 = p2 - p0;
- return v0.cross(v1);
-}
-
static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
return (SkScalarNearlyZero(perp_dot(p0, p1, p2)));
}
@@ -1534,7 +1570,7 @@ void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector&
sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
const SkPoint3& zPlane, bool transparent) {
- if (!path.isFinite() || !path.isConvex() || !ctm.isFinite() || !zPlane.isFinite()) {
+ if (!path.isFinite() || !ctm.isFinite() || !zPlane.isFinite()) {
return nullptr;
}
SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent);
@@ -1544,7 +1580,7 @@ sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkM
sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm,
const SkPoint3& zPlane, const SkPoint3& lightPos,
SkScalar lightRadius, bool transparent) {
- if (!path.isFinite() || !path.isConvex() || !ctm.isFinite() || !zPlane.isFinite() ||
+ if (!path.isFinite() || !ctm.isFinite() || !zPlane.isFinite() ||
!lightPos.isFinite() || !SkScalarIsFinite(lightRadius) || !(lightRadius > 0)) {
return nullptr;
}