diff options
-rw-r--r-- | include/core/SkPath.h | 55 | ||||
-rw-r--r-- | samplecode/SampleTests.cpp | 99 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 139 | ||||
-rw-r--r-- | tests/PathTest.cpp | 65 |
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" |