Spot shadow cleanup and optimization

Fixes clip polygon and centroid calculation.
Clips interior umbra region against original path.

BUG=skia:6119

Change-Id: I37a740ae004c38b75405e05158d92cf9fd954eda
Reviewed-on: https://skia-review.googlesource.com/7823
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Jim Van Verth 2017-02-02 13:06:31 -05:00 committed by Skia Commit-Bot
parent cae3a4c8ab
commit 47784c18ad

View File

@ -6,6 +6,7 @@
*/ */
#include "SkShadowTessellator.h" #include "SkShadowTessellator.h"
#include "SkColorPriv.h"
#include "SkGeometry.h" #include "SkGeometry.h"
#include "SkPath.h" #include "SkPath.h"
@ -59,7 +60,7 @@ private:
SkTDArray<SkColor> fColors; SkTDArray<SkColor> fColors;
SkTDArray<uint16_t> fIndices; SkTDArray<uint16_t> fIndices;
int fPrevInnerIndex; int fPrevUmbraIndex;
SkVector fPrevNormal; SkVector fPrevNormal;
int fFirstVertex; int fFirstVertex;
SkVector fFirstNormal; SkVector fFirstNormal;
@ -110,7 +111,7 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
, fUmbraColor(umbraColor) , fUmbraColor(umbraColor)
, fPenumbraColor(penumbraColor) , fPenumbraColor(penumbraColor)
, fTransparent(transparent) , fTransparent(transparent)
, fPrevInnerIndex(-1) { , fPrevUmbraIndex(-1) {
// Outer ring: 3*numPts // Outer ring: 3*numPts
// Middle ring: numPts // Middle ring: numPts
@ -154,14 +155,14 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
} }
SkVector normal; SkVector normal;
if (compute_normal(fPositions[fPrevInnerIndex], fPositions[fFirstVertex], fRadius, fDirection, if (compute_normal(fPositions[fPrevUmbraIndex], fPositions[fFirstVertex], fRadius, fDirection,
&normal)) { &normal)) {
this->addArc(normal); this->addArc(normal);
// close out previous arc // close out previous arc
*fPositions.push() = fPositions[fPrevInnerIndex] + normal; *fPositions.push() = fPositions[fPrevUmbraIndex] + normal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
@ -169,7 +170,7 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
*fPositions.push() = fPositions[fFirstVertex] + normal; *fPositions.push() = fPositions[fFirstVertex] + normal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fFirstVertex; *fIndices.push() = fFirstVertex;
@ -183,13 +184,13 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
fPositions[0] *= SkScalarFastInvert(fCentroidCount); fPositions[0] *= SkScalarFastInvert(fCentroidCount);
*fIndices.push() = 0; *fIndices.push() = 0;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fFirstVertex; *fIndices.push() = fFirstVertex;
} }
// final fan // final fan
if (fPositions.count() >= 3) { if (fPositions.count() >= 3) {
fPrevInnerIndex = fFirstVertex; fPrevUmbraIndex = fFirstVertex;
fPrevNormal = normal; fPrevNormal = normal;
this->addArc(fFirstNormal); this->addArc(fFirstNormal);
@ -234,7 +235,7 @@ void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
fFirstVertex = fPositions.count(); fFirstVertex = fPositions.count();
fPrevNormal = fFirstNormal; fPrevNormal = fFirstNormal;
fPrevInnerIndex = fFirstVertex; fPrevUmbraIndex = fFirstVertex;
*fPositions.push() = fInitPoints[0]; *fPositions.push() = fInitPoints[0];
*fColors.push() = fUmbraColor; *fColors.push() = fUmbraColor;
@ -251,7 +252,7 @@ void SkAmbientShadowTessellator::handleLine(const SkPoint& p) {
} }
SkVector normal; SkVector normal;
if (compute_normal(fPositions[fPrevInnerIndex], p, fRadius, fDirection, &normal)) { if (compute_normal(fPositions[fPrevUmbraIndex], p, fRadius, fDirection, &normal)) {
this->addArc(normal); this->addArc(normal);
this->finishArcAndAddEdge(p, normal); this->finishArcAndAddEdge(p, normal);
} }
@ -313,9 +314,9 @@ void SkAmbientShadowTessellator::addArc(const SkVector& nextNormal) {
SkVector nextNormal; SkVector nextNormal;
nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin; nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin; nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
*fPositions.push() = fPositions[fPrevInnerIndex] + nextNormal; *fPositions.push() = fPositions[fPrevUmbraIndex] + nextNormal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
@ -326,9 +327,9 @@ void SkAmbientShadowTessellator::addArc(const SkVector& nextNormal) {
void SkAmbientShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint, void SkAmbientShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
const SkVector& nextNormal) { const SkVector& nextNormal) {
// close out previous arc // close out previous arc
*fPositions.push() = fPositions[fPrevInnerIndex] + nextNormal; *fPositions.push() = fPositions[fPrevUmbraIndex] + nextNormal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
@ -342,7 +343,7 @@ void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVecto
*fPositions.push() = nextPoint + nextNormal; *fPositions.push() = nextPoint + nextNormal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 3; *fIndices.push() = fPositions.count() - 3;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
@ -356,11 +357,11 @@ void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVecto
++fCentroidCount; ++fCentroidCount;
*fIndices.push() = 0; *fIndices.push() = 0;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
} }
fPrevInnerIndex = fPositions.count() - 2; fPrevUmbraIndex = fPositions.count() - 2;
fPrevNormal = nextNormal; fPrevNormal = nextNormal;
} }
@ -389,6 +390,9 @@ public:
private: private:
void computeClipBounds(const SkPath& path); void computeClipBounds(const SkPath& path);
void checkUmbraAndTransformCentroid(SkScalar scale, const SkVector& xlate,
bool useDistanceToPoint);
bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
void handleLine(const SkPoint& p); void handleLine(const SkPoint& p);
void handleLine(SkScalar scale, const SkVector& xlate, SkPoint p); void handleLine(SkScalar scale, const SkVector& xlate, SkPoint p);
@ -401,7 +405,7 @@ private:
void handleConic(SkScalar scale, const SkVector& xlate, SkPoint pts[3], SkScalar w); void handleConic(SkScalar scale, const SkVector& xlate, SkPoint pts[3], SkScalar w);
void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count); void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
void addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor, SkScalar radiusSqd); void addInnerPoint(const SkPoint& pathPoint);
void addArc(const SkVector& nextNormal); void addArc(const SkVector& nextNormal);
void finishArcAndAddEdge(const SkVector& nextPoint, const SkVector& nextNormal); void finishArcAndAddEdge(const SkVector& nextPoint, const SkVector& nextNormal);
void addEdge(const SkVector& nextPoint, const SkVector& nextNormal); void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
@ -409,12 +413,14 @@ private:
SkScalar fRadius; SkScalar fRadius;
SkColor fUmbraColor; SkColor fUmbraColor;
SkColor fPenumbraColor; SkColor fPenumbraColor;
bool fTransparent;
bool fValidUmbra;
SkTDArray<SkPoint> fPositions; SkTDArray<SkPoint> fPositions;
SkTDArray<SkColor> fColors; SkTDArray<SkColor> fColors;
SkTDArray<uint16_t> fIndices; SkTDArray<uint16_t> fIndices;
int fPrevInnerIndex; int fPrevUmbraIndex;
SkPoint fPrevPoint; SkPoint fPrevPoint;
SkVector fPrevNormal; SkVector fPrevNormal;
int fFirstVertex; int fFirstVertex;
@ -424,6 +430,10 @@ private:
SkPoint fCentroid; SkPoint fCentroid;
SkTDArray<SkPoint> fClipPolygon; SkTDArray<SkPoint> fClipPolygon;
SkTDArray<SkVector> fClipVectors;
int fCurrPolyPoint;
bool fPrevUmbraOutside;
bool fFirstUmbraOutside;
// first three points // first three points
SkTDArray<SkPoint> fInitPoints; SkTDArray<SkPoint> fInitPoints;
@ -431,42 +441,55 @@ private:
SkTDArray<SkPoint> fPointBuffer; SkTDArray<SkPoint> fPointBuffer;
}; };
SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path,
SkScalar scale, const SkVector& translate, SkScalar scale, const SkVector& translate,
SkScalar radius, SkScalar radius,
SkColor umbraColor, SkColor penumbraColor, SkColor umbraColor, SkColor penumbraColor,
bool /* transparent */) bool transparent)
: fRadius(radius) : fRadius(radius)
, fUmbraColor(umbraColor) , fUmbraColor(umbraColor)
, fPenumbraColor(penumbraColor) , fPenumbraColor(penumbraColor)
, fPrevInnerIndex(-1) { , fTransparent(transparent)
, fPrevUmbraIndex(-1) {
// TODO: calculate these better // TODO: calculate these better
// Outer ring: 3*numPts // Penumbra ring: 3*numPts
// Umbra ring: numPts
// Inner ring: numPts // Inner ring: numPts
fPositions.setReserve(4 * path.countPoints()); fPositions.setReserve(5 * path.countPoints());
fColors.setReserve(4 * path.countPoints()); fColors.setReserve(5 * path.countPoints());
// Outer ring: 12*numPts // Penumbra ring: 12*numPts
// Inner ring: 0 // Umbra ring: 3*numPts
fIndices.setReserve(12 * path.countPoints()); fIndices.setReserve(15 * path.countPoints());
fInitPoints.setReserve(3); fInitPoints.setReserve(3);
fClipPolygon.setReserve(path.countPoints()); fClipPolygon.setReserve(path.countPoints());
// compute rough clip bounds for umbra, plus centroid
this->computeClipBounds(path); this->computeClipBounds(path);
if (fClipPolygon.count() < 3) {
return;
}
// We are going to apply 'scale' and 'xlate' (in that order) to each computed path point. We // We are going to apply 'scale' and 'xlate' (in that order) to each computed path point. We
// want the effect to be to scale the points relative to the path centroid and then translate // want the effect to be to scale the points relative to the path centroid and then translate
// them by the 'translate' param we were passed. // them by the 'translate' param we were passed.
SkVector xlate = fCentroid * (1.f - scale) + translate; SkVector xlate = fCentroid * (1.f - scale) + translate;
// Also translate the centroid by the global translate.
fCentroid += translate; // check to see if we have a valid umbra at all
bool usePointCheck = path.isRRect(nullptr) || path.isRect(nullptr) || path.isOval(nullptr);
this->checkUmbraAndTransformCentroid(scale, translate, usePointCheck);
fPrevUmbraOutside = false;
// walk around the path, tessellate and generate inner and outer rings // walk around the path, tessellate and generate inner and outer rings
SkPath::Iter iter(path, true); SkPath::Iter iter(path, true);
SkPoint pts[4]; SkPoint pts[4];
SkPath::Verb verb; SkPath::Verb verb;
*fPositions.push() = fCentroid; if (fTransparent) {
*fColors.push() = fUmbraColor; *fPositions.push() = fCentroid;
*fColors.push() = fUmbraColor;
}
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) { switch (verb) {
case SkPath::kLine_Verb: case SkPath::kLine_Verb:
@ -496,44 +519,69 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path,
// close out previous arc // close out previous arc
*fPositions.push() = fPrevPoint + normal; *fPositions.push() = fPrevPoint + normal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
// add to center fan
if (fTransparent) {
*fIndices.push() = 0;
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fFirstVertex;
// or to clip ring
} else {
if (fFirstUmbraOutside) {
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fFirstVertex;
*fIndices.push() = fFirstVertex + 1;
if (fPrevUmbraOutside) {
// fill out quad
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fFirstVertex + 1;
*fIndices.push() = fPrevUmbraIndex + 1;
}
} else if (fPrevUmbraOutside) {
// add tri
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fFirstVertex;
*fIndices.push() = fPrevUmbraIndex + 1;
}
}
// add final edge // add final edge
*fPositions.push() = fFirstPoint + normal; *fPositions.push() = fFirstPoint + normal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fFirstVertex; *fIndices.push() = fFirstVertex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
*fIndices.push() = fFirstVertex; *fIndices.push() = fFirstVertex;
// add to center fan
*fIndices.push() = 0;
*fIndices.push() = fPrevInnerIndex;
*fIndices.push() = fFirstVertex;
} }
// final fan // final fan
if (fPositions.count() >= 3) { if (fPositions.count() >= 3) {
fPrevInnerIndex = fFirstVertex; fPrevUmbraIndex = fFirstVertex;
fPrevPoint = fFirstPoint; fPrevPoint = fFirstPoint;
fPrevNormal = normal; fPrevNormal = normal;
this->addArc(fFirstNormal); this->addArc(fFirstNormal);
*fIndices.push() = fFirstVertex; *fIndices.push() = fFirstVertex;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
*fIndices.push() = fFirstVertex + 1; if (fFirstUmbraOutside) {
*fIndices.push() = fFirstVertex + 2;
} else {
*fIndices.push() = fFirstVertex + 1;
}
} }
} }
void SkSpotShadowTessellator::computeClipBounds(const SkPath& path) { void SkSpotShadowTessellator::computeClipBounds(const SkPath& path) {
// walk around the path and compute clip polygon // walk around the path and compute clip polygon
// if original path is transparent, will accumulate sum of points for centroid // if original path is transparent, will accumulate sum of points for centroid
// for Bezier curves, we compute additional interior points on curve
SkPath::Iter iter(path, true); SkPath::Iter iter(path, true);
SkPoint pts[4]; SkPoint pts[4];
SkPath::Verb verb; SkPath::Verb verb;
@ -542,6 +590,14 @@ void SkSpotShadowTessellator::computeClipBounds(const SkPath& path) {
int centroidCount = 0; int centroidCount = 0;
fClipPolygon.reset(); fClipPolygon.reset();
// coefficients to compute cubic Bezier at t = 5/16
const SkScalar kA = 0.32495117187f;
const SkScalar kB = 0.44311523437f;
const SkScalar kC = 0.20141601562f;
const SkScalar kD = 0.03051757812f;
SkPoint curvePoint;
SkScalar w;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) { switch (verb) {
case SkPath::kMove_Verb: case SkPath::kMove_Verb:
@ -552,23 +608,41 @@ void SkSpotShadowTessellator::computeClipBounds(const SkPath& path) {
*fClipPolygon.push() = pts[1]; *fClipPolygon.push() = pts[1];
break; break;
case SkPath::kQuad_Verb: case SkPath::kQuad_Verb:
fCentroid += pts[1]; // 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;
fCentroid += curvePoint;
*fClipPolygon.push() = pts[2];
fCentroid += pts[2]; fCentroid += pts[2];
centroidCount += 2; centroidCount += 2;
*fClipPolygon.push() = pts[2];
break; break;
case SkPath::kConic_Verb: case SkPath::kConic_Verb:
fCentroid += pts[1]; // point at t = 1/2
w = iter.conicWeight();
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;
fCentroid += curvePoint;
*fClipPolygon.push() = pts[2];
fCentroid += pts[2]; fCentroid += pts[2];
centroidCount += 2; centroidCount += 2;
*fClipPolygon.push() = pts[2];
break; break;
case SkPath::kCubic_Verb: case SkPath::kCubic_Verb:
fCentroid += pts[1]; // point at t = 5/16
fCentroid += pts[2]; 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;
fCentroid += 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;
fCentroid += curvePoint;
*fClipPolygon.push() = pts[3];
fCentroid += pts[3]; fCentroid += pts[3];
centroidCount += 3; centroidCount += 3;
*fClipPolygon.push() = pts[3];
break; break;
case SkPath::kClose_Verb: case SkPath::kClose_Verb:
break; break;
@ -578,6 +652,113 @@ void SkSpotShadowTessellator::computeClipBounds(const SkPath& path) {
} }
fCentroid *= SkScalarInvert(centroidCount); fCentroid *= SkScalarInvert(centroidCount);
fCurrPolyPoint = fClipPolygon.count() - 1;
}
void SkSpotShadowTessellator::checkUmbraAndTransformCentroid(SkScalar scale,
const SkVector& xlate,
bool useDistanceToPoint) {
SkASSERT(fClipPolygon.count() >= 3);
SkPoint transformedCentroid = fCentroid;
transformedCentroid += xlate;
SkScalar localRadius = fRadius / scale;
localRadius *= localRadius;
// init umbra check
SkVector w = fCentroid - fClipPolygon[0];
SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
*fClipVectors.push() = v0;
bool validUmbra;
SkScalar minDistance;
// check distance against line segment
if (useDistanceToPoint) {
minDistance = w.lengthSqd();
} else {
SkScalar vSq = v0.dot(v0);
SkScalar wDotV = w.dot(v0);
minDistance = w.dot(w) - wDotV*wDotV/vSq;
}
validUmbra = (minDistance >= localRadius);
// init centroid check
bool hiddenCentroid = true;
SkVector v1 = transformedCentroid - fClipPolygon[0];
SkScalar initCross = v0.cross(v1);
for (int p = 1; p < fClipPolygon.count(); ++p) {
// Determine whether we have a real umbra by insetting clipPolygon by radius/scale
// and see if it extends past centroid.
// TODO: adjust this later for more accurate umbra calcs
w = fCentroid - fClipPolygon[p];
v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
*fClipVectors.push() = v0;
// check distance against line segment
SkScalar distance;
if (useDistanceToPoint) {
distance = w.lengthSqd();
} else {
SkScalar vSq = v0.dot(v0);
SkScalar wDotV = w.dot(v0);
distance = w.dot(w) - wDotV*wDotV/vSq;
}
if (distance < localRadius) {
validUmbra = false;
}
if (distance < minDistance) {
minDistance = distance;
}
// Determine if transformed centroid is inside clipPolygon.
v1 = transformedCentroid - fClipPolygon[p];
if (initCross*v0.cross(v1) <= 0) {
hiddenCentroid = false;
}
}
SkASSERT(fClipVectors.count() == fClipPolygon.count());
if (!validUmbra) {
SkScalar ratio = 256 * SkScalarSqrt(minDistance / localRadius);
// they aren't PMColors, but the interpolation algorithm is the same
fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
}
fTransparent = fTransparent || !hiddenCentroid || !validUmbra;
fValidUmbra = validUmbra;
fCentroid = transformedCentroid;
}
bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
SkPoint* clipPoint) {
SkVector segmentVector = centroid - umbraPoint;
int startPolyPoint = fCurrPolyPoint;
do {
SkVector dp = umbraPoint - fClipPolygon[fCurrPolyPoint];
SkScalar denom = fClipVectors[fCurrPolyPoint].cross(segmentVector);
SkScalar t_num = dp.cross(segmentVector);
// if line segments are nearly parallel
if (SkScalarNearlyZero(denom)) {
// and collinear
if (SkScalarNearlyZero(t_num)) {
return false;
}
// otherwise are separate, will try the next poly segment
// else if crossing lies within poly segment
} else if (t_num >= 0 && t_num <= denom) {
SkScalar s_num = dp.cross(fClipVectors[fCurrPolyPoint]);
// if umbra point is inside the clip polygon
if (s_num < 0) {
return false;
} else {
segmentVector *= s_num/denom;
*clipPoint = umbraPoint + segmentVector;
return true;
}
}
fCurrPolyPoint = (fCurrPolyPoint + 1) % fClipPolygon.count();
} while (fCurrPolyPoint != startPolyPoint);
return false;
} }
void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate, void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
@ -621,9 +802,21 @@ void SkSpotShadowTessellator::handleLine(const SkPoint& p) {
fFirstVertex = fPositions.count(); fFirstVertex = fPositions.count();
fPrevNormal = fFirstNormal; fPrevNormal = fFirstNormal;
fPrevPoint = fFirstPoint; fPrevPoint = fFirstPoint;
fPrevInnerIndex = fFirstVertex; fPrevUmbraIndex = fFirstVertex;
this->addInnerPoint(fFirstPoint);
if (!fTransparent) {
SkPoint clipPoint;
bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertex], fCentroid, &clipPoint);
if (isOutside) {
*fPositions.push() = clipPoint;
*fColors.push() = fUmbraColor;
}
fPrevUmbraOutside = isOutside;
fFirstUmbraOutside = isOutside;
}
this->addInnerPoint(fFirstPoint, fUmbraColor, fRadius);
SkPoint newPoint = fFirstPoint + fFirstNormal; SkPoint newPoint = fFirstPoint + fFirstNormal;
*fPositions.push() = newPoint; *fPositions.push() = newPoint;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
@ -699,21 +892,21 @@ void SkSpotShadowTessellator::handleConic(SkScalar scale, const SkVector& xlate,
} }
} }
void SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor, void SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
SkScalar radius) {
SkVector v = fCentroid - pathPoint; SkVector v = fCentroid - pathPoint;
SkScalar distance = v.length(); SkScalar distance = v.length();
if (distance < radius) { SkScalar t;
*fPositions.push() = fCentroid; if (fValidUmbra) {
*fColors.push() = umbraColor; // fix this SkASSERT(distance >= fRadius);
// TODO: deal with fanning from centroid t = fRadius / distance;
} else { } else {
SkScalar t = radius / distance; t = 0.95f;
v *= t;
SkPoint innerPoint = pathPoint + v;
*fPositions.push() = innerPoint;
*fColors.push() = umbraColor;
} }
v *= t;
SkPoint umbraPoint = pathPoint + v;
*fPositions.push() = umbraPoint;
*fColors.push() = fUmbraColor;
fPrevPoint = pathPoint; fPrevPoint = pathPoint;
} }
@ -729,7 +922,7 @@ void SkSpotShadowTessellator::addArc(const SkVector& nextNormal) {
nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin; nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
*fPositions.push() = fPrevPoint + nextNormal; *fPositions.push() = fPrevPoint + nextNormal;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
@ -743,7 +936,7 @@ void SkSpotShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
SkPoint newPoint = fPrevPoint + nextNormal; SkPoint newPoint = fPrevPoint + nextNormal;
*fPositions.push() = newPoint; *fPositions.push() = newPoint;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = fPositions.count() - 2;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
@ -751,26 +944,58 @@ void SkSpotShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
} }
void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) { void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
// add next quad // add next umbra point
this->addInnerPoint(nextPoint, fUmbraColor, fRadius); this->addInnerPoint(nextPoint);
int prevPenumbraIndex = fPositions.count() - 2;
int currUmbraIndex = fPositions.count() - 1;
// add to center fan if transparent or centroid showing
if (fTransparent) {
*fIndices.push() = 0;
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = currUmbraIndex;
// otherwise add to clip ring
} else {
if (!fTransparent) {
SkPoint clipPoint;
bool isOutside = clipUmbraPoint(fPositions[currUmbraIndex], fCentroid, &clipPoint);
if (isOutside) {
*fPositions.push() = clipPoint;
*fColors.push() = fUmbraColor;
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = currUmbraIndex;
*fIndices.push() = currUmbraIndex + 1;
if (fPrevUmbraOutside) {
// fill out quad
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = currUmbraIndex + 1;
*fIndices.push() = fPrevUmbraIndex + 1;
}
} else if (fPrevUmbraOutside) {
// add tri
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = currUmbraIndex;
*fIndices.push() = fPrevUmbraIndex + 1;
}
fPrevUmbraOutside = isOutside;
}
}
// add next penumbra point and quad
SkPoint newPoint = nextPoint + nextNormal; SkPoint newPoint = nextPoint + nextNormal;
*fPositions.push() = newPoint; *fPositions.push() = newPoint;
*fColors.push() = fPenumbraColor; *fColors.push() = fPenumbraColor;
*fIndices.push() = fPrevInnerIndex; *fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = fPositions.count() - 3; *fIndices.push() = prevPenumbraIndex;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = currUmbraIndex;
*fIndices.push() = fPositions.count() - 3; *fIndices.push() = prevPenumbraIndex;
*fIndices.push() = fPositions.count() - 1; *fIndices.push() = fPositions.count() - 1;
*fIndices.push() = fPositions.count() - 2; *fIndices.push() = currUmbraIndex;
// add to center fan fPrevUmbraIndex = currUmbraIndex;
*fIndices.push() = 0;
*fIndices.push() = fPrevInnerIndex;
*fIndices.push() = fPositions.count() - 2;
fPrevInnerIndex = fPositions.count() - 2;
fPrevNormal = nextNormal; fPrevNormal = nextNormal;
} }