Revert "Create new inset algorithm for spot shadows"

This reverts commit e5f5bf5175.

Reason for revert: Breaking a bunch of bots. e.g:

https://luci-milo.appspot.com/swarming/task/3519cae0a03c7b10/steps/dm/0/stdout

Original change's description:
> Create new inset algorithm for spot shadows
> 
> BUG=skia:
> 
> Change-Id: If7c67c2a5b9beea28f86d13362a5156b46394d0e
> Reviewed-on: https://skia-review.googlesource.com/9875
> Commit-Queue: Ravi Mistry <rmistry@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>
> 

TBR=jvanverth@google.com,bsalomon@google.com,rmistry@google.com,robertphillips@google.com,msarett@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:

Change-Id: I3d119ff631dbb1a41f873b9c8753d542ec91254e
Reviewed-on: https://skia-review.googlesource.com/10112
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2017-03-24 16:00:35 +00:00 committed by Skia Commit-Bot
parent adf7b330bd
commit e7c85c45c4
9 changed files with 306 additions and 919 deletions

View File

@ -6,7 +6,6 @@
*/
#include "gm.h"
#include "SkInsetConvexPolygon.h"
#include "SkPathPriv.h"
static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
@ -23,135 +22,6 @@ static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
}
}
namespace ConvexLineOnlyData {
// narrow rect
const SkPoint gPoints0[] = {
{ -1.5f, -50.0f },
{ 1.5f, -50.0f },
{ 1.5f, 50.0f },
{ -1.5f, 50.0f }
};
// narrow rect on an angle
const SkPoint gPoints1[] = {
{ -50.0f, -49.0f },
{ -49.0f, -50.0f },
{ 50.0f, 49.0f },
{ 49.0f, 50.0f }
};
// trap - narrow on top - wide on bottom
const SkPoint gPoints2[] = {
{ -10.0f, -50.0f },
{ 10.0f, -50.0f },
{ 50.0f, 50.0f },
{ -50.0f, 50.0f }
};
// wide skewed rect
const SkPoint gPoints3[] = {
{ -50.0f, -50.0f },
{ 0.0f, -50.0f },
{ 50.0f, 50.0f },
{ 0.0f, 50.0f }
};
// thin rect with colinear-ish lines
const SkPoint gPoints4[] = {
{ -6.0f, -50.0f },
{ 4.0f, -50.0f },
{ 5.0f, -25.0f },
{ 6.0f, 0.0f },
{ 5.0f, 25.0f },
{ 4.0f, 50.0f },
{ -4.0f, 50.0f }
};
// degenerate
const SkPoint gPoints5[] = {
{ -0.025f, -0.025f },
{ 0.025f, -0.025f },
{ 0.025f, 0.025f },
{ -0.025f, 0.025f }
};
// Triangle in which the first point should fuse with last
const SkPoint gPoints6[] = {
{ -20.0f, -13.0f },
{ -20.0f, -13.05f },
{ 20.0f, -13.0f },
{ 20.0f, 27.0f }
};
// thin rect with colinear lines
const SkPoint gPoints7[] = {
{ -10.0f, -50.0f },
{ 10.0f, -50.0f },
{ 10.0f, -25.0f },
{ 10.0f, 0.0f },
{ 10.0f, 25.0f },
{ 10.0f, 50.0f },
{ -10.0f, 50.0f }
};
// capped teardrop
const SkPoint gPoints8[] = {
{ 50.00f, 50.00f },
{ 0.00f, 50.00f },
{ -15.45f, 47.55f },
{ -29.39f, 40.45f },
{ -40.45f, 29.39f },
{ -47.55f, 15.45f },
{ -50.00f, 0.00f },
{ -47.55f, -15.45f },
{ -40.45f, -29.39f },
{ -29.39f, -40.45f },
{ -15.45f, -47.55f },
{ 0.00f, -50.00f },
{ 50.00f, -50.00f }
};
// teardrop
const SkPoint gPoints9[] = {
{ 4.39f, 40.45f },
{ -9.55f, 47.55f },
{ -25.00f, 50.00f },
{ -40.45f, 47.55f },
{ -54.39f, 40.45f },
{ -65.45f, 29.39f },
{ -72.55f, 15.45f },
{ -75.00f, 0.00f },
{ -72.55f, -15.45f },
{ -65.45f, -29.39f },
{ -54.39f, -40.45f },
{ -40.45f, -47.55f },
{ -25.0f, -50.0f },
{ -9.55f, -47.55f },
{ 4.39f, -40.45f },
{ 75.00f, 0.00f }
};
// clipped triangle
const SkPoint gPoints10[] = {
{ -10.0f, -50.0f },
{ 10.0f, -50.0f },
{ 50.0f, 31.0f },
{ 40.0f, 50.0f },
{ -40.0f, 50.0f },
{ -50.0f, 31.0f },
};
const SkPoint* gPoints[] = {
gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
gPoints7, gPoints8, gPoints9, gPoints10,
};
const size_t gSizes[] = {
SK_ARRAY_COUNT(gPoints0),
SK_ARRAY_COUNT(gPoints1),
SK_ARRAY_COUNT(gPoints2),
SK_ARRAY_COUNT(gPoints3),
SK_ARRAY_COUNT(gPoints4),
SK_ARRAY_COUNT(gPoints5),
SK_ARRAY_COUNT(gPoints6),
SK_ARRAY_COUNT(gPoints7),
SK_ARRAY_COUNT(gPoints8),
SK_ARRAY_COUNT(gPoints9),
SK_ARRAY_COUNT(gPoints10),
};
static_assert(SK_ARRAY_COUNT(gSizes) == SK_ARRAY_COUNT(gPoints), "array_mismatch");
}
namespace skiagm {
// This GM is intended to exercise Ganesh's handling of convex line-only
@ -172,19 +42,146 @@ protected:
SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
bool runAsBench() const override { return true; }
static SkPath GetPath(int index, SkPath::Direction dir) {
static SkPath GetPath(int index, int offset, SkPath::Direction dir) {
// narrow rect
const SkPoint gPoints0[] = {
{ -1.5f, -50.0f },
{ 1.5f, -50.0f },
{ 1.5f, 50.0f },
{ -1.5f, 50.0f }
};
// narrow rect on an angle
const SkPoint gPoints1[] = {
{ -50.0f, -49.0f },
{ -49.0f, -50.0f },
{ 50.0f, 49.0f },
{ 49.0f, 50.0f }
};
// trap - narrow on top - wide on bottom
const SkPoint gPoints2[] = {
{ -10.0f, -50.0f },
{ 10.0f, -50.0f },
{ 50.0f, 50.0f },
{ -50.0f, 50.0f }
};
// wide skewed rect
const SkPoint gPoints3[] = {
{ -50.0f, -50.0f },
{ 0.0f, -50.0f },
{ 50.0f, 50.0f },
{ 0.0f, 50.0f }
};
// thin rect with colinear-ish lines
const SkPoint gPoints4[] = {
{ -6.0f, -50.0f },
{ 4.0f, -50.0f },
{ 5.0f, -25.0f },
{ 6.0f, 0.0f },
{ 5.0f, 25.0f },
{ 4.0f, 50.0f },
{ -4.0f, 50.0f }
};
// degenerate
const SkPoint gPoints5[] = {
{ -0.025f, -0.025f },
{ 0.025f, -0.025f },
{ 0.025f, 0.025f },
{ -0.025f, 0.025f }
};
// Triangle in which the first point should fuse with last
const SkPoint gPoints6[] = {
{ -20.0f, -13.0f },
{ -20.0f, -13.05f },
{ 20.0f, -13.0f },
{ 20.0f, 27.0f }
};
// thin rect with colinear lines
const SkPoint gPoints7[] = {
{ -10.0f, -50.0f },
{ 10.0f, -50.0f },
{ 10.0f, -25.0f },
{ 10.0f, 0.0f },
{ 10.0f, 25.0f },
{ 10.0f, 50.0f },
{ -10.0f, 50.0f }
};
// capped teardrop
const SkPoint gPoints8[] = {
{ 50.00f, 50.00f },
{ 0.00f, 50.00f },
{ -15.45f, 47.55f },
{ -29.39f, 40.45f },
{ -40.45f, 29.39f },
{ -47.55f, 15.45f },
{ -50.00f, 0.00f },
{ -47.55f, -15.45f },
{ -40.45f, -29.39f },
{ -29.39f, -40.45f },
{ -15.45f, -47.55f },
{ 0.00f, -50.00f },
{ 50.00f, -50.00f }
};
// teardrop
const SkPoint gPoints9[] = {
{ 4.39f, 40.45f },
{ -9.55f, 47.55f },
{ -25.00f, 50.00f },
{ -40.45f, 47.55f },
{ -54.39f, 40.45f },
{ -65.45f, 29.39f },
{ -72.55f, 15.45f },
{ -75.00f, 0.00f },
{ -72.55f, -15.45f },
{ -65.45f, -29.39f },
{ -54.39f, -40.45f },
{ -40.45f, -47.55f },
{ -25.0f, -50.0f },
{ -9.55f, -47.55f },
{ 4.39f, -40.45f },
{ 75.00f, 0.00f }
};
// clipped triangle
const SkPoint gPoints10[] = {
{ -10.0f, -50.0f },
{ 10.0f, -50.0f },
{ 50.0f, 31.0f },
{ 40.0f, 50.0f },
{ -40.0f, 50.0f },
{ -50.0f, 31.0f },
};
const SkPoint* gPoints[] = {
gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
gPoints7, gPoints8, gPoints9, gPoints10,
};
const size_t gSizes[] = {
SK_ARRAY_COUNT(gPoints0),
SK_ARRAY_COUNT(gPoints1),
SK_ARRAY_COUNT(gPoints2),
SK_ARRAY_COUNT(gPoints3),
SK_ARRAY_COUNT(gPoints4),
SK_ARRAY_COUNT(gPoints5),
SK_ARRAY_COUNT(gPoints6),
SK_ARRAY_COUNT(gPoints7),
SK_ARRAY_COUNT(gPoints8),
SK_ARRAY_COUNT(gPoints9),
SK_ARRAY_COUNT(gPoints10),
};
static_assert(SK_ARRAY_COUNT(gSizes) == SK_ARRAY_COUNT(gPoints), "array_mismatch");
std::unique_ptr<SkPoint[]> data(nullptr);
const SkPoint* points;
int numPts;
if (index < (int) SK_ARRAY_COUNT(ConvexLineOnlyData::gPoints)) {
if (index < (int) SK_ARRAY_COUNT(gPoints)) {
// manually specified
points = ConvexLineOnlyData::gPoints[index];
numPts = (int)ConvexLineOnlyData::gSizes[index];
points = gPoints[index];
numPts = (int) gSizes[index];
} else {
// procedurally generated
SkScalar width = kMaxPathHeight/2;
SkScalar height = kMaxPathHeight/2;
switch (index-SK_ARRAY_COUNT(ConvexLineOnlyData::gPoints)) {
switch (index-SK_ARRAY_COUNT(gPoints)) {
case 0:
numPts = 3;
break;
@ -262,7 +259,7 @@ protected:
SkPoint center;
{
SkPath path = GetPath(index, SkPath::kCW_Direction);
SkPath path = GetPath(index, 0, SkPath::kCW_Direction);
if (offset->fX+path.getBounds().width() > kGMWidth) {
offset->fX = 0;
offset->fY += kMaxPathHeight;
@ -289,7 +286,7 @@ protected:
paint.setAntiAlias(true);
for (size_t i = 0; i < SK_ARRAY_COUNT(scales); ++i) {
SkPath path = GetPath(index, dirs[i%2]);
SkPath path = GetPath(index, (int) i, dirs[i%2]);
if (fDoStrokeAndFill) {
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setStrokeJoin(joins[i%3]);
@ -350,173 +347,8 @@ private:
typedef GM INHERITED;
};
// This GM is intended to exercise the insetting of convex polygons
class ConvexPolygonInsetGM : public GM {
public:
ConvexPolygonInsetGM() {
this->setBGColor(0xFFFFFFFF);
}
protected:
SkString onShortName() override {
return SkString("convex-polygon-inset");
}
SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
bool runAsBench() const override { return true; }
static void GetPath(int index, SkPath::Direction dir,
std::unique_ptr<SkPoint[]>* data, int* numPts) {
if (index < (int)SK_ARRAY_COUNT(ConvexLineOnlyData::gPoints)) {
// manually specified
*numPts = (int)ConvexLineOnlyData::gSizes[index];
data->reset(new SkPoint[*numPts]);
if (SkPath::kCW_Direction == dir) {
for (int i = 0; i < *numPts; ++i) {
(*data)[i] = ConvexLineOnlyData::gPoints[index][i];
}
} else {
for (int i = 0; i < *numPts; ++i) {
(*data)[i] = ConvexLineOnlyData::gPoints[index][*numPts - i - 1];
}
}
} else {
// procedurally generated
SkScalar width = kMaxPathHeight / 2;
SkScalar height = kMaxPathHeight / 2;
switch (index - SK_ARRAY_COUNT(ConvexLineOnlyData::gPoints)) {
case 0:
*numPts = 3;
break;
case 1:
*numPts = 4;
break;
case 2:
*numPts = 5;
break;
case 3: // squashed pentagon
*numPts = 5;
width = kMaxPathHeight / 5;
break;
case 4:
*numPts = 6;
break;
case 5:
*numPts = 8;
break;
case 6: // squashed octogon
*numPts = 8;
width = kMaxPathHeight / 5;
break;
case 7:
*numPts = 20;
break;
case 8:
*numPts = 100;
break;
default:
*numPts = 3;
break;
}
data->reset(new SkPoint[*numPts]);
create_ngon(*numPts, data->get(), width, height);
if (SkPath::kCCW_Direction == dir) {
// reverse it
for (int i = 0; i < *numPts/2; ++i) {
SkPoint tmp = (*data)[i];
(*data)[i] = (*data)[*numPts - i - 1];
(*data)[*numPts - i - 1] = tmp;
}
}
}
}
// Draw a single path several times, shrinking it, flipping its direction
// and changing its start vertex each time.
void drawPath(SkCanvas* canvas, int index, SkPoint* offset) {
SkPoint center;
{
std::unique_ptr<SkPoint[]> data(nullptr);
int numPts;
GetPath(index, SkPath::kCW_Direction, &data, &numPts);
SkRect bounds;
bounds.set(data.get(), numPts);
if (offset->fX + bounds.width() > kGMWidth) {
offset->fX = 0;
offset->fY += kMaxPathHeight;
}
center = { offset->fX + SkScalarHalf(bounds.width()), offset->fY };
offset->fX += bounds.width();
}
const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
const float insets[] = { 5, 10, 15, 20, 25, 30, 35, 40 };
const SkColor colors[] = { 0xFF901313, 0xFF8D6214, 0xFF698B14, 0xFF1C8914,
0xFF148755, 0xFF146C84, 0xFF142482, 0xFF4A1480 };
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(1);
std::unique_ptr<SkPoint[]> data(nullptr);
int numPts;
GetPath(index, dirs[index % 2], &data, &numPts);
{
SkPath path;
path.moveTo(data.get()[0]);
for (int i = 1; i < numPts; ++i) {
path.lineTo(data.get()[i]);
}
path.close();
canvas->save();
canvas->translate(center.fX, center.fY);
canvas->drawPath(path, paint);
canvas->restore();
}
SkTDArray<SkPoint> insetPoly;
for (size_t i = 0; i < SK_ARRAY_COUNT(insets); ++i) {
if (SkInsetConvexPolygon(data.get(), numPts, insets[i], &insetPoly)) {
SkPath path;
path.moveTo(insetPoly[0]);
for (int i = 1; i < insetPoly.count(); ++i) {
path.lineTo(insetPoly[i]);
}
path.close();
paint.setColor(colors[i]);
canvas->save();
canvas->translate(center.fX, center.fY);
canvas->drawPath(path, paint);
canvas->restore();
}
}
}
void onDraw(SkCanvas* canvas) override {
// the right edge of the last drawn path
SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) };
for (int i = 0; i < kNumPaths; ++i) {
this->drawPath(canvas, i, &offset);
}
}
private:
static constexpr int kNumPaths = 20;
static constexpr int kMaxPathHeight = 100;
static constexpr int kGMWidth = 512;
static constexpr int kGMHeight = 512;
typedef GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM(return new ConvexLineOnlyPathsGM(false);)
DEF_GM(return new ConvexLineOnlyPathsGM(true);)
DEF_GM(return new ConvexPolygonInsetGM();)
}

View File

@ -19,7 +19,7 @@ void draw_shadow(SkCanvas* canvas, const SkPath& path, int height, SkColor color
color, flags, cache);
}
static constexpr int kW = 800;
static constexpr int kW = 700;
static constexpr int kH = 800;
DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) {
@ -38,7 +38,6 @@ DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) {
paths.push_back().addRect(SkRect::MakeWH(50, 50));
paths.push_back().addCircle(25, 25, 25);
paths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
paths.push_back().addOval(SkRect::MakeWH(20, 60));
static constexpr SkScalar kPad = 15.f;
static constexpr SkPoint3 kLightPos = {250, 400, 500};

View File

@ -112,7 +112,6 @@ tests_sources = [
"$_tests/ImageTest.cpp",
"$_tests/IndexedPngOverflowTest.cpp",
"$_tests/InfRectTest.cpp",
"$_tests/InsetConvexPolyTest.cpp",
"$_tests/InterpolatorTest.cpp",
"$_tests/IntTextureTest.cpp",
"$_tests/InvalidIndexedPngTest.cpp",

View File

@ -42,8 +42,6 @@ skia_utils_sources = [
"$_src/utils/SkDumpCanvas.cpp",
"$_src/utils/SkEventTracer.cpp",
"$_src/utils/SkFloatUtils.h",
"$_src/utils/SkInsetConvexPolygon.cpp",
"$_src/utils/SkInsetConvexPolygon.h",
"$_src/utils/SkInterpolator.cpp",
"$_src/utils/SkMatrix22.cpp",
"$_src/utils/SkMatrix22.h",

View File

@ -500,8 +500,8 @@ protected:
paint.setColor(SK_ColorCYAN);
canvas->translate(250, 0);
lightPos.fX += 250;
this->drawShadowedPath(canvas, fCubicPath, SkTMax(1.0f, 16 + fZDelta), paint,
kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
this->drawShadowedPath(canvas, fCubicPath, 16, paint, kAmbientAlpha,
lightPos, kLightWidth, kSpotAlpha);
// circular reveal
SkPath tmpPath;
@ -513,7 +513,7 @@ protected:
canvas->translate(-125, 60);
lightPos.fX -= 125;
lightPos.fY += 60;
this->drawShadowedPath(canvas, tmpPath, SkTMax(1.0f, 32 + fZDelta), paint, .1f,
this->drawShadowedPath(canvas, tmpPath, 32, paint, .1f,
lightPos, kLightWidth, .5f);
// perspective paths
@ -532,7 +532,7 @@ protected:
lightPos = fLightPos;
lightPos.fX += pivot.fX + translate.fX;
lightPos.fY += pivot.fY + translate.fY;
this->drawShadowedPath(canvas, fWideRectPath, SkTMax(1.0f, 16 + fZDelta), paint, .1f,
this->drawShadowedPath(canvas, fWideRectPath, 16, paint, .1f,
lightPos, kLightWidth, .5f);
pivot = SkPoint::Make(fWideOvalPath.getBounds().width() / 2,
@ -547,12 +547,12 @@ protected:
lightPos = fLightPos;
lightPos.fX += pivot.fX + translate.fX;
lightPos.fY += pivot.fY + translate.fY;
this->drawShadowedPath(canvas, fWideOvalPath, SkTMax(1.0f, 32 + fZDelta), paint, .1f,
this->drawShadowedPath(canvas, fWideOvalPath, 32, paint, .1f,
lightPos, kLightWidth, .5f);
}
bool onAnimate(const SkAnimTimer& timer) override {
fAnimTranslate = timer.pingPong(30, 0, 200, -200);
fAnimTranslate = timer.pingPong(10, 0, 200, -200);
return true;
}

View File

@ -1,233 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkInsetConvexPolygon.h"
#include "SkTemplates.h"
struct InsetSegment {
SkPoint fP0;
SkPoint fP1;
};
// Computes perpDot for point compared to segment.
// A positive value means the point is to the left of the segment,
// negative is to the right, 0 is collinear.
static int compute_side(const SkPoint& s0, const SkPoint& s1, const SkPoint& p) {
SkVector v0 = s1 - s0;
SkVector v1 = p - s0;
SkScalar perpDot = v0.cross(v1);
if (!SkScalarNearlyZero(perpDot)) {
return ((perpDot > 0) ? 1 : -1);
}
return 0;
}
// returns 1 for ccw, -1 for cw and 0 if degenerate
static int get_winding(const SkPoint* polygonVerts, int polygonSize) {
SkPoint p0 = polygonVerts[0];
SkPoint p1 = polygonVerts[1];
for (int i = 2; i < polygonSize; ++i) {
SkPoint p2 = polygonVerts[i];
// determine if cw or ccw
int side = compute_side(p0, p1, p2);
if (0 != side) {
return ((side > 0) ? 1 : -1);
}
// if nearly collinear, treat as straight line and continue
p1 = p2;
}
return 0;
}
// Perpendicularly offset line segment p0-p1 'distance' units in the direction specified by 'dir'
static void inset_edge(const SkPoint& p0, const SkPoint& p1, SkScalar distance, int dir,
InsetSegment* inset) {
SkASSERT(dir == -1 || dir == 1);
// compute perpendicular
SkVector perp;
perp.fX = p0.fY - p1.fY;
perp.fY = p1.fX - p0.fX;
perp.setLength(distance*dir);
inset->fP0 = p0 + perp;
inset->fP1 = p1 + perp;
}
// Compute the intersection 'p' between segments s0 and s1, if any.
// 's' is the parametric value for the intersection along 's0' & 't' is the same for 's1'.
// Returns false if there is no intersection.
static bool compute_intersection(const InsetSegment& s0, const InsetSegment& s1,
SkPoint* p, SkScalar* s, SkScalar* t) {
SkVector v0 = s0.fP1 - s0.fP0;
SkVector v1 = s1.fP1 - s1.fP0;
SkScalar perpDot = v0.cross(v1);
if (SkScalarNearlyZero(perpDot)) {
// segments are parallel
// check if endpoints are touching
if (s0.fP1.equalsWithinTolerance(s1.fP0)) {
*p = s0.fP1;
*s = SK_Scalar1;
*t = 0;
return true;
}
if (s1.fP1.equalsWithinTolerance(s0.fP0)) {
*p = s1.fP1;
*s = 0;
*t = SK_Scalar1;
return true;
}
return false;
}
SkVector d = s1.fP0 - s0.fP0;
SkScalar localS = d.cross(v1) / perpDot;
if (localS < 0 || localS > SK_Scalar1) {
return false;
}
SkScalar localT = d.cross(v0) / perpDot;
if (localT < 0 || localT > SK_Scalar1) {
return false;
}
v0 *= localS;
*p = s0.fP0 + v0;
*s = localS;
*t = localT;
return true;
}
// The objective here is to inset all of the edges by the given distance, and then
// remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
// we should only be making left-hand turns (for cw polygons, we use the winding
// parameter to reverse this). We detect this by checking whether the second intersection
// on an edge is closer to its tail than the first one.
//
// We might also have the case that there is no intersection between two neighboring inset edges.
// In this case, one edge will lie to the right of the other and should be discarded along with
// its previous intersection (if any).
//
// Note: the assumption is that inputPolygon is convex and has no coincident points.
//
bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
SkScalar insetDistance, SkTDArray<SkPoint>* insetPolygon) {
if (inputPolygonSize < 3) {
return false;
}
int winding = get_winding(inputPolygonVerts, inputPolygonSize);
if (0 == winding) {
return false;
}
// set up
struct EdgeData {
InsetSegment fInset;
SkPoint fIntersection;
SkScalar fTValue;
bool fValid;
};
SkAutoSTMalloc<64, EdgeData> edgeData(inputPolygonSize);
for (int i = 0; i < inputPolygonSize; ++i) {
edgeData[i].fValid = true;
int j = (i + 1) % inputPolygonSize;
inset_edge(inputPolygonVerts[i], inputPolygonVerts[j], insetDistance, winding,
&edgeData[i].fInset);
edgeData[i].fTValue = SK_ScalarMin;
}
int prevIndex = inputPolygonSize - 1;
int currIndex = 0;
int insetVertexCount = inputPolygonSize;
while (prevIndex != currIndex) {
if (!edgeData[prevIndex].fValid) {
prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
continue;
}
SkScalar s, t;
SkPoint intersection;
if (compute_intersection(edgeData[prevIndex].fInset, edgeData[currIndex].fInset,
&intersection, &s, &t)) {
// if new intersection is further back on previous inset from the prior intersection
if (s < edgeData[prevIndex].fTValue) {
// no point in considering this one again
edgeData[prevIndex].fValid = false;
--insetVertexCount;
// go back one segment
prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
// we've already considered this intersection, we're done
} else if (edgeData[currIndex].fTValue > SK_ScalarMin &&
intersection.equalsWithinTolerance(edgeData[currIndex].fIntersection,
1.0e-6f)) {
break;
} else {
// add intersection
edgeData[currIndex].fIntersection = intersection;
edgeData[currIndex].fTValue = t;
// go to next segment
prevIndex = currIndex;
currIndex = (currIndex + 1) % inputPolygonSize;
}
} else {
// if prev to right side of curr
int side = winding*compute_side(edgeData[currIndex].fInset.fP0,
edgeData[currIndex].fInset.fP1,
edgeData[prevIndex].fInset.fP1);
if (side < 0 && side == winding*compute_side(edgeData[currIndex].fInset.fP0,
edgeData[currIndex].fInset.fP1,
edgeData[prevIndex].fInset.fP0)) {
// no point in considering this one again
edgeData[prevIndex].fValid = false;
--insetVertexCount;
// go back one segment
prevIndex = (prevIndex + inputPolygonSize - 1) % inputPolygonSize;
} else {
// move to next segment
edgeData[currIndex].fValid = false;
--insetVertexCount;
currIndex = (currIndex + 1) % inputPolygonSize;
}
}
}
// store all the valid intersections
insetPolygon->reset();
insetPolygon->setReserve(insetVertexCount);
for (int i = 0; i < inputPolygonSize; ++i) {
if (edgeData[i].fValid) {
*insetPolygon->push() = edgeData[i].fIntersection;
}
}
#ifdef SK_DEBUG
bool convex = true;
for (int i = 0; i < insetPolygon->count(); ++i) {
int j = (i + 1) % insetPolygon->count();
int k = (i + 2) % insetPolygon->count();
int side = winding*compute_side((*insetPolygon)[i], (*insetPolygon)[j],
(*insetPolygon)[k]);
if (side < 0) {
convex = false;
break;
}
}
SkASSERT(convex);
#endif
return (insetPolygon->count() >= 3);
}

View File

@ -1,27 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkInsetConvexPolygon_DEFINED
#define SkInsetConvexPolygon_DEFINED
#include "SkTDArray.h"
#include "SkPoint.h"
/**
* Generates a polygon that is inset a given distance from the boundary of a given convex polygon.
*
* @param inputPolygonVerts Array of points representing the vertices of the original polygon.
* It should be convex and have no coincident points.
* @param inputPolygonSize Number of vertices in the original polygon.
* @param insetDistance How far we wish to inset the polygon. This should be a positive value.
* @param insetPolygon The resulting inset polygon, if any.
* @return true if an inset polygon exists, false otherwise.
*/
bool SkInsetConvexPolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
SkScalar insetDistance, SkTDArray<SkPoint>* insetPolygon);
#endif

View File

@ -8,7 +8,6 @@
#include "SkShadowTessellator.h"
#include "SkColorPriv.h"
#include "SkGeometry.h"
#include "SkInsetConvexPolygon.h"
#include "SkPath.h"
#include "SkVertices.h"
@ -440,28 +439,22 @@ public:
bool transparent);
private:
void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
SkScalar scale, const SkVector& xlate);
void computeClipVectorsAndTestCentroid();
void computeClipBounds(const SkPath& path, const SkMatrix& ctm, SkPath* devPath);
void checkUmbraAndTransformCentroid(SkScalar scale, const SkVector& xlate,
bool useDistanceToPoint);
bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
int getClosestUmbraPoint(const SkPoint& point);
void handleLine(const SkPoint& p) override;
void handlePolyPoint(const SkPoint& p);
void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
bool addInnerPoint(const SkPoint& pathPoint);
void addInnerPoint(const SkPoint& pathPoint);
void addEdge(const SkVector& nextPoint, const SkVector& nextNormal) override;
SkTDArray<SkPoint> fClipPolygon;
SkTDArray<SkVector> fClipVectors;
SkPoint fCentroid;
SkScalar fArea;
SkTDArray<SkPoint> fPathPolygon;
SkTDArray<SkPoint> fUmbraPolygon;
int fCurrClipPoint;
int fCurrUmbraPoint;
int fCurrPolyPoint;
bool fPrevUmbraOutside;
bool fFirstUmbraOutside;
bool fValidUmbra;
@ -474,11 +467,11 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
SkScalar radius, SkColor umbraColor,
SkColor penumbraColor, bool transparent)
: INHERITED(radius, umbraColor, penumbraColor, transparent)
, fCurrClipPoint(0)
, fCurrPolyPoint(0)
, fPrevUmbraOutside(false)
, fFirstUmbraOutside(false)
, fValidUmbra(true) {
// TODO: calculate these reserves better
// TODO: calculate these better
// Penumbra ring: 3*numPts
// Umbra ring: numPts
// Inner ring: numPts
@ -487,65 +480,61 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
// Penumbra ring: 12*numPts
// Umbra ring: 3*numPts
fIndices.setReserve(15 * path.countPoints());
fClipPolygon.setReserve(path.countPoints());
// compute rough clip bounds for umbra, plus offset polygon, plus centroid
this->computeClipAndPathPolygons(path, ctm, scale, translate);
if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) {
fClipPolygon.setReserve(path.countPoints());
// compute rough clip bounds for umbra, plus centroid
SkPath devPath;
this->computeClipBounds(path, ctm, &devPath);
if (fClipPolygon.count() < 3) {
return;
}
// We are going to apply 'scale' and 'xlate' (in that order) to each computed path point. We
// want the effect to be to scale the points relative to the path centroid and then translate
// them by the 'translate' param we were passed.
SkVector xlate = fCentroid * (1.f - scale) + translate;
// check to see if umbra collapses
SkScalar minDistSq = fCentroid.distanceToLineSegmentBetweenSqd(fPathPolygon[0],
fPathPolygon[1]);
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 = fCentroid.distanceToLineSegmentBetweenSqd(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;
SkScalar ratio = 256 * newRadius / radius;
// they aren't PMColors, but the interpolation algorithm is the same
fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
radius = newRadius;
}
// check to see if we have a valid umbra at all
bool usePointCheck = path.isRRect(nullptr) || path.isRect(nullptr) || path.isOval(nullptr);
this->checkUmbraAndTransformCentroid(scale, translate, usePointCheck);
// 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
// walk around the path, tessellate and generate inner and outer rings
SkPath::Iter iter(devPath, true);
SkPoint pts[4];
SkPath::Verb verb;
if (fTransparent) {
*fPositions.push() = fCentroid;
*fColors.push() = fUmbraColor;
}
fCurrUmbraPoint = 0;
for (int i = 0; i < fPathPolygon.count(); ++i) {
this->handlePolyPoint(fPathPolygon[i]);
SkMatrix shadowTransform;
shadowTransform.setScaleTranslate(scale, scale, xlate.fX, xlate.fY);
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kLine_Verb:
this->INHERITED::handleLine(shadowTransform, &pts[1]);
break;
case SkPath::kQuad_Verb:
this->handleQuad(shadowTransform, pts);
break;
case SkPath::kCubic_Verb:
this->handleCubic(shadowTransform, pts);
break;
case SkPath::kConic_Verb:
this->handleConic(shadowTransform, pts, iter.conicWeight());
break;
case SkPath::kMove_Verb:
case SkPath::kClose_Verb:
case SkPath::kDone_Verb:
break;
}
}
if (!this->indexCount()) {
return;
}
// finish up the final verts
SkVector normal;
if (compute_normal(fPrevPoint, fFirstPoint, fRadius, fDirection, &normal)) {
if (compute_normal(fPrevPoint, fFirstPoint, fRadius, fDirection,
&normal)) {
this->addArc(normal);
// close out previous arc
@ -611,138 +600,175 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMat
fSucceeded = true;
}
void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm,
SkScalar scale, const SkVector& xlate) {
// For the path polygon we are going to apply 'scale' and 'xlate' (in that order) to each
// computed path point. We want the effect to be to scale the points relative to the path
// bounds center and then translate them by the 'xlate' param we were passed.
SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
ctm.mapPoints(&center, 1);
SkVector translate = center * (1.f - scale) + xlate;
SkMatrix shadowTransform;
shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY);
fPathPolygon.setReserve(path.countPoints());
// Walk around the path and compute clip polygon and path polygon.
// Will also accumulate sum of areas for centroid.
// For Bezier curves, we compute additional interior points on curve.
void SkSpotShadowTessellator::computeClipBounds(const SkPath& path, const SkMatrix& ctm,
SkPath* devPath) {
// walk around the path and compute clip polygon
// if original path is transparent, will accumulate sum of points for centroid
// for Bezier curves, we compute additional interior points on curve
SkPath::Iter iter(path, true);
SkPoint pts[4];
SkPath::Verb verb;
fCentroid = SkPoint::Make(0, 0);
int centroidCount = 0;
fClipPolygon.reset();
// init centroid
fCentroid = SkPoint::Make(0, 0);
fArea = 0;
// coefficients to compute cubic Bezier at t = 5/16
static constexpr SkScalar kA = 0.32495117187f;
static constexpr SkScalar kB = 0.44311523437f;
static constexpr SkScalar kC = 0.20141601562f;
static constexpr SkScalar kD = 0.03051757812f;
const SkScalar kA = 0.32495117187f;
const SkScalar kB = 0.44311523437f;
const SkScalar kC = 0.20141601562f;
const SkScalar kD = 0.03051757812f;
SkPoint curvePoint;
SkScalar w;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
ctm.mapPoints(&pts[0], 1);
devPath->moveTo(pts[0]);
break;
case SkPath::kLine_Verb:
ctm.mapPoints(&pts[1], 1);
devPath->lineTo(pts[1]);
fCentroid += pts[1];
centroidCount++;
*fClipPolygon.push() = pts[1];
this->INHERITED::handleLine(shadowTransform, &pts[1]);
break;
case SkPath::kQuad_Verb:
ctm.mapPoints(pts, 3);
devPath->quadTo(pts[1], pts[2]);
// point at t = 1/2
curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
*fClipPolygon.push() = curvePoint;
fCentroid += curvePoint;
*fClipPolygon.push() = pts[2];
this->handleQuad(shadowTransform, pts);
fCentroid += pts[2];
centroidCount += 2;
break;
case SkPath::kConic_Verb:
ctm.mapPoints(pts, 3);
w = iter.conicWeight();
devPath->conicTo(pts[1], pts[2], w);
// point at t = 1/2
curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
*fClipPolygon.push() = curvePoint;
fCentroid += curvePoint;
*fClipPolygon.push() = pts[2];
this->handleConic(shadowTransform, pts, w);
fCentroid += pts[2];
centroidCount += 2;
break;
case SkPath::kCubic_Verb:
ctm.mapPoints(pts, 4);
devPath->cubicTo(pts[1], pts[2], pts[3]);
// point at t = 5/16
curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
*fClipPolygon.push() = curvePoint;
fCentroid += curvePoint;
// point at t = 11/16
curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX;
curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY;
*fClipPolygon.push() = curvePoint;
fCentroid += curvePoint;
*fClipPolygon.push() = pts[3];
this->handleCubic(shadowTransform, pts);
fCentroid += pts[3];
centroidCount += 3;
break;
case SkPath::kMove_Verb:
case SkPath::kClose_Verb:
case SkPath::kDone_Verb:
devPath->close();
break;
default:
SkDEBUGFAIL("unknown verb");
}
}
// finish centroid
if (fPathPolygon.count() > 0) {
SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1];
SkPoint nextPoint = fPathPolygon[0];
SkScalar quadArea = currPoint.cross(nextPoint);
fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea;
fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea;
fArea += quadArea;
fCentroid *= SK_Scalar1 / (3 * fArea);
}
fCurrClipPoint = fClipPolygon.count() - 1;
fCentroid *= SkScalarInvert(centroidCount);
fCurrPolyPoint = fClipPolygon.count() - 1;
}
void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() {
void SkSpotShadowTessellator::checkUmbraAndTransformCentroid(SkScalar scale,
const SkVector& xlate,
bool useDistanceToPoint) {
SkASSERT(fClipPolygon.count() >= 3);
SkPoint transformedCentroid = fCentroid;
transformedCentroid += xlate;
// init clip vectors
SkScalar localRadius = fRadius / scale;
localRadius *= localRadius;
// init umbra check
SkVector w = fCentroid - fClipPolygon[0];
SkVector v0 = fClipPolygon[1] - fClipPolygon[0];
*fClipVectors.push() = v0;
bool validUmbra;
SkScalar minDistance;
// check distance against line segment
if (useDistanceToPoint) {
minDistance = w.lengthSqd();
} else {
SkScalar vSq = v0.dot(v0);
SkScalar wDotV = w.dot(v0);
minDistance = w.dot(w) - wDotV*wDotV/vSq;
}
validUmbra = (minDistance >= localRadius);
// init centroid check
bool hiddenCentroid = true;
SkVector v1 = fCentroid - fClipPolygon[0];
SkVector v1 = transformedCentroid - fClipPolygon[0];
SkScalar initCross = v0.cross(v1);
for (int p = 1; p < fClipPolygon.count(); ++p) {
// add to clip vectors
// Determine whether we have a real umbra by insetting clipPolygon by radius/scale
// and see if it extends past centroid.
// TODO: adjust this later for more accurate umbra calcs
w = fCentroid - fClipPolygon[p];
v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p];
*fClipVectors.push() = v0;
// check distance against line segment
SkScalar distance;
if (useDistanceToPoint) {
distance = w.lengthSqd();
} else {
SkScalar vSq = v0.dot(v0);
SkScalar wDotV = w.dot(v0);
distance = w.dot(w) - wDotV*wDotV/vSq;
}
if (distance < localRadius) {
validUmbra = false;
}
if (distance < minDistance) {
minDistance = distance;
}
// Determine if transformed centroid is inside clipPolygon.
v1 = fCentroid - fClipPolygon[p];
v1 = transformedCentroid - fClipPolygon[p];
if (initCross*v0.cross(v1) <= 0) {
hiddenCentroid = false;
}
}
SkASSERT(fClipVectors.count() == fClipPolygon.count());
fTransparent = fTransparent || !hiddenCentroid;
if (!validUmbra) {
SkScalar ratio = 256 * SkScalarSqrt(minDistance / localRadius);
// they aren't PMColors, but the interpolation algorithm is the same
fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio);
}
fTransparent = fTransparent || !hiddenCentroid || !validUmbra;
fValidUmbra = validUmbra;
fCentroid = transformedCentroid;
}
bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid,
SkPoint* clipPoint) {
SkVector segmentVector = centroid - umbraPoint;
int startClipPoint = fCurrClipPoint;
int startPolyPoint = fCurrPolyPoint;
do {
SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint];
SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector);
SkVector dp = umbraPoint - fClipPolygon[fCurrPolyPoint];
SkScalar denom = fClipVectors[fCurrPolyPoint].cross(segmentVector);
SkScalar t_num = dp.cross(segmentVector);
// if line segments are nearly parallel
if (SkScalarNearlyZero(denom)) {
@ -753,7 +779,7 @@ bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const Sk
// otherwise are separate, will try the next poly segment
// else if crossing lies within poly segment
} else if (t_num >= 0 && t_num <= denom) {
SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]);
SkScalar s_num = dp.cross(fClipVectors[fCurrPolyPoint]);
// if umbra point is inside the clip polygon
if (s_num < 0) {
return false;
@ -763,41 +789,12 @@ bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const Sk
return true;
}
}
fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count();
} while (fCurrClipPoint != startClipPoint);
fCurrPolyPoint = (fCurrPolyPoint + 1) % fClipPolygon.count();
} while (fCurrPolyPoint != startPolyPoint);
return false;
}
int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) {
SkScalar minDistance = p.distanceToSqd(fUmbraPolygon[fCurrUmbraPoint]);
int index = fCurrUmbraPoint;
int dir = 1;
int next = (index + dir) % fUmbraPolygon.count();
// init travel direction
SkScalar distance = p.distanceToSqd(fUmbraPolygon[next]);
if (distance < minDistance) {
index = next;
minDistance = distance;
} else {
dir = fUmbraPolygon.count()-1;
}
// iterate until we find a point that increases the distance
next = (index + dir) % fUmbraPolygon.count();
distance = p.distanceToSqd(fUmbraPolygon[next]);
while (distance < minDistance) {
index = next;
minDistance = distance;
next = (index + dir) % fUmbraPolygon.count();
distance = p.distanceToSqd(fUmbraPolygon[next]);
}
fCurrUmbraPoint = index;
return index;
}
void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
SkPoint* pts, int count) {
// TODO: vectorize
@ -807,44 +804,7 @@ void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
}
}
static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
static constexpr SkScalar kClose = (SK_Scalar1 / 16);
static constexpr SkScalar kCloseSqd = kClose*kClose;
SkScalar distSq = p0.distanceToSqd(p1);
return distSq < kCloseSqd;
}
static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
SkVector v0 = p1 - p0;
SkVector v1 = p2 - p0;
return (SkScalarNearlyZero(v0.cross(v1)));
}
void SkSpotShadowTessellator::handleLine(const SkPoint& p) {
// remove coincident points and add to centroid
if (fPathPolygon.count() > 0) {
const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1];
if (duplicate_pt(p, lastPoint)) {
return;
}
SkScalar quadArea = lastPoint.cross(p);
fCentroid.fX += (p.fX + lastPoint.fX) * quadArea;
fCentroid.fY += (p.fY + lastPoint.fY) * quadArea;
fArea += quadArea;
}
// try to remove collinear points
if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2],
fPathPolygon[fPathPolygon.count()-1],
p)) {
fPathPolygon[fPathPolygon.count() - 1] = p;
} else {
*fPathPolygon.push() = p;
}
}
void SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
if (fInitPoints.count() < 2) {
*fInitPoints.push() = p;
return;
@ -854,7 +814,7 @@ void SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
// determine if cw or ccw
SkVector v0 = fInitPoints[1] - fInitPoints[0];
SkVector v1 = p - fInitPoints[0];
SkScalar perpDot = v0.cross(v1);
SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
if (SkScalarNearlyZero(perpDot)) {
// nearly parallel, just treat as straight line and continue
fInitPoints[1] = p;
@ -907,47 +867,40 @@ void SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
}
}
bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
SkPoint umbraPoint;
if (!fValidUmbra) {
SkVector v = fCentroid - pathPoint;
v *= 0.95f;
umbraPoint = pathPoint + v;
void SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
SkVector v = fCentroid - pathPoint;
SkScalar distance = v.length();
SkScalar t;
if (fValidUmbra) {
SkASSERT(distance >= fRadius);
t = fRadius / distance;
} else {
umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
t = 0.95f;
}
v *= t;
SkPoint umbraPoint = pathPoint + v;
*fPositions.push() = umbraPoint;
*fColors.push() = fUmbraColor;
fPrevPoint = pathPoint;
// merge "close" points
if (fPrevUmbraIndex == fFirstVertex ||
!duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
*fPositions.push() = umbraPoint;
*fColors.push() = fUmbraColor;
return false;
} else {
return true;
}
}
void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
// add next umbra point
bool duplicate = this->addInnerPoint(nextPoint);
int prevPenumbraIndex = duplicate ? fPositions.count()-1 : fPositions.count()-2;
int currUmbraIndex = duplicate ? fPrevUmbraIndex : fPositions.count()-1;
this->addInnerPoint(nextPoint);
int prevPenumbraIndex = fPositions.count() - 2;
int currUmbraIndex = fPositions.count() - 1;
if (!duplicate) {
// add to center fan if transparent or centroid showing
if (fTransparent) {
*fIndices.push() = 0;
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = currUmbraIndex;
// otherwise add to clip ring
} else {
// add to center fan if transparent or centroid showing
if (fTransparent) {
*fIndices.push() = 0;
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = currUmbraIndex;
// otherwise add to clip ring
} else {
if (!fTransparent) {
SkPoint clipPoint;
bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid,
&clipPoint);
bool isOutside = clipUmbraPoint(fPositions[currUmbraIndex], fCentroid, &clipPoint);
if (isOutside) {
*fPositions.push() = clipPoint;
*fColors.push() = fUmbraColor;
@ -976,11 +929,9 @@ void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector&
*fPositions.push() = newPoint;
*fColors.push() = fPenumbraColor;
if (!duplicate) {
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = prevPenumbraIndex;
*fIndices.push() = currUmbraIndex;
}
*fIndices.push() = fPrevUmbraIndex;
*fIndices.push() = prevPenumbraIndex;
*fIndices.push() = currUmbraIndex;
*fIndices.push() = prevPenumbraIndex;
*fIndices.push() = fPositions.count() - 1;

View File

@ -1,132 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
#include "SkInsetConvexPolygon.h"
static bool is_convex(const SkTDArray<SkPoint>& poly) {
if (poly.count() < 3) {
return false;
}
SkVector v0 = poly[0] - poly[poly.count() - 1];
SkVector v1 = poly[1] - poly[poly.count() - 1];
SkScalar winding = v0.cross(v1);
for (int i = 0; i < poly.count()-1; ++i) {
int j = i + 1;
int k = (i + 2) % poly.count();
SkVector v0 = poly[j] - poly[i];
SkVector v1 = poly[k] - poly[i];
SkScalar perpDot = v0.cross(v1);
int side = winding*perpDot;
if (side < 0) {
return false;
}
}
return true;
}
DEF_TEST(InsetConvexPoly, reporter) {
SkTDArray<SkPoint> rrectPoly;
// round rect
*rrectPoly.push() = SkPoint::Make(-100, 55);
*rrectPoly.push() = SkPoint::Make(100, 55);
*rrectPoly.push() = SkPoint::Make(100 + 2.5f, 50 + 4.330127f);
*rrectPoly.push() = SkPoint::Make(100 + 3.535534f, 50 + 3.535534f);
*rrectPoly.push() = SkPoint::Make(100 + 4.330127f, 50 + 2.5f);
*rrectPoly.push() = SkPoint::Make(105, 50);
*rrectPoly.push() = SkPoint::Make(105, -50);
*rrectPoly.push() = SkPoint::Make(100 + 4.330127f, -50 - 2.5f);
*rrectPoly.push() = SkPoint::Make(100 + 3.535534f, -50 - 3.535534f);
*rrectPoly.push() = SkPoint::Make(100 + 2.5f, -50 - 4.330127f);
*rrectPoly.push() = SkPoint::Make(100, -55);
*rrectPoly.push() = SkPoint::Make(-100, -55);
*rrectPoly.push() = SkPoint::Make(-100 - 2.5f, -50 - 4.330127f);
*rrectPoly.push() = SkPoint::Make(-100 - 3.535534f, -50 - 3.535534f);
*rrectPoly.push() = SkPoint::Make(-100 - 4.330127f, -50 - 2.5f);
*rrectPoly.push() = SkPoint::Make(-105, -50);
*rrectPoly.push() = SkPoint::Make(-105, 50);
*rrectPoly.push() = SkPoint::Make(-100 - 4.330127f, 50 + 2.5f);
*rrectPoly.push() = SkPoint::Make(-100 - 3.535534f, 50 + 3.535534f);
*rrectPoly.push() = SkPoint::Make(-100 - 2.5f, 50 + 4.330127f);
REPORTER_ASSERT(reporter, is_convex(rrectPoly));
// inset a little
SkTDArray<SkPoint> insetPoly;
bool result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 3, &insetPoly);
REPORTER_ASSERT(reporter, result);
REPORTER_ASSERT(reporter, is_convex(insetPoly));
// inset to rect
result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 10, &insetPoly);
REPORTER_ASSERT(reporter, result);
REPORTER_ASSERT(reporter, is_convex(insetPoly));
REPORTER_ASSERT(reporter, insetPoly.count() == 4);
if (insetPoly.count() == 4) {
REPORTER_ASSERT(reporter, insetPoly[0].equals(-95, 45));
REPORTER_ASSERT(reporter, insetPoly[1].equals(95, 45));
REPORTER_ASSERT(reporter, insetPoly[2].equals(95, -45));
REPORTER_ASSERT(reporter, insetPoly[3].equals(-95, -45));
}
// just to full inset
// for shadows having a flat poly here is fine
// may want to revisit for strokes
result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 55, &insetPoly);
REPORTER_ASSERT(reporter, result);
REPORTER_ASSERT(reporter, is_convex(insetPoly));
REPORTER_ASSERT(reporter, insetPoly.count() == 4);
if (insetPoly.count() == 4) {
REPORTER_ASSERT(reporter, insetPoly[0].equals(-50, 0));
REPORTER_ASSERT(reporter, insetPoly[1].equals(50, 0));
REPORTER_ASSERT(reporter, insetPoly[2].equals(50, 0));
REPORTER_ASSERT(reporter, insetPoly[3].equals(-50, 0));
}
// past full inset
result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 75, &insetPoly);
REPORTER_ASSERT(reporter, !result);
// troublesome case
SkTDArray<SkPoint> clippedRRectPoly;
*clippedRRectPoly.push() = SkPoint::Make(335.928101f, 428.219055f);
*clippedRRectPoly.push() = SkPoint::Make(330.414459f, 423.034912f);
*clippedRRectPoly.push() = SkPoint::Make(325.749084f, 417.395508f);
*clippedRRectPoly.push() = SkPoint::Make(321.931946f, 411.300842f);
*clippedRRectPoly.push() = SkPoint::Make(318.963074f, 404.750977f);
*clippedRRectPoly.push() = SkPoint::Make(316.842468f, 397.745850f);
*clippedRRectPoly.push() = SkPoint::Make(315.570068f, 390.285522f);
*clippedRRectPoly.push() = SkPoint::Make(315.145966f, 382.369965f);
*clippedRRectPoly.push() = SkPoint::Make(315.570068f, 374.454346f);
*clippedRRectPoly.push() = SkPoint::Make(316.842468f, 366.994019f);
*clippedRRectPoly.push() = SkPoint::Make(318.963074f, 359.988892f);
*clippedRRectPoly.push() = SkPoint::Make(321.931946f, 353.439056f);
*clippedRRectPoly.push() = SkPoint::Make(325.749084f, 347.344421f);
*clippedRRectPoly.push() = SkPoint::Make(330.414459f, 341.705017f);
*clippedRRectPoly.push() = SkPoint::Make(335.928101f, 336.520813f);
*clippedRRectPoly.push() = SkPoint::Make(342.289948f, 331.791901f);
*clippedRRectPoly.push() = SkPoint::Make(377.312134f, 331.791901f);
*clippedRRectPoly.push() = SkPoint::Make(381.195313f, 332.532593f);
*clippedRRectPoly.push() = SkPoint::Make(384.464935f, 334.754700f);
*clippedRRectPoly.push() = SkPoint::Make(386.687042f, 338.024292f);
*clippedRRectPoly.push() = SkPoint::Make(387.427765f, 341.907532f);
*clippedRRectPoly.push() = SkPoint::Make(387.427765f, 422.832367f);
*clippedRRectPoly.push() = SkPoint::Make(386.687042f, 426.715576f);
*clippedRRectPoly.push() = SkPoint::Make(384.464935f, 429.985168f);
*clippedRRectPoly.push() = SkPoint::Make(381.195313f, 432.207275f);
*clippedRRectPoly.push() = SkPoint::Make(377.312134f, 432.947998f);
*clippedRRectPoly.push() = SkPoint::Make(342.289948f, 432.947998f);
REPORTER_ASSERT(reporter, is_convex(clippedRRectPoly));
result = SkInsetConvexPolygon(&clippedRRectPoly[0], clippedRRectPoly.count(), 32.3699417f,
&insetPoly);
REPORTER_ASSERT(reporter, result);
REPORTER_ASSERT(reporter, is_convex(insetPoly));
}