Fix SkPathStroker::lineTo() for line with length SK_ScalarNearlyZero
Review URL: https://codereview.appspot.com/5992077 git-svn-id: http://skia.googlecode.com/svn/trunk@3650 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
1647a1955f
commit
94fa43c625
@ -185,7 +185,7 @@ public:
|
||||
@return true if the line is of zero length; otherwise false.
|
||||
*/
|
||||
static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2) {
|
||||
return p1.equalsWithinTolerance(p2, SK_ScalarNearlyZero);
|
||||
return p1.equalsWithinTolerance(p2);
|
||||
}
|
||||
|
||||
/** Test a quad for zero length
|
||||
@ -194,8 +194,8 @@ public:
|
||||
*/
|
||||
static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
|
||||
const SkPoint& p3) {
|
||||
return p1.equalsWithinTolerance(p2, SK_ScalarNearlyZero) &&
|
||||
p2.equalsWithinTolerance(p3, SK_ScalarNearlyZero);
|
||||
return p1.equalsWithinTolerance(p2) &&
|
||||
p2.equalsWithinTolerance(p3);
|
||||
}
|
||||
|
||||
/** Test a cubic curve for zero length
|
||||
@ -204,9 +204,9 @@ public:
|
||||
*/
|
||||
static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
|
||||
const SkPoint& p3, const SkPoint& p4) {
|
||||
return p1.equalsWithinTolerance(p2, SK_ScalarNearlyZero) &&
|
||||
p2.equalsWithinTolerance(p3, SK_ScalarNearlyZero) &&
|
||||
p3.equalsWithinTolerance(p4, SK_ScalarNearlyZero);
|
||||
return p1.equalsWithinTolerance(p2) &&
|
||||
p2.equalsWithinTolerance(p3) &&
|
||||
p3.equalsWithinTolerance(p4);
|
||||
}
|
||||
|
||||
/** Returns true if the path specifies a rectangle. If so, and if rect is
|
||||
|
@ -315,11 +315,29 @@ struct SK_API SkPoint {
|
||||
return a.fX != b.fX || a.fY != b.fY;
|
||||
}
|
||||
|
||||
/** Return true if this and the given point are componentwise within tol.
|
||||
/** Return true if this point and the given point are far enough apart
|
||||
such that a vector between them would be non-degenerate.
|
||||
|
||||
WARNING: Unlike the deprecated version of equalsWithinTolerance(),
|
||||
this method does not use componentwise comparison. Instead, it
|
||||
uses a comparison designed to match judgments elsewhere regarding
|
||||
degeneracy ("points A and B are so close that the vector between them
|
||||
is essentially zero").
|
||||
*/
|
||||
bool equalsWithinTolerance(const SkPoint& v, SkScalar tol) const {
|
||||
return SkScalarNearlyZero(fX - v.fX, tol)
|
||||
&& SkScalarNearlyZero(fY - v.fY, tol);
|
||||
bool equalsWithinTolerance(const SkPoint& p) const {
|
||||
return !CanNormalize(fX - p.fX, fY - p.fY);
|
||||
}
|
||||
|
||||
/** DEPRECATED: Return true if this and the given point are componentwise
|
||||
within tolerance "tol".
|
||||
|
||||
WARNING: There is no guarantee that the result will reflect judgments
|
||||
elsewhere regarding degeneracy ("points A and B are so close that the
|
||||
vector between them is essentially zero").
|
||||
*/
|
||||
bool equalsWithinTolerance(const SkPoint& p, SkScalar tol) const {
|
||||
return SkScalarNearlyZero(fX - p.fX, tol)
|
||||
&& SkScalarNearlyZero(fY - p.fY, tol);
|
||||
}
|
||||
|
||||
/** Returns a new point whose coordinates are the difference between
|
||||
|
@ -87,31 +87,49 @@ bool SkPoint::setLength(SkScalar length) {
|
||||
return this->setLength(fX, fY, length);
|
||||
}
|
||||
|
||||
#ifdef SK_SCALAR_IS_FLOAT
|
||||
|
||||
// Returns the square of the Euclidian distance to (dx,dy).
|
||||
static inline float getLengthSquared(float dx, float dy) {
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
// Calculates the square of the Euclidian distance to (dx,dy) and stores it in
|
||||
// *lengthSquared. Returns true if the distance is judged to be "nearly zero".
|
||||
//
|
||||
// This logic is encapsulated in a helper method to make it explicit that we
|
||||
// always perform this check in the same manner, to avoid inconsistencies
|
||||
// (see http://code.google.com/p/skia/issues/detail?id=560 ).
|
||||
static inline bool isLengthNearlyZero(float dx, float dy,
|
||||
float *lengthSquared) {
|
||||
*lengthSquared = getLengthSquared(dx, dy);
|
||||
return *lengthSquared <= (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
|
||||
}
|
||||
|
||||
SkScalar SkPoint::Normalize(SkPoint* pt) {
|
||||
SkScalar mag = SkPoint::Length(pt->fX, pt->fY);
|
||||
if (mag > SK_ScalarNearlyZero) {
|
||||
SkScalar scale = SkScalarInvert(mag);
|
||||
pt->fX = SkScalarMul(pt->fX, scale);
|
||||
pt->fY = SkScalarMul(pt->fY, scale);
|
||||
float mag2;
|
||||
if (!isLengthNearlyZero(pt->fX, pt->fY, &mag2)) {
|
||||
float mag = sk_float_sqrt(mag2);
|
||||
float scale = 1.0 / mag;
|
||||
pt->fX = pt->fX * scale;
|
||||
pt->fY = pt->fY * scale;
|
||||
return mag;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SK_SCALAR_IS_FLOAT
|
||||
|
||||
bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
|
||||
float mag2 = dx * dx + dy * dy;
|
||||
return mag2 > SK_ScalarNearlyZero * SK_ScalarNearlyZero;
|
||||
float mag2_unused;
|
||||
return !isLengthNearlyZero(dx, dy, &mag2_unused);
|
||||
}
|
||||
|
||||
SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
|
||||
return sk_float_sqrt(dx * dx + dy * dy);
|
||||
return sk_float_sqrt(getLengthSquared(dx, dy));
|
||||
}
|
||||
|
||||
bool SkPoint::setLength(float x, float y, float length) {
|
||||
float mag2 = x * x + y * y;
|
||||
if (mag2 > SK_ScalarNearlyZero * SK_ScalarNearlyZero) {
|
||||
float mag2;
|
||||
if (!isLengthNearlyZero(x, y, &mag2)) {
|
||||
float scale = length / sk_float_sqrt(mag2);
|
||||
fX = x * scale;
|
||||
fY = y * scale;
|
||||
@ -124,12 +142,25 @@ 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);
|
||||
// Returns the square of the Euclidian distance to (dx,dy) in *result.
|
||||
static inline void getLengthSquared(SkScalar dx, SkScalar dy, Sk64 *result) {
|
||||
Sk64 dySqr;
|
||||
|
||||
result->setMul(dx, dx);
|
||||
dySqr.setMul(dy, dy);
|
||||
result->add(dySqr);
|
||||
}
|
||||
|
||||
// Calculates the square of the Euclidian distance to (dx,dy) and stores it in
|
||||
// *lengthSquared. Returns true if the distance is judged to be "nearly zero".
|
||||
//
|
||||
// This logic is encapsulated in a helper method to make it explicit that we
|
||||
// always perform this check in the same manner, to avoid inconsistencies
|
||||
// (see http://code.google.com/p/skia/issues/detail?id=560 ).
|
||||
static inline bool isLengthNearlyZero(SkScalar dx, SkScalar dy,
|
||||
Sk64 *lengthSquared) {
|
||||
Sk64 tolSqr;
|
||||
getLengthSquared(dx, dy, lengthSquared);
|
||||
|
||||
// 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
|
||||
@ -138,17 +169,30 @@ bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
|
||||
SkASSERT(SK_ScalarNearlyZero <= 0xB504);
|
||||
|
||||
tolSqr.set(0, SK_ScalarNearlyZero * SK_ScalarNearlyZero);
|
||||
return tmp1 > tolSqr;
|
||||
return *lengthSquared <= tolSqr;
|
||||
}
|
||||
|
||||
SkScalar SkPoint::Normalize(SkPoint* pt) {
|
||||
Sk64 mag2;
|
||||
if (!isLengthNearlyZero(pt->fX, pt->fY, &mag2)) {
|
||||
SkScalar mag = mag2.getSqrt();
|
||||
SkScalar scale = SkScalarInvert(mag);
|
||||
pt->fX = SkScalarMul(pt->fX, scale);
|
||||
pt->fY = SkScalarMul(pt->fY, scale);
|
||||
return mag;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
|
||||
Sk64 mag2_unused;
|
||||
return !isLengthNearlyZero(dx, dy, &mag2_unused);
|
||||
}
|
||||
|
||||
SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
|
||||
Sk64 tmp1, tmp2;
|
||||
|
||||
tmp1.setMul(dx, dx);
|
||||
tmp2.setMul(dy, dy);
|
||||
tmp1.add(tmp2);
|
||||
|
||||
return tmp1.getSqrt();
|
||||
Sk64 tmp;
|
||||
getLengthSquared(dx, dy, &tmp);
|
||||
return tmp.getSqrt();
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUGx
|
||||
|
@ -286,6 +286,29 @@ SIMPLE_TEST_STEP(DrawData, drawData(kTestText.c_str(), kTestText.size()));
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Complex test steps
|
||||
|
||||
// exercise fix for http://code.google.com/p/skia/issues/detail?id=560
|
||||
// ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero')
|
||||
static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas,
|
||||
skiatest::Reporter* reporter,
|
||||
CanvasTestStep* testStep) {
|
||||
SkPaint paint;
|
||||
paint.setStrokeWidth(SkIntToScalar(1));
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
|
||||
SkPath path;
|
||||
SkPoint pt1 = { 0, 0 };
|
||||
SkPoint pt2 = { 0, SK_ScalarNearlyZero };
|
||||
SkPoint pt3 = { SkIntToScalar(1), 0 };
|
||||
SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 };
|
||||
path.moveTo(pt1);
|
||||
path.lineTo(pt2);
|
||||
path.lineTo(pt3);
|
||||
path.lineTo(pt4);
|
||||
|
||||
canvas->drawPath(path, paint);
|
||||
}
|
||||
TEST_STEP(DrawNearlyZeroLengthPath, DrawNearlyZeroLengthPathTestStep);
|
||||
|
||||
static void DrawVerticesShaderTestStep(SkCanvas* canvas,
|
||||
skiatest::Reporter* reporter,
|
||||
CanvasTestStep* testStep) {
|
||||
|
Loading…
Reference in New Issue
Block a user