diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-07-26 15:20:36 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-07-26 15:20:36 +0000 |
commit | 0bb18bb264b26afca45452910437c09445e23a3c (patch) | |
tree | de94a02e74a789b5a80ad9e6a3b8fdcda9573215 | |
parent | 904160772ed097cd481d6fcd8a3f4bf0a1af8b52 (diff) |
explicitly track if a path is finite or not
we need this (it appears) so we can definitively reject non-finite paths
in canvas, before passing them down into the guts.
Review URL: https://codereview.appspot.com/6453047
git-svn-id: http://skia.googlecode.com/svn/trunk@4784 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | include/core/SkPath.h | 13 | ||||
-rw-r--r-- | include/core/SkPoint.h | 23 | ||||
-rw-r--r-- | include/core/SkRect.h | 18 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 20 | ||||
-rw-r--r-- | src/core/SkRect.cpp | 7 | ||||
-rw-r--r-- | tests/PathTest.cpp | 62 |
6 files changed, 132 insertions, 11 deletions
diff --git a/include/core/SkPath.h b/include/core/SkPath.h index 8f03de1e86..fcb3cb5f86 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -192,6 +192,17 @@ public: */ bool isEmpty() const; + /** + * Returns true if all of the points in this path are finite, meaning there + * are no infinities and no NaNs. + */ + bool isFinite() const { + if (fBoundsIsDirty) { + this->computeBounds(); + } + return fIsFinite; + } + /** Test a line for zero length @return true if the line is of zero length; otherwise false. @@ -818,7 +829,7 @@ private: uint8_t fSegmentMask; mutable uint8_t fBoundsIsDirty; mutable uint8_t fConvexity; - + mutable SkBool8 fIsFinite; // only meaningful if bounds are valid mutable SkBool8 fIsOval; #ifdef SK_BUILD_FOR_ANDROID uint32_t fGenerationID; diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h index ffe11595f1..5e20f530c4 100644 --- a/include/core/SkPoint.h +++ b/include/core/SkPoint.h @@ -314,6 +314,29 @@ struct SK_API SkPoint { fY -= v.fY; } + /** + * Returns true if both X and Y are finite (not infinity or NaN) + */ + bool isFinite() const { +#ifdef SK_SCALAR_IS_FLOAT + SkScalar accum = 0; + accum *= fX; + accum *= fY; + + // accum is either NaN or it is finite (zero). + SkASSERT(0 == accum || !(accum == accum)); + + // value==value will be true iff value is not NaN + // TODO: is it faster to say !accum or accum==accum? + return accum == accum; +#else + // use bit-or for speed, since we don't care about short-circuting the + // tests, and we expect the common case will be that we need to check all. + int isNaN = (SK_FixedNaN == fX) | (SK_FixedNaN == fX)); + return !isNaN; +#endif + } + /** Returns true if the point's coordinates equal (x,y) */ bool equals(SkScalar x, SkScalar y) const { return fX == x && fY == y; } diff --git a/include/core/SkRect.h b/include/core/SkRect.h index 83ef2df35c..723f5f6e2b 100644 --- a/include/core/SkRect.h +++ b/include/core/SkRect.h @@ -448,13 +448,25 @@ struct SK_API SkRect { If the array is empty (count == 0), then set this rectangle to the empty rectangle (0,0,0,0) */ - void set(const SkPoint pts[], int count); + void set(const SkPoint pts[], int count) { + // set() had been checking for non-finite values, so keep that behavior + // for now. Now that we have setBoundsCheck(), we may decide to make + // set() be simpler/faster, and not check for those. + (void)this->setBoundsCheck(pts, count); + } // alias for set(pts, count) void setBounds(const SkPoint pts[], int count) { - this->set(pts, count); + (void)this->setBoundsCheck(pts, count); } - + + /** + * Compute the bounds of the array of points, and set this rect to that + * bounds and return true... unless a non-finite value is encountered, + * in which case this rect is set to empty and false is returned. + */ + bool setBoundsCheck(const SkPoint pts[], int count); + void set(const SkPoint& p0, const SkPoint& p1) { fLeft = SkMinScalar(p0.fX, p1.fX); fRight = SkMaxScalar(p0.fX, p1.fX); diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 36d18370a2..22d3d86963 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -80,9 +80,11 @@ public: if (fEmpty) { fPath->fBounds = fRect; fPath->fBoundsIsDirty = false; + fPath->fIsFinite = fPath->fBounds.isFinite(); } else if (!fDirty) { joinNoEmptyChecks(&fPath->fBounds, fRect); fPath->fBoundsIsDirty = false; + fPath->fIsFinite = fPath->fBounds.isFinite(); } } @@ -104,12 +106,14 @@ private: } }; -static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) { - if (pts.count() <= 1) { // we ignore just 1 point (moveto) - bounds->set(0, 0, 0, 0); +// Return true if the computed bounds are finite. +static bool compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) { + int count = pts.count(); + if (count <= 1) { // we ignore just 1 point (moveto) + bounds->setEmpty(); + return count ? pts.begin()->isFinite() : true; } else { - bounds->set(pts.begin(), pts.count()); -// SkDebugf("------- compute bounds %p %d", &pts, pts.count()); + return bounds->setBoundsCheck(pts.begin(), pts.count()); } } @@ -139,6 +143,7 @@ SkPath::SkPath() fSegmentMask = 0; fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; fIsOval = false; + fIsFinite = false; // gets computed when we know our bounds #ifdef SK_BUILD_FOR_ANDROID fGenerationID = 0; fSourcePath = NULL; @@ -169,6 +174,7 @@ SkPath& SkPath::operator=(const SkPath& src) { fFillType = src.fFillType; fBoundsIsDirty = src.fBoundsIsDirty; fConvexity = src.fConvexity; + fIsFinite = src.fIsFinite; fSegmentMask = src.fSegmentMask; fLastMoveToIndex = src.fLastMoveToIndex; fIsOval = src.fIsOval; @@ -204,6 +210,7 @@ void SkPath::swap(SkPath& other) { SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask); SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex); SkTSwap<SkBool8>(fIsOval, other.fIsOval); + SkTSwap<SkBool8>(fIsFinite, other.fIsFinite); GEN_ID_INC; } } @@ -449,7 +456,7 @@ void SkPath::computeBounds() const { SkASSERT(fBoundsIsDirty); fBoundsIsDirty = false; - compute_pt_bounds(&fBounds, fPts); + fIsFinite = compute_pt_bounds(&fBounds, fPts); } void SkPath::setConvexity(Convexity c) { @@ -1340,6 +1347,7 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { // if we're empty, fastbounds should not be mapped matrix.mapRect(&dst->fBounds, fBounds); dst->fBoundsIsDirty = false; + dst->fIsFinite = dst->fBounds.isFinite(); } else { GEN_ID_PTR_INC(dst); dst->fBoundsIsDirty = true; diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp index 2e7b184b31..55dbe3d6e0 100644 --- a/src/core/SkRect.cpp +++ b/src/core/SkRect.cpp @@ -70,8 +70,10 @@ void SkRect::toQuad(SkPoint quad[4]) const { #define MINMAX_ELSE else #endif -void SkRect::set(const SkPoint pts[], int count) { +bool SkRect::setBoundsCheck(const SkPoint pts[], int count) { SkASSERT((pts && count > 0) || count == 0); + + bool isFinite = true; if (count <= 0) { sk_bzero(this, sizeof(SkRect)); @@ -118,11 +120,14 @@ void SkRect::set(const SkPoint pts[], int count) { SkASSERT(!accum || !SkScalarIsFinite(accum)); if (accum) { l = t = r = b = 0; + isFinite = false; } #endif this->set(l, t, r, b); #endif } + + return isFinite; } bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index f37760131a..e23ee0e9b1 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -16,6 +16,67 @@ #include "SkSize.h" #include "SkWriter32.h" +static void test_rect_isfinite(skiatest::Reporter* reporter) { + const SkScalar inf = SK_ScalarInfinity; + const SkScalar nan = SK_ScalarNaN; + + SkRect r; + r.setEmpty(); + REPORTER_ASSERT(reporter, r.isFinite()); + r.set(0, 0, inf, -inf); + REPORTER_ASSERT(reporter, !r.isFinite()); + r.set(0, 0, nan, 0); + REPORTER_ASSERT(reporter, !r.isFinite()); + + SkPoint pts[] = { + { 0, 0 }, + { SK_Scalar1, 0 }, + { 0, SK_Scalar1 }, + }; + + bool isFine = r.setBoundsCheck(pts, 3); + REPORTER_ASSERT(reporter, isFine); + REPORTER_ASSERT(reporter, !r.isEmpty()); + + pts[1].set(inf, 0); + isFine = r.setBoundsCheck(pts, 3); + REPORTER_ASSERT(reporter, !isFine); + REPORTER_ASSERT(reporter, r.isEmpty()); + + pts[1].set(nan, 0); + isFine = r.setBoundsCheck(pts, 3); + REPORTER_ASSERT(reporter, !isFine); + REPORTER_ASSERT(reporter, r.isEmpty()); +} + +static void test_path_isfinite(skiatest::Reporter* reporter) { + const SkScalar inf = SK_ScalarInfinity; + const SkScalar nan = SK_ScalarNaN; + + SkPath path; + REPORTER_ASSERT(reporter, path.isFinite()); + + path.reset(); + REPORTER_ASSERT(reporter, path.isFinite()); + + path.reset(); + path.moveTo(SK_Scalar1, 0); + REPORTER_ASSERT(reporter, path.isFinite()); + + path.reset(); + path.moveTo(inf, -inf); + REPORTER_ASSERT(reporter, !path.isFinite()); + + path.reset(); + path.moveTo(nan, 0); + REPORTER_ASSERT(reporter, !path.isFinite()); +} + +static void test_isfinite(skiatest::Reporter* reporter) { + test_rect_isfinite(reporter); + test_path_isfinite(reporter); +} + // assert that we always // start with a moveTo // only have 1 moveTo @@ -1466,6 +1527,7 @@ static void TestPath(skiatest::Reporter* reporter) { test_oval(reporter); test_strokerec(reporter); test_addPoly(reporter); + test_isfinite(reporter); } #include "TestClassDef.h" |