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
This commit is contained in:
reed@google.com 2012-07-26 15:20:36 +00:00
Родитель 904160772e
Коммит 0bb18bb264
6 изменённых файлов: 132 добавлений и 11 удалений

Просмотреть файл

@ -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;

Просмотреть файл

@ -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; }

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;

Просмотреть файл

@ -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,

Просмотреть файл

@ -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"