Change PerspectiveClip to clip directly to half plane.

This should be a little more numerically stable, and avoids the need
for the assert.

Bug: chromium:1032435
Change-Id: I038890000b251f3ffed41f37a14451afcf3b1866
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/259167
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2019-12-10 16:16:06 -05:00 committed by Skia Commit-Bot
parent 6ee695c33c
commit 1031994219

View File

@ -3463,31 +3463,31 @@ struct SkHalfPlane {
}
SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
bool twoPts(SkPoint pts[2]) const {
// normalize plane to help with the perpendicular step, below
SkScalar len = SkScalarSqrt(fA*fA + fB*fB);
if (!len) {
bool normalize() {
double a = fA;
double b = fB;
double c = fC;
double dmag = sqrt(a * a + b * b);
// length of initial plane normal is zero
if (dmag == 0) {
fA = fB = 0;
fC = SK_Scalar1;
return true;
}
double dscale = sk_ieee_double_divide(1.0, dmag);
a *= dscale;
b *= dscale;
c *= dscale;
// check if we're not finite, or normal is zero-length
if (!sk_float_isfinite(a) || !sk_float_isfinite(b) || !sk_float_isfinite(c) ||
(a == 0 && b == 0)) {
fA = fB = 0;
fC = SK_Scalar1;
return false;
}
SkScalar denom = SkScalarInvert(len);
SkScalar a = fA * denom;
SkScalar b = fB * denom;
SkScalar c = fC * denom;
// We compute p0 on the half-plane by setting one of the components to 0
// We compute p1 by stepping from p0 along a perpendicular to the normal
if (b) {
pts[0] = { 0, -c / b };
pts[1] = { b, pts[0].fY - a};
} else if (a) {
pts[0] = { -c / a, 0 };
pts[1] = { pts[0].fX + b, -a };
} else {
return false;
}
SkASSERT(SkScalarNearlyZero(this->operator()(pts[0].fX, pts[0].fY)));
SkASSERT(SkScalarNearlyZero(this->operator()(pts[1].fX, pts[1].fY)));
fA = a;
fB = b;
fC = c;
return true;
}
@ -3527,12 +3527,13 @@ struct SkHalfPlane {
}
};
static void clip(const SkPath& path, SkPoint p0, SkPoint p1, SkPath* clippedPath) {
// assumes plane is pre-normalized
static void clip(const SkPath& path, const SkHalfPlane& plane, SkPath* clippedPath) {
SkMatrix mx, inv;
SkVector v = p1 - p0;
mx.setAll(v.fX, -v.fY, p0.fX,
v.fY, v.fX, p0.fY,
0, 0, 1);
SkPoint p0 = { -plane.fA*plane.fC, -plane.fB*plane.fC };
mx.setAll( plane.fB, plane.fA, p0.fX,
-plane.fA, plane.fB, p0.fY,
0, 0, 1);
SkAssertResult(mx.invert(&inv));
SkPath rotated;
@ -3593,23 +3594,21 @@ bool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkP
}
constexpr SkScalar kW0PlaneDistance = 0.05f;
const SkHalfPlane plane {
SkHalfPlane plane {
matrix[SkMatrix::kMPersp0],
matrix[SkMatrix::kMPersp1],
matrix[SkMatrix::kMPersp2] - kW0PlaneDistance
};
switch (plane.test(path.getBounds())) {
case SkHalfPlane::kAllPositive:
return false;
case SkHalfPlane::kMixed: {
SkPoint pts[2];
if (plane.twoPts(pts)) {
clip(path, pts[0], pts[1], clippedPath);
if (plane.normalize()) {
switch (plane.test(path.getBounds())) {
case SkHalfPlane::kAllPositive:
return false;
case SkHalfPlane::kMixed: {
clip(path, plane, clippedPath);
return true;
}
} break;
default: break; // handled outside of the switch
} break;
default: break; // handled outside of the switch
}
}
// clipped out (or failed)
clippedPath->reset();