add SkPoint::CanNormalize to unify decisions about when a vector is degenerate

git-svn-id: http://skia.googlecode.com/svn/trunk@2236 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2011-09-07 12:23:41 +00:00
parent 11f6380510
commit 55b5f4bd6a
4 changed files with 79 additions and 7 deletions

View File

@ -199,6 +199,16 @@ struct SK_API SkPoint {
SkScalar length() const { return SkPoint::Length(fX, fY); }
SkScalar distanceToOrigin() const { return this->length(); }
/**
* Return true if the computed length of the vector is >= the internal
* tolerance (used to avoid dividing by tiny values).
*/
static bool CanNormalize(SkScalar dx, SkScalar dy);
bool canNormalize() const {
return CanNormalize(fX, fY);
}
/** Set the point (vector) to be unit-length in the same direction as it
already points. If the point has a degenerate length (i.e. nearly 0)
then return false and do nothing; otherwise return true.
@ -317,7 +327,8 @@ struct SK_API SkPoint {
static SkScalar Length(SkScalar x, SkScalar y);
/** Normalize pt, returning its previous length. If the prev length is too
small (degenerate), return 0 and leave pt unchanged.
small (degenerate), return 0 and leave pt unchanged. This uses the same
tolerance as CanNormalize.
Note that this method may be significantly more expensive than
the non-static normalize(), because it has to return the previous length

View File

@ -100,16 +100,21 @@ SkScalar SkPoint::Normalize(SkPoint* pt) {
#ifdef SK_SCALAR_IS_FLOAT
bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
float mag2 = dx * dx + dy * dy;
return mag2 > SK_ScalarNearlyZero * SK_ScalarNearlyZero;
}
SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
return sk_float_sqrt(dx * dx + dy * dy);
}
bool SkPoint::setLength(float x, float y, float length) {
float mag = sk_float_sqrt(x * x + y * y);
if (mag > SK_ScalarNearlyZero) {
length /= mag;
fX = x * length;
fY = y * length;
float mag2 = x * x + y * y;
if (mag2 > SK_ScalarNearlyZero * SK_ScalarNearlyZero) {
float scale = length / sk_float_sqrt(mag2);
fX = x * scale;
fY = y * scale;
return true;
}
return false;
@ -119,6 +124,23 @@ bool SkPoint::setLength(float x, float y, float length) {
#include "Sk64.h"
bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
Sk64 tmp1, tmp2, tolSqr;
tmp1.setMul(dx, dx);
tmp2.setMul(dy, dy);
tmp1.add(tmp2);
// we want nearlyzero^2, but to compute it fast we want to just do a
// 32bit multiply, so we require that it not exceed 31bits. That is true
// if nearlyzero is <= 0xB504, which should be trivial, since usually
// nearlyzero is a very small fixed-point value.
SkASSERT(SK_ScalarNearlyZero <= 0xB504);
tolSqr.set(0, SK_ScalarNearlyZero * SK_ScalarNearlyZero);
return tmp1 > tolSqr;
}
SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
Sk64 tmp1, tmp2;

View File

@ -15,7 +15,7 @@
#define kMaxCubicSubdivide 4
static inline bool degenerate_vector(const SkVector& v) {
return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY);
return !SkPoint::CanNormalize(v.fX, v.fY);
}
static inline bool degenerate_line(const SkPoint& a, const SkPoint& b,

View File

@ -6,10 +6,47 @@
* found in the LICENSE file.
*/
#include "Test.h"
#include "SkPaint.h"
#include "SkPath.h"
#include "SkParse.h"
#include "SkSize.h"
static void stroke_cubic(const SkPoint pts[4]) {
SkPath path;
path.moveTo(pts[0]);
path.cubicTo(pts[1], pts[2], pts[3]);
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(SK_Scalar1 * 2);
SkPath fill;
paint.getFillPath(path, &fill);
}
// just ensure this can run w/o any SkASSERTS firing in the debug build
// we used to assert due to differences in how we determine a degenerate vector
// but that was fixed with the introduction of SkPoint::CanNormalize
static void stroke_tiny_cubic() {
SkPoint p0[] = {
{ 372.0f, 92.0f },
{ 372.0f, 92.0f },
{ 372.0f, 92.0f },
{ 372.0f, 92.0f },
};
stroke_cubic(p0);
SkPoint p1[] = {
{ 372.0f, 92.0f },
{ 372.0007f, 92.000755f },
{ 371.99927f, 92.003922f },
{ 371.99826f, 92.003899f },
};
stroke_cubic(p1);
}
static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
for (int i = 0; i < 2; ++i) {
SkPath::Iter iter(path, (bool)i);
@ -102,6 +139,8 @@ static void test_close(skiatest::Reporter* reporter) {
moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
check_close(reporter, moves);
stroke_tiny_cubic();
}
static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,