Add initial support for simple concave shadows.

Adds support for spot shadow outlines. Since filling the penumbra still
needs to be done, this code is disabled for now.

Bug: skia:
Change-Id: I3369eb13832b47ad16dd29ce7c7d6a1a10b39aeb
Reviewed-on: https://skia-review.googlesource.com/22363
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Jim Van Verth 2018-04-10 11:24:11 -04:00 committed by Skia Commit-Bot
parent 3cdd7e22dd
commit 872da6b571
5 changed files with 422 additions and 221 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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.

View File

@ -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;
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;
}
}
// 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])) {
if (path.isConvex()) {
if (!this->computeConvexShadow(radius)) {
return;
}
}
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;
}
}
// 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;

View File

@ -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;