aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-26 15:20:36 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-26 15:20:36 +0000
commit0bb18bb264b26afca45452910437c09445e23a3c (patch)
treede94a02e74a789b5a80ad9e6a3b8fdcda9573215
parent904160772ed097cd481d6fcd8a3f4bf0a1af8b52 (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.h13
-rw-r--r--include/core/SkPoint.h23
-rw-r--r--include/core/SkRect.h18
-rw-r--r--src/core/SkPath.cpp20
-rw-r--r--src/core/SkRect.cpp7
-rw-r--r--tests/PathTest.cpp62
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"