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:
parent
3cdd7e22dd
commit
872da6b571
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user