diff options
-rw-r--r-- | samplecode/SampleAndroidShadows.cpp | 12 | ||||
-rw-r--r-- | src/core/SkDrawShadowInfo.h | 2 | ||||
-rwxr-xr-x | src/utils/SkShadowTessellator.cpp | 114 |
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; } |