зеркало из https://github.com/mozilla/moz-skia.git
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:
Родитель
904160772e
Коммит
0bb18bb264
|
@ -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"
|
||||
|
|
Загрузка…
Ссылка в новой задаче