aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--samplecode/SampleAndroidShadows.cpp55
-rwxr-xr-xsrc/utils/SkOffsetPolygon.cpp35
-rwxr-xr-xsrc/utils/SkOffsetPolygon.h4
-rwxr-xr-xsrc/utils/SkShadowTessellator.cpp548
-rw-r--r--tests/OffsetSimplePolyTest.cpp1
5 files changed, 422 insertions, 221 deletions
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index 8f076f4c66..5d4cd26956 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -28,9 +28,13 @@ class ShadowsView : public SampleView {
SkPath fCirclePath;
SkPath fFunkyRRPath;
SkPath fCubicPath;
+ SkPath fStarPath;
SkPath fSquareRRectPath;
SkPath fWideRectPath;
SkPath fWideOvalPath;
+ SkPath fNotchPath;
+ SkPath fTabPath;
+
SkPoint3 fLightPos;
SkScalar fZDelta;
SkScalar fAnimTranslate;
@@ -68,11 +72,38 @@ protected:
fCubicPath.cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
20 * SK_Scalar1, 100 * SK_Scalar1,
0 * SK_Scalar1, 0 * SK_Scalar1);
+ fStarPath.moveTo(0.0f, -50.0f);
+ fStarPath.lineTo(14.43f, -25.0f);
+ fStarPath.lineTo(43.30f, -25.0f);
+ fStarPath.lineTo(28.86f, 0.0f);
+ fStarPath.lineTo(43.30f, 25.0f);
+ fStarPath.lineTo(14.43f, 25.0f);
+ fStarPath.lineTo(0.0f, 50.0f);
+ fStarPath.lineTo(-14.43f, 25.0f);
+ fStarPath.lineTo(-43.30f, 25.0f);
+ fStarPath.lineTo(-28.86f, 0.0f);
+ fStarPath.lineTo(-43.30f, -25.0f);
+ fStarPath.lineTo(-14.43f, -25.0f);
fSquareRRectPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-50, -50, 100, 100),
10, 10));
fWideRectPath.addRect(SkRect::MakeXYWH(0, 0, 630, 70));
fWideOvalPath.addOval(SkRect::MakeXYWH(0, 0, 630, 70));
+ fNotchPath.moveTo(0, 80);
+ fNotchPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), -90, -90, false);
+ fNotchPath.lineTo(-75, 100);
+ fNotchPath.lineTo(-75, -100);
+ fNotchPath.lineTo(75, -100);
+ fNotchPath.lineTo(75, 100);
+ fNotchPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), 0, -90, false);
+
+ fTabPath.moveTo(-75, -100);
+ fTabPath.lineTo(75, -100);
+ fTabPath.lineTo(75, 100);
+ fTabPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), 0, 90, false);
+ fTabPath.arcTo(SkRect::MakeLTRB(-20, 80, 20, 120), 90, 90, false);
+ fTabPath.lineTo(-75, 100);
+
fLightPos = SkPoint3::Make(350, 0, 600);
}
@@ -141,8 +172,8 @@ protected:
const SkPaint& paint, SkScalar ambientAlpha,
const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
if (fIgnoreShadowAlpha) {
- ambientAlpha = 255;
- spotAlpha = 255;
+ ambientAlpha = 1;
+ spotAlpha = 1;
}
if (!fShowAmbient) {
ambientAlpha = 0;
@@ -220,6 +251,24 @@ protected:
this->drawShadowedPath(canvas, fCubicPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
+ paint.setColor(SK_ColorWHITE);
+ canvas->translate(250, -180);
+ zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
+ this->drawShadowedPath(canvas, fStarPath, zPlaneParams, paint,
+ kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
+
+ paint.setColor(SK_ColorWHITE);
+ canvas->translate(150, 0);
+ zPlaneParams.fZ = SkTMax(1.0f, 2 + fZDelta);
+ this->drawShadowedPath(canvas, fNotchPath, zPlaneParams, paint,
+ kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
+
+ paint.setColor(SK_ColorWHITE);
+ canvas->translate(200, 0);
+ zPlaneParams.fZ = SkTMax(1.0f, 16 + fZDelta);
+ this->drawShadowedPath(canvas, fTabPath, zPlaneParams, paint,
+ kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
+
// circular reveal
SkPath tmpPath;
SkPath tmpClipPath;
@@ -227,7 +276,7 @@ protected:
Op(fSquareRRectPath, tmpClipPath, kIntersect_SkPathOp, &tmpPath);
paint.setColor(SK_ColorMAGENTA);
- canvas->translate(-125, 60);
+ canvas->translate(-725, 240);
zPlaneParams.fZ = SkTMax(1.0f, 32 + fZDelta);
this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
lightPos, kLightWidth, .5f);
diff --git a/src/utils/SkOffsetPolygon.cpp b/src/utils/SkOffsetPolygon.cpp
index bfd12d2bc8..94fe96562d 100755
--- a/src/utils/SkOffsetPolygon.cpp
+++ b/src/utils/SkOffsetPolygon.cpp
@@ -218,11 +218,26 @@ struct EdgeData {
OffsetSegment fInset;
SkPoint fIntersection;
SkScalar fTValue;
+ uint16_t fStart;
+ uint16_t fEnd;
+ uint16_t fIndex;
bool fValid;
void init() {
fIntersection = fInset.fP0;
fTValue = SK_ScalarMin;
+ fStart = 0;
+ fEnd = 0;
+ fIndex = 0;
+ fValid = true;
+ }
+
+ void init(uint16_t start, uint16_t end) {
+ fIntersection = fInset.fP0;
+ fTValue = SK_ScalarMin;
+ fStart = start;
+ fEnd = end;
+ fIndex = start;
fValid = true;
}
};
@@ -571,7 +586,8 @@ static bool is_simple_polygon(const SkPoint* polygon, int polygonSize) {
// TODO: assuming a constant offset here -- do we want to support variable offset?
bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
- SkScalar offset, SkTDArray<SkPoint>* offsetPolygon) {
+ SkScalar offset, SkTDArray<SkPoint>* offsetPolygon,
+ SkTDArray<int>* polygonIndices) {
if (inputPolygonSize < 3) {
return false;
}
@@ -625,20 +641,20 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
EdgeData& edge = edgeData.push_back();
edge.fInset.fP0 = inputPolygonVerts[currIndex] + prevNormal;
edge.fInset.fP1 = inputPolygonVerts[currIndex] + currNormal;
- edge.init();
+ edge.init(currIndex, currIndex);
prevNormal = currNormal;
}
EdgeData& edge = edgeData.push_back();
edge.fInset.fP0 = inputPolygonVerts[currIndex] + prevNormal;
edge.fInset.fP1 = inputPolygonVerts[currIndex] + normals[currIndex];
- edge.init();
+ edge.init(currIndex, currIndex);
}
// Add the edge
EdgeData& edge = edgeData.push_back();
edge.fInset.fP0 = inputPolygonVerts[currIndex] + normals[currIndex];
edge.fInset.fP1 = inputPolygonVerts[nextIndex] + normals[currIndex];
- edge.init();
+ edge.init(currIndex, nextIndex);
prevIndex = currIndex;
currIndex++;
@@ -654,6 +670,10 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
prevIndex = (prevIndex + edgeDataSize - 1) % edgeDataSize;
continue;
}
+ if (!edgeData[currIndex].fValid) {
+ currIndex = (currIndex + 1) % edgeDataSize;
+ continue;
+ }
SkScalar s, t;
SkPoint intersection;
@@ -676,6 +696,7 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
// add intersection
edgeData[currIndex].fIntersection = intersection;
edgeData[currIndex].fTValue = t;
+ edgeData[currIndex].fIndex = edgeData[prevIndex].fEnd;
// go to next segment
prevIndex = currIndex;
@@ -714,6 +735,9 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
(*offsetPolygon)[currIndex],
kCleanupTolerance))) {
*offsetPolygon->push() = edgeData[i].fIntersection;
+ if (polygonIndices) {
+ *polygonIndices->push() = edgeData[i].fIndex;
+ }
currIndex++;
}
}
@@ -722,6 +746,9 @@ bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSiz
SkPointPriv::EqualsWithinTolerance((*offsetPolygon)[0], (*offsetPolygon)[currIndex],
kCleanupTolerance)) {
offsetPolygon->pop();
+ if (polygonIndices) {
+ polygonIndices->pop();
+ }
}
// compute signed area to check winding (it should be same as the original polygon)
diff --git a/src/utils/SkOffsetPolygon.h b/src/utils/SkOffsetPolygon.h
index a0555ab51e..b6c3a222ad 100755
--- a/src/utils/SkOffsetPolygon.h
+++ b/src/utils/SkOffsetPolygon.h
@@ -44,10 +44,12 @@ inline bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPoly
* @param offset How far we wish to offset the polygon.
* Positive value means inset, negative value means outset.
* @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,
- SkScalar offset, SkTDArray<SkPoint>* offsetPolygon);
+ SkScalar offset, SkTDArray<SkPoint>* offsetPolygon,
+ SkTDArray<int>* polygonIndices = nullptr);
/**
* Offset a segment by the given distance at each point.
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index 75e4059222..5d6a4cc15d 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -59,6 +59,9 @@ protected:
bool addArc(const SkVector& nextNormal, bool finishArc);
+ void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2);
+ void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3);
+
SkScalar heightFunc(SkScalar x, SkScalar y) {
return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ;
}
@@ -258,24 +261,41 @@ bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc)
currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
*fPositions.push() = fPrevPoint + currNormal;
*fColors.push() = fPenumbraColor;
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 2;
+ this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
prevNormal = currNormal;
}
if (finishArc && numSteps) {
*fPositions.push() = fPrevPoint + nextNormal;
*fColors.push() = fPenumbraColor;
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 2;
+ this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2);
}
fPrevOutset = nextNormal;
return (numSteps > 0);
}
+void SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) {
+ auto indices = fIndices.append(3);
+
+ indices[0] = index0;
+ indices[1] = index1;
+ indices[2] = index2;
+}
+
+void SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1,
+ uint16_t index2, uint16_t index3) {
+ auto indices = fIndices.append(6);
+
+ indices[0] = index0;
+ indices[1] = index1;
+ indices[2] = index2;
+
+ indices[3] = index2;
+ indices[4] = index1;
+ indices[5] = index3;
+}
+
bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
fTransformedHeightFunc = [this](const SkPoint& p) {
@@ -481,21 +501,11 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
*fColors.push() = fPenumbraColor;
if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 3;
- *fIndices.push() = fPositions.count() - 2;
-
- *fIndices.push() = fPositions.count() - 3;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 2;
+ this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
+ fPositions.count() - 2, fPositions.count() - 1);
} else {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fPositions.count() - 1;
-
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 3;
+ this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
+ fPrevUmbraIndex, fPositions.count() - 3);
}
// if transparent, add point to first one in array and add to center fan
@@ -516,21 +526,11 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
*fColors.push() = fPenumbraColor;
if (fColors[fPrevUmbraIndex] > fColors[fFirstVertexIndex]) {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fFirstVertexIndex;
-
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fFirstVertexIndex;
+ this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
+ fFirstVertexIndex, fPositions.count() - 1);
} else {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fPositions.count() - 1;
-
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fFirstVertexIndex;
+ this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
+ fPrevUmbraIndex, fFirstVertexIndex);
}
fPrevOutset = normal;
}
@@ -540,9 +540,7 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
fPositions[0] *= SkScalarFastInvert(fCentroidCount);
fColors[0] = this->umbraColor(fTransformedHeightFunc(fPositions[0]));
- *fIndices.push() = 0;
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fFirstVertexIndex;
+ this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
}
// final fan
@@ -551,9 +549,7 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
fPrevPoint = fFirstPoint;
fRadius = this->offset(fTransformedHeightFunc(fPrevPoint));
if (this->addArc(fFirstOutset, false)) {
- *fIndices.push() = fFirstVertexIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fFirstVertexIndex + 1;
+ this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, fFirstVertexIndex + 1);
} else {
// arc is too small, set the first penumbra point to be the same position
// as the last one
@@ -668,21 +664,11 @@ void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVecto
// set triangularization to get best interpolation of color
if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 3;
- *fIndices.push() = fPositions.count() - 2;
-
- *fIndices.push() = fPositions.count() - 3;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 2;
+ this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
+ fPositions.count() - 2, fPositions.count() - 1);
} else {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fPositions.count() - 1;
-
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 3;
+ this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
+ fPrevUmbraIndex, fPositions.count() - 3);
}
// if transparent, add point to first one in array and add to center fan
@@ -690,9 +676,7 @@ void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVecto
fPositions[0] += centerPoint;
++fCentroidCount;
- *fIndices.push() = 0;
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
+ this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
}
fSplitPreviousEdge = true;
@@ -712,21 +696,11 @@ void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVecto
// set triangularization to get best interpolation of color
if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 3;
- *fIndices.push() = fPositions.count() - 2;
-
- *fIndices.push() = fPositions.count() - 3;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 2;
+ this->appendQuad(fPrevUmbraIndex, fPositions.count() - 3,
+ fPositions.count() - 2, fPositions.count() - 1);
} else {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fPositions.count() - 1;
-
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 3;
+ this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
+ fPrevUmbraIndex, fPositions.count() - 3);
}
// if transparent, add point to first one in array and add to center fan
@@ -734,9 +708,7 @@ void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVecto
fPositions[0] += nextPoint;
++fCentroidCount;
- *fIndices.push() = 0;
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
+ this->appendTriangle(0, fPrevUmbraIndex, fPositions.count() - 2);
}
fPrevUmbraIndex = fPositions.count() - 2;
@@ -759,6 +731,9 @@ private:
bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
int getClosestUmbraPoint(const SkPoint& point);
+ bool computeConvexShadow(SkScalar radius);
+ bool computeConcaveShadow(SkScalar radius);
+
void handleLine(const SkPoint& p) override;
bool handlePolyPoint(const SkPoint& p);
@@ -804,9 +779,6 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
, fFirstUmbraOutside(false)
, fValidUmbra(true) {
- // TODO: support some concave paths
- SkASSERT(path.isConvex());
-
// make sure we're not below the canvas plane
if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) {
// Adjust light height and radius
@@ -864,125 +836,55 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
}
// check to see if umbra collapses
- SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, fPathPolygon[0],
- fPathPolygon[1]);
- SkRect bounds;
- bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
- for (int i = 1; i < fPathPolygon.count(); ++i) {
- int j = i + 1;
- if (i == fPathPolygon.count() - 1) {
- j = 0;
+ if (path.isConvex()) {
+ SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
+ fPathPolygon[0],
+ fPathPolygon[1]);
+ SkRect bounds;
+ bounds.setBounds(&fPathPolygon[0], fPathPolygon.count());
+ for (int i = 1; i < fPathPolygon.count(); ++i) {
+ int j = i + 1;
+ if (i == fPathPolygon.count() - 1) {
+ j = 0;
+ }
+ SkPoint currPoint = fPathPolygon[i];
+ SkPoint nextPoint = fPathPolygon[j];
+ SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
+ nextPoint);
+ if (distSq < minDistSq) {
+ minDistSq = distSq;
+ }
}
- SkPoint currPoint = fPathPolygon[i];
- SkPoint nextPoint = fPathPolygon[j];
- SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
- nextPoint);
- if (distSq < minDistSq) {
- minDistSq = distSq;
+ static constexpr auto kTolerance = 1.0e-2f;
+ if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
+ // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
+ SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
+ fOffsetAdjust = newRadius - radius;
+ SkScalar ratio = 128 * (newRadius + radius) / radius;
+ // they aren't PMColors, but the interpolation algorithm is the same
+ fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
+ radius = newRadius;
}
}
- static constexpr auto kTolerance = 1.0e-2f;
- if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) {
- // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha
- SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance;
- fOffsetAdjust = newRadius - radius;
- SkScalar ratio = 128 * (newRadius + radius) / radius;
- // they aren't PMColors, but the interpolation algorithm is the same
- fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
- radius = newRadius;
- }
// compute vectors for clip tests
this->computeClipVectorsAndTestCentroid();
- // generate inner ring
- if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
- &fUmbraPolygon)) {
- // this shouldn't happen, but just in case we'll inset using the centroid
- fValidUmbra = false;
- }
-
- // walk around the path polygon, generate outer ring and connect to inner ring
- if (fTransparent) {
- *fPositions.push() = fCentroid;
- *fColors.push() = fUmbraColor;
- }
- fCurrUmbraPoint = 0;
- for (int i = 0; i < fPathPolygon.count(); ++i) {
- if (!this->handlePolyPoint(fPathPolygon[i])) {
- return;
+ if (ctm.hasPerspective()) {
+ for (int i = 0; i < fPositions.count(); ++i) {
+ SkScalar pathZ = fTransformedHeightFunc(fPositions[i]);
+ SkScalar factor = SkScalarInvert(fLightZ - pathZ);
+ fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor;
}
}
- if (!this->indexCount()) {
- return;
- }
-
- // finish up the final verts
- SkVector normal;
- if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
- normal *= fRadius;
- this->addArc(normal, true);
-
- // add to center fan
- if (fTransparent) {
- *fIndices.push() = 0;
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fFirstVertexIndex;
- // or to clip ring
- } else {
- if (fFirstUmbraOutside) {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fFirstVertexIndex;
- *fIndices.push() = fFirstVertexIndex + 1;
- if (fPrevUmbraOutside) {
- // fill out quad
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fFirstVertexIndex + 1;
- *fIndices.push() = fPrevUmbraIndex + 1;
- }
- } else if (fPrevUmbraOutside) {
- // add tri
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fFirstVertexIndex;
- *fIndices.push() = fPrevUmbraIndex + 1;
- }
+ if (path.isConvex()) {
+ if (!this->computeConvexShadow(radius)) {
+ return;
}
-
- // add final edge
- *fPositions.push() = fFirstPoint + normal;
- *fColors.push() = fPenumbraColor;
-
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fFirstVertexIndex;
-
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fFirstVertexIndex;
-
- fPrevOutset = normal;
- }
-
- // final fan
- if (fPositions.count() >= 3) {
- fPrevUmbraIndex = fFirstVertexIndex;
- fPrevPoint = fFirstPoint;
- if (this->addArc(fFirstOutset, false)) {
- *fIndices.push() = fFirstVertexIndex;
- *fIndices.push() = fPositions.count() - 1;
- if (fFirstUmbraOutside) {
- *fIndices.push() = fFirstVertexIndex + 2;
- } else {
- *fIndices.push() = fFirstVertexIndex + 1;
- }
- } else {
- // no arc added, fix up by setting first penumbra point position to last one
- if (fFirstUmbraOutside) {
- fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
- } else {
- fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
- }
+ } else {
+ if (!this->computeConcaveShadow(radius)) {
+ return;
}
}
@@ -1010,13 +912,8 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
*fPositions.push() = fCentroid + SkVector::Make(2, 2);
*fColors.push() = SkColorSetARGB(255, 0, 255, 255);
- *fIndices.push() = fPositions.count() - 4;
- *fIndices.push() = fPositions.count() - 2;
- *fIndices.push() = fPositions.count() - 1;
-
- *fIndices.push() = fPositions.count() - 4;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = fPositions.count() - 3;
+ this->appendQuad(fPositions.count() - 2, fPositions.count() - 1,
+ fPositions.count() - 4, fPositions.count() - 3);
#endif
fSucceeded = true;
@@ -1199,6 +1096,244 @@ int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
return index;
}
+bool SkSpotShadowTessellator::computeConvexShadow(SkScalar radius) {
+ // generate inner ring
+ if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
+ &fUmbraPolygon)) {
+ // this shouldn't happen, but just in case we'll inset using the centroid
+ fValidUmbra = false;
+ }
+
+ // walk around the path polygon, generate outer ring and connect to inner ring
+ if (fTransparent) {
+ *fPositions.push() = fCentroid;
+ *fColors.push() = fUmbraColor;
+ }
+ fCurrUmbraPoint = 0;
+ for (int i = 0; i < fPathPolygon.count(); ++i) {
+ if (!this->handlePolyPoint(fPathPolygon[i])) {
+ return false;
+ }
+ }
+
+ if (!this->indexCount()) {
+ return false;
+ }
+
+ // finish up the final verts
+ SkVector normal;
+ if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) {
+ normal *= fRadius;
+ this->addArc(normal, true);
+
+ // add to center fan
+ if (fTransparent) {
+ this->appendTriangle(0, fPrevUmbraIndex, fFirstVertexIndex);
+ // or to clip ring
+ } else {
+ if (fFirstUmbraOutside) {
+ this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fFirstVertexIndex + 1);
+ if (fPrevUmbraOutside) {
+ // fill out quad
+ this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex + 1,
+ fPrevUmbraIndex + 1);
+ }
+ } else if (fPrevUmbraOutside) {
+ // add tri
+ this->appendTriangle(fPrevUmbraIndex, fFirstVertexIndex, fPrevUmbraIndex + 1);
+ }
+ }
+
+ // add final edge
+ *fPositions.push() = fFirstPoint + normal;
+ *fColors.push() = fPenumbraColor;
+
+ this->appendQuad(fPrevUmbraIndex, fPositions.count() - 2,
+ fFirstVertexIndex, fPositions.count() - 1);
+
+ fPrevOutset = normal;
+ }
+
+ // final fan
+ if (fPositions.count() >= 3) {
+ fPrevUmbraIndex = fFirstVertexIndex;
+ fPrevPoint = fFirstPoint;
+ if (this->addArc(fFirstOutset, false)) {
+ if (fFirstUmbraOutside) {
+ this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
+ fFirstVertexIndex + 2);
+ } else {
+ this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1,
+ fFirstVertexIndex + 1);
+ }
+ } else {
+ // no arc added, fix up by setting first penumbra point position to last one
+ if (fFirstUmbraOutside) {
+ fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1];
+ } else {
+ fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1];
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
+ // TODO: remove when we support filling the penumbra
+ if (fTransparent) {
+ return false;
+ }
+
+ // generate inner ring
+ SkTDArray<int> umbraIndices;
+ umbraIndices.setReserve(fPathPolygon.count());
+ if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), radius,
+ &fUmbraPolygon, &umbraIndices)) {
+ // TODO: figure out how to handle this case
+ return false;
+ }
+
+ // generate outer ring
+ SkTDArray<SkPoint> penumbraPolygon;
+ SkTDArray<int> penumbraIndices;
+ penumbraPolygon.setReserve(fUmbraPolygon.count());
+ penumbraIndices.setReserve(fUmbraPolygon.count());
+ if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), -radius,
+ &penumbraPolygon, &penumbraIndices)) {
+ // TODO: figure out how to handle this case
+ return false;
+ }
+
+ if (!fUmbraPolygon.count() || !penumbraPolygon.count()) {
+ return false;
+ }
+
+ // attach the rings together
+
+ // find minimum indices
+ int minIndex = 0;
+ int min = penumbraIndices[0];
+ for (int i = 1; i < penumbraIndices.count(); ++i) {
+ if (penumbraIndices[i] < min) {
+ min = penumbraIndices[i];
+ minIndex = i;
+ }
+ }
+ int currPenumbra = minIndex;
+
+ minIndex = 0;
+ min = umbraIndices[0];
+ for (int i = 1; i < umbraIndices.count(); ++i) {
+ if (umbraIndices[i] < min) {
+ min = umbraIndices[i];
+ minIndex = i;
+ }
+ }
+ int currUmbra = minIndex;
+
+ // now find a case where the indices are equal (there should be at least one)
+ int maxPenumbraIndex = fPathPolygon.count()-1;
+ int maxUmbraIndex = fPathPolygon.count()-1;
+ while (penumbraIndices[currPenumbra] != umbraIndices[currUmbra]) {
+ if (penumbraIndices[currPenumbra] < umbraIndices[currUmbra]) {
+ penumbraIndices[currPenumbra] += fPathPolygon.count();
+ maxPenumbraIndex = penumbraIndices[currPenumbra];
+ currPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
+ } else {
+ umbraIndices[currUmbra] += fPathPolygon.count();
+ maxUmbraIndex = umbraIndices[currUmbra];
+ currUmbra = (currUmbra + 1) % fUmbraPolygon.count();
+ }
+ }
+
+ *fPositions.push() = penumbraPolygon[currPenumbra];
+ *fColors.push() = fPenumbraColor;
+ int prevPenumbraIndex = 0;
+ *fPositions.push() = fUmbraPolygon[currUmbra];
+ *fColors.push() = fUmbraColor;
+ fPrevUmbraIndex = 1;
+
+ int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
+ int nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
+ while (penumbraIndices[nextPenumbra] <= maxPenumbraIndex ||
+ umbraIndices[nextUmbra] <= maxUmbraIndex) {
+
+ if (umbraIndices[nextUmbra] == penumbraIndices[nextPenumbra]) {
+ // advance both one step
+ *fPositions.push() = penumbraPolygon[nextPenumbra];
+ *fColors.push() = fPenumbraColor;
+ int currPenumbraIndex = fPositions.count() - 1;
+
+ *fPositions.push() = fUmbraPolygon[nextUmbra];
+ *fColors.push() = fUmbraColor;
+ int currUmbraIndex = fPositions.count() - 1;
+
+ this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
+ fPrevUmbraIndex, currUmbraIndex);
+
+ prevPenumbraIndex = currPenumbraIndex;
+ penumbraIndices[currPenumbra] += fPathPolygon.count();
+ currPenumbra = nextPenumbra;
+ nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
+
+ fPrevUmbraIndex = currUmbraIndex;
+ umbraIndices[currUmbra] += fPathPolygon.count();
+ currUmbra = nextUmbra;
+ nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
+ }
+
+ while (penumbraIndices[nextPenumbra] < umbraIndices[nextUmbra] &&
+ penumbraIndices[nextPenumbra] <= maxPenumbraIndex) {
+ // fill out penumbra arc
+ *fPositions.push() = penumbraPolygon[nextPenumbra];
+ *fColors.push() = fPenumbraColor;
+ int currPenumbraIndex = fPositions.count() - 1;
+
+ this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex);
+
+ prevPenumbraIndex = currPenumbraIndex;
+ // this ensures the ordering when we wrap around
+ penumbraIndices[currPenumbra] += fPathPolygon.count();
+ currPenumbra = nextPenumbra;
+ nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
+ }
+
+ while (umbraIndices[nextUmbra] < penumbraIndices[nextPenumbra] &&
+ umbraIndices[nextUmbra] <= maxUmbraIndex) {
+ // fill out umbra arc
+ *fPositions.push() = fUmbraPolygon[nextUmbra];
+ *fColors.push() = fUmbraColor;
+ int currUmbraIndex = fPositions.count() - 1;
+
+ this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
+
+ fPrevUmbraIndex = currUmbraIndex;
+ // this ensures the ordering when we wrap around
+ umbraIndices[currUmbra] += fPathPolygon.count();
+ currUmbra = nextUmbra;
+ nextUmbra = (currUmbra + 1) % fUmbraPolygon.count();
+ }
+ }
+ // finish up by advancing both one step
+ *fPositions.push() = penumbraPolygon[nextPenumbra];
+ *fColors.push() = fPenumbraColor;
+ int currPenumbraIndex = fPositions.count() - 1;
+
+ *fPositions.push() = fUmbraPolygon[nextUmbra];
+ *fColors.push() = fUmbraColor;
+ int currUmbraIndex = fPositions.count() - 1;
+
+ this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
+ fPrevUmbraIndex, currUmbraIndex);
+
+ if (fTransparent) {
+ // TODO: fill penumbra
+ }
+
+ return true;
+}
+
void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
SkPoint* pts, int count) {
// TODO: vectorize
@@ -1356,33 +1491,27 @@ void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector&
if (!duplicate) {
// add to center fan if transparent or centroid showing
if (fTransparent) {
- *fIndices.push() = 0;
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = currUmbraIndex;
+ this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex);
// otherwise add to clip ring
} else {
SkPoint clipPoint;
bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
&clipPoint);
+
if (isOutside) {
*fPositions.push() = clipPoint;
*fColors.push() = fUmbraColor;
-
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = currUmbraIndex;
- *fIndices.push() = currUmbraIndex + 1;
+ this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1);
if (fPrevUmbraOutside) {
// fill out quad
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = currUmbraIndex + 1;
- *fIndices.push() = fPrevUmbraIndex + 1;
+ this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1,
+ fPrevUmbraIndex + 1);
}
} else if (fPrevUmbraOutside) {
// add tri
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = currUmbraIndex;
- *fIndices.push() = fPrevUmbraIndex + 1;
+ this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1);
}
+
fPrevUmbraOutside = isOutside;
}
}
@@ -1393,14 +1522,9 @@ void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector&
*fColors.push() = fPenumbraColor;
if (!duplicate) {
- *fIndices.push() = fPrevUmbraIndex;
- *fIndices.push() = prevPenumbraIndex;
- *fIndices.push() = currUmbraIndex;
+ this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
}
-
- *fIndices.push() = prevPenumbraIndex;
- *fIndices.push() = fPositions.count() - 1;
- *fIndices.push() = currUmbraIndex;
+ this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex);
fPrevUmbraIndex = currUmbraIndex;
fPrevOutset = nextNormal;
diff --git a/tests/OffsetSimplePolyTest.cpp b/tests/OffsetSimplePolyTest.cpp
index 50f680ef23..de720b5cf2 100644
--- a/tests/OffsetSimplePolyTest.cpp
+++ b/tests/OffsetSimplePolyTest.cpp
@@ -92,7 +92,6 @@ DEF_TEST(OffsetSimplePoly, reporter) {
// past full inset
result = SkOffsetSimplePolygon(&rrectPoly[0], rrectPoly.count(), 75, &offsetPoly);
REPORTER_ASSERT(reporter, !result);
- REPORTER_ASSERT(reporter, offsetPoly.count() == 0);
// troublesome case
SkTDArray<SkPoint> clippedRRectPoly;