aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/convex_all_line_paths.cpp436
-rw-r--r--gm/shadowutils.cpp3
-rw-r--r--gn/tests.gni1
-rw-r--r--gn/utils.gni2
-rw-r--r--samplecode/SampleAndroidShadows.cpp12
-rwxr-xr-xsrc/utils/SkInsetConvexPolygon.cpp233
-rwxr-xr-xsrc/utils/SkInsetConvexPolygon.h27
-rw-r--r--src/utils/SkShadowTessellator.cpp379
-rw-r--r--tests/InsetConvexPolyTest.cpp132
9 files changed, 306 insertions, 919 deletions
diff --git a/gm/convex_all_line_paths.cpp b/gm/convex_all_line_paths.cpp
index a4bc6a66cb..62e8835831 100644
--- a/gm/convex_all_line_paths.cpp
+++ b/gm/convex_all_line_paths.cpp
@@ -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();)
}
diff --git a/gm/shadowutils.cpp b/gm/shadowutils.cpp
index 53d9b78602..350a546f0a 100644
--- a/gm/shadowutils.cpp
+++ b/gm/shadowutils.cpp
@@ -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};
diff --git a/gn/tests.gni b/gn/tests.gni
index 44351121d2..124be390cf 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -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",
diff --git a/gn/utils.gni b/gn/utils.gni
index bb0bd94ee4..807ae587b1 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -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",
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index 271004ed10..fa9cb50c7b 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -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;
}
diff --git a/src/utils/SkInsetConvexPolygon.cpp b/src/utils/SkInsetConvexPolygon.cpp
deleted file mode 100755
index 8df7f0efe6..0000000000
--- a/src/utils/SkInsetConvexPolygon.cpp
+++ /dev/null
@@ -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);
-}
diff --git a/src/utils/SkInsetConvexPolygon.h b/src/utils/SkInsetConvexPolygon.h
deleted file mode 100755
index 3ab7558a25..0000000000
--- a/src/utils/SkInsetConvexPolygon.h
+++ /dev/null
@@ -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
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index ef8ce3212f..ed9e62af7a 100644
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -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;
- }
-
- // 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;
- }
+ // 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);
- // 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;
- fClipPolygon.reset();
-
- // init centroid
fCentroid = SkPoint::Make(0, 0);
- fArea = 0;
+ int centroidCount = 0;
+ fClipPolygon.reset();
// 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;
diff --git a/tests/InsetConvexPolyTest.cpp b/tests/InsetConvexPolyTest.cpp
deleted file mode 100644
index e13a25a70f..0000000000
--- a/tests/InsetConvexPolyTest.cpp
+++ /dev/null
@@ -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));
-}