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:
parent
adf7b330bd
commit
e7c85c45c4
@ -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,7 +22,27 @@ static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace ConvexLineOnlyData {
|
||||
namespace skiagm {
|
||||
|
||||
// This GM is intended to exercise Ganesh's handling of convex line-only
|
||||
// paths
|
||||
class ConvexLineOnlyPathsGM : public GM {
|
||||
public:
|
||||
ConvexLineOnlyPathsGM(bool doStrokeAndFill) : fDoStrokeAndFill(doStrokeAndFill) {
|
||||
this->setBGColor(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override {
|
||||
if (fDoStrokeAndFill) {
|
||||
return SkString("convex-lineonly-paths-stroke-and-fill");
|
||||
}
|
||||
return SkString("convex-lineonly-paths");
|
||||
}
|
||||
SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
|
||||
bool runAsBench() const override { return true; }
|
||||
|
||||
static SkPath GetPath(int index, int offset, SkPath::Direction dir) {
|
||||
// narrow rect
|
||||
const SkPoint gPoints0[] = {
|
||||
{ -1.5f, -50.0f },
|
||||
@ -150,41 +169,19 @@ const size_t gSizes[] = {
|
||||
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
|
||||
// paths
|
||||
class ConvexLineOnlyPathsGM : public GM {
|
||||
public:
|
||||
ConvexLineOnlyPathsGM(bool doStrokeAndFill) : fDoStrokeAndFill(doStrokeAndFill) {
|
||||
this->setBGColor(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override {
|
||||
if (fDoStrokeAndFill) {
|
||||
return SkString("convex-lineonly-paths-stroke-and-fill");
|
||||
}
|
||||
return SkString("convex-lineonly-paths");
|
||||
}
|
||||
SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
|
||||
bool runAsBench() const override { return true; }
|
||||
|
||||
static SkPath GetPath(int index, SkPath::Direction dir) {
|
||||
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();)
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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(¢er, 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);
|
||||
fCentroid *= SkScalarInvert(centroidCount);
|
||||
fCurrPolyPoint = fClipPolygon.count() - 1;
|
||||
}
|
||||
|
||||
fCurrClipPoint = 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,37 +867,30 @@ void SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
|
||||
SkPoint umbraPoint;
|
||||
if (!fValidUmbra) {
|
||||
void SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
|
||||
SkVector v = fCentroid - pathPoint;
|
||||
v *= 0.95f;
|
||||
umbraPoint = pathPoint + v;
|
||||
SkScalar distance = v.length();
|
||||
SkScalar t;
|
||||
if (fValidUmbra) {
|
||||
SkASSERT(distance >= fRadius);
|
||||
t = fRadius / distance;
|
||||
} else {
|
||||
umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)];
|
||||
t = 0.95f;
|
||||
}
|
||||
|
||||
fPrevPoint = pathPoint;
|
||||
|
||||
// merge "close" points
|
||||
if (fPrevUmbraIndex == fFirstVertex ||
|
||||
!duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) {
|
||||
v *= t;
|
||||
SkPoint umbraPoint = pathPoint + v;
|
||||
*fPositions.push() = umbraPoint;
|
||||
*fColors.push() = fUmbraColor;
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
fPrevPoint = pathPoint;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -945,9 +898,9 @@ void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector&
|
||||
*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() = prevPenumbraIndex;
|
||||
*fIndices.push() = fPositions.count() - 1;
|
||||
|
@ -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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user