aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkPath.h55
-rw-r--r--samplecode/SampleTests.cpp99
-rw-r--r--src/core/SkPath.cpp139
-rw-r--r--tests/PathTest.cpp65
4 files changed, 299 insertions, 59 deletions
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 2ebc59d3eb..18dcd11070 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -96,19 +96,54 @@ public:
GEN_ID_INC;
}
- /** Returns true if the path is flagged as being convex. This is not a
- confirmed by any analysis, it is just the value set earlier.
+ enum Convexity {
+ kUnknown_Convexity,
+ kConvex_Convexity,
+ kConcave_Convexity
+ };
+
+ /**
+ * Return the path's convexity, as stored in the path.
+ */
+ Convexity getConvexity() const { return (Convexity)fConvexity; }
+
+ /**
+ * Store a convexity setting in the path. There is no automatic check to
+ * see if this value actually agress with the return value from
+ * ComputeConvexity().
+ */
+ void setConvexity(Convexity);
+
+ /**
+ * Compute the convexity of the specified path. This does not look at the
+ * value stored in the path, but computes it directly from the path's data.
+ *
+ * If there is more than one contour, this returns kConcave_Convexity.
+ * If the contour is degenerate (i.e. all segements are colinear,
+ * then this returns kUnknown_Convexity.
+ * The contour is treated as if it were closed, even if there is no kClose
+ * verb.
*/
- bool isConvex() const { return fIsConvex != 0; }
+ static Convexity ComputeConvexity(const SkPath&);
- /** Set the isConvex flag to true or false. Convex paths may draw faster if
- this flag is set, though setting this to true on a path that is in fact
- not convex can give undefined results when drawn. Paths default to
- isConvex == false
+ /**
+ * DEPRECATED: use getConvexity()
+ * Returns true if the path is flagged as being convex. This is not a
+ * confirmed by any analysis, it is just the value set earlier.
+ */
+ bool isConvex() const {
+ return kConvex_Convexity == this->getConvexity();
+ }
+
+ /**
+ * DEPRECATED: use setConvexity()
+ * Set the isConvex flag to true or false. Convex paths may draw faster if
+ * this flag is set, though setting this to true on a path that is in fact
+ * not convex can give undefined results when drawn. Paths default to
+ * isConvex == false
*/
void setIsConvex(bool isConvex) {
- fIsConvex = (isConvex != 0);
- GEN_ID_INC;
+ this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
}
/** Clear any lines and curves from the path, making it empty. This frees up
@@ -600,7 +635,7 @@ private:
mutable SkRect fBounds;
mutable uint8_t fBoundsIsDirty;
uint8_t fFillType;
- uint8_t fIsConvex;
+ uint8_t fConvexity;
#ifdef ANDROID
uint32_t fGenerationID;
#endif
diff --git a/samplecode/SampleTests.cpp b/samplecode/SampleTests.cpp
index c05189d0bb..a97c622726 100644
--- a/samplecode/SampleTests.cpp
+++ b/samplecode/SampleTests.cpp
@@ -1,32 +1,60 @@
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
-#include "SkBlurMaskFilter.h"
-#include "SkCamera.h"
-#include "SkColorFilter.h"
-#include "SkColorPriv.h"
-#include "SkDevice.h"
-#include "SkGradientShader.h"
-#include "SkImageDecoder.h"
-#include "SkInterpolator.h"
-#include "SkMaskFilter.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkShaderExtras.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkUtils.h"
-#include "SkKey.h"
-#include "SkXfermode.h"
-#include "SkDrawFilter.h"
#include "test.h"
-class TestsView : public SkView {
+namespace skiatest {
+
+class MyReporter : public Reporter {
+protected:
+ virtual void onStart(Test* test) {}
+ virtual void onReport(const char desc[], Reporter::Result result) {
+ SkASSERT(Reporter::kPassed == result);
+ }
+ virtual void onEnd(Test* test) {}
+};
+
+class Iter {
public:
- skia::Test::Iter fIter;
+ Iter(Reporter* r) : fReporter(r) {
+ r->ref();
+ fReg = TestRegistry::Head();
+ }
+
+ ~Iter() {
+ fReporter->unref();
+ }
+
+ Test* next() {
+ if (fReg) {
+ TestRegistry::Factory fact = fReg->factory();
+ fReg = fReg->next();
+ Test* test = fact(NULL);
+ test->setReporter(fReporter);
+ return test;
+ }
+ return NULL;
+ }
+
+ static int Count() {
+ const TestRegistry* reg = TestRegistry::Head();
+ int count = 0;
+ while (reg) {
+ count += 1;
+ reg = reg->next();
+ }
+ return count;
+ }
+
+private:
+ Reporter* fReporter;
+ const TestRegistry* fReg;
+};
+}
+class TestsView : public SkView {
+public:
TestsView() {}
protected:
@@ -45,30 +73,15 @@ protected:
virtual void onDraw(SkCanvas* canvas) {
this->drawBG(canvas);
+
+ skiatest::MyReporter reporter;
+ skiatest::Iter iter(&reporter);
+ skiatest::Test* test;
- skia::Test* test = fIter.next();
- if (NULL == test) {
- fIter.reset();
- test = fIter.next();
+ while ((test = iter.next()) != NULL) {
+ test->run();
+ SkDELETE(test);
}
-
- SkIPoint size;
- test->getSize(&size);
-
- SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.fX, size.fY);
- bitmap.allocPixels();
- bitmap.eraseColor(0);
-
- SkCanvas c(bitmap);
- test->draw(&c);
-
- canvas->drawBitmap(bitmap, SkIntToScalar(10), SkIntToScalar(10), NULL);
-
- SkString str;
- test->getString(skia::Test::kTitle, &str);
- SkDebugf("--- %s\n", str.c_str());
- delete test;
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 704863cc87..fa6b3b9f66 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -97,7 +97,7 @@ static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
////////////////////////////////////////////////////////////////////////////
SkPath::SkPath() : fBoundsIsDirty(true), fFillType(kWinding_FillType) {
- fIsConvex = false; // really should be kUnknown
+ fConvexity = kUnknown_Convexity;
#ifdef ANDROID
fGenerationID = 0;
#endif
@@ -125,7 +125,7 @@ SkPath& SkPath::operator=(const SkPath& src) {
fVerbs = src.fVerbs;
fFillType = src.fFillType;
fBoundsIsDirty = src.fBoundsIsDirty;
- fIsConvex = src.fIsConvex;
+ fConvexity = src.fConvexity;
GEN_ID_INC;
}
SkDEBUGCODE(this->validate();)
@@ -148,7 +148,7 @@ void SkPath::swap(SkPath& other) {
fVerbs.swap(other.fVerbs);
SkTSwap<uint8_t>(fFillType, other.fFillType);
SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
- SkTSwap<uint8_t>(fIsConvex, other.fIsConvex);
+ SkTSwap<uint8_t>(fConvexity, other.fConvexity);
GEN_ID_INC;
}
}
@@ -166,7 +166,7 @@ void SkPath::reset() {
fVerbs.reset();
GEN_ID_INC;
fBoundsIsDirty = true;
- fIsConvex = false; // really should be kUnknown
+ fConvexity = kUnknown_Convexity;
}
void SkPath::rewind() {
@@ -176,7 +176,7 @@ void SkPath::rewind() {
fVerbs.rewind();
GEN_ID_INC;
fBoundsIsDirty = true;
- fIsConvex = false; // really should be kUnknown
+ fConvexity = kUnknown_Convexity;
}
bool SkPath::isEmpty() const {
@@ -244,6 +244,13 @@ void SkPath::computeBounds() const {
compute_pt_bounds(&fBounds, fPts);
}
+void SkPath::setConvexity(Convexity c) {
+ if (fConvexity != c) {
+ fConvexity = c;
+ GEN_ID_INC;
+ }
+}
+
//////////////////////////////////////////////////////////////////////////////
// Construction methods
@@ -1382,3 +1389,125 @@ void SkPath::validate() const {
}
#endif
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Returns -1 || 0 || 1 depending on the sign of value:
+ * -1 if value < 0
+ * 0 if vlaue == 0
+ * 1 if value > 0
+ */
+static int SkScalarSign(SkScalar value) {
+ return value < 0 ? -1 : (value > 0);
+}
+
+static int CrossProductSign(const SkVector& a, const SkVector& b) {
+ return SkScalarSign(SkPoint::CrossProduct(a, b));
+}
+
+// only valid for a single contour
+struct Convexicator {
+ Convexicator() : fPtCount(0), fConvexity(SkPath::kUnknown_Convexity) {
+ fSign = 0;
+ // warnings
+ fCurrPt.set(0, 0);
+ fVec0.set(0, 0);
+ fVec1.set(0, 0);
+ fFirstVec.set(0, 0);
+ }
+
+ SkPath::Convexity getConvexity() const { return fConvexity; }
+
+ void addPt(const SkPoint& pt) {
+ if (SkPath::kConcave_Convexity == fConvexity) {
+ return;
+ }
+
+ if (0 == fPtCount) {
+ fCurrPt = pt;
+ ++fPtCount;
+ } else {
+ SkVector vec = pt - fCurrPt;
+ if (vec.fX || vec.fY) {
+ fCurrPt = pt;
+ if (++fPtCount == 2) {
+ fFirstVec = fVec1 = vec;
+ } else {
+ SkASSERT(fPtCount > 2);
+ this->addVec(vec);
+ }
+ }
+ }
+ }
+
+ void close() {
+ if (fPtCount > 2) {
+ this->addVec(fFirstVec);
+ }
+ }
+
+private:
+ void addVec(const SkVector& vec) {
+ SkASSERT(vec.fX || vec.fY);
+ fVec0 = fVec1;
+ fVec1 = vec;
+ int sign = CrossProductSign(fVec0, fVec1);
+ if (0 == fSign) {
+ fSign = sign;
+ } else if (sign) {
+ if (fSign == sign) {
+ fConvexity = SkPath::kConvex_Convexity;
+ } else {
+ fConvexity = SkPath::kConcave_Convexity;
+ }
+ }
+ }
+
+ SkPoint fCurrPt;
+ SkVector fVec0, fVec1, fFirstVec;
+ int fPtCount; // non-degenerate points
+ int fSign;
+ SkPath::Convexity fConvexity;
+};
+
+SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) {
+ SkPoint pts[4];
+ SkPath::Verb verb;
+ SkPath::Iter iter(path, true);
+
+ int contourCount = 0;
+ int count;
+ Convexicator state;
+
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case kMove_Verb:
+ if (++contourCount > 1) {
+ return kConcave_Convexity;
+ }
+ pts[1] = pts[0];
+ count = 1;
+ break;
+ case kLine_Verb: count = 1; break;
+ case kQuad_Verb: count = 2; break;
+ case kCubic_Verb: count = 3; break;
+ case kClose_Verb:
+ state.close();
+ count = 0;
+ break;
+ default:
+ SkASSERT(!"bad verb");
+ return kConcave_Convexity;
+ }
+
+ for (int i = 1; i <= count; i++) {
+ state.addPt(pts[i]);
+ }
+ // early exit
+ if (kConcave_Convexity == state.getConvexity()) {
+ return kConcave_Convexity;
+ }
+ }
+ return state.getConvexity();
+}
+
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index b4b631769b..4d00f70db2 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -1,5 +1,6 @@
#include "Test.h"
#include "SkPath.h"
+#include "SkParse.h"
#include "SkSize.h"
static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
@@ -17,7 +18,67 @@ static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
REPORTER_ASSERT(reporter, other.getBounds() == bounds);
}
-static void TestPath(skiatest::Reporter* reporter) {
+static void setFromString(SkPath* path, const char str[]) {
+ bool first = true;
+ while (str) {
+ SkScalar x, y;
+ str = SkParse::FindScalar(str, &x);
+ if (NULL == str) {
+ break;
+ }
+ str = SkParse::FindScalar(str, &y);
+ SkASSERT(str);
+ if (first) {
+ path->moveTo(x, y);
+ first = false;
+ } else {
+ path->lineTo(x, y);
+ }
+ }
+}
+
+static void test_convexity(skiatest::Reporter* reporter) {
+ static const SkPath::Convexity U = SkPath::kUnknown_Convexity;
+ static const SkPath::Convexity C = SkPath::kConcave_Convexity;
+ static const SkPath::Convexity V = SkPath::kConvex_Convexity;
+
+ SkPath path;
+
+ REPORTER_ASSERT(reporter, U == SkPath::ComputeConvexity(path));
+ path.addCircle(0, 0, 10);
+ REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+ path.addCircle(0, 0, 10); // 2nd circle
+ REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
+ path.reset();
+ path.addRect(0, 0, 10, 10, SkPath::kCCW_Direction);
+ REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+ path.reset();
+ path.addRect(0, 0, 10, 10, SkPath::kCW_Direction);
+ REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+
+ static const struct {
+ const char* fPathStr;
+ SkPath::Convexity fExpectedConvexity;
+ } gRec[] = {
+ { "0 0", SkPath::kUnknown_Convexity },
+ { "0 0 10 10", SkPath::kUnknown_Convexity },
+ { "0 0 10 10 20 20 0 0 10 10", SkPath::kUnknown_Convexity },
+ { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
+ { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
+ { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
+ { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+ SkPath path;
+ setFromString(&path, gRec[i].fPathStr);
+ SkPath::Convexity c = SkPath::ComputeConvexity(path);
+ REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
+ }
+}
+
+void TestPath(skiatest::Reporter* reporter);
+void TestPath(skiatest::Reporter* reporter) {
{
SkSize size;
size.fWidth = 3.4f;
@@ -100,6 +161,8 @@ static void TestPath(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, p.isConvex());
p.rewind();
REPORTER_ASSERT(reporter, !p.isConvex());
+
+ test_convexity(reporter);
}
#include "TestClassDef.h"