Some more tweaks to perspective path clipping

Bug: skia:9546
Change-Id: Ib91880e09ae6373bc133d684aa7510c67681ec1e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/257629
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-03 12:05:14 -05:00 committed by Skia Commit-Bot
parent e0f374bf8f
commit c336315d3c

View File

@ -146,15 +146,30 @@ struct SkHalfPlane {
}
bool twoPts(SkPoint pts[2]) const {
if (fB) {
pts[0] = { 0, -fC / fB };
pts[1] = { 1, (-fC - fA) / fB };
} else if (fA) {
pts[0] = { -fC / fA, 0 };
pts[1] = { (-fC - fB) / fA, 1 };
// normalize plane to help with the perpendicular step, below
SkScalar len = SkScalarSqrt(fA*fA + fB*fB);
if (!len) {
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)));
return true;
}
};
@ -222,6 +237,39 @@ static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
}
static SkPath clip(const SkPath& path, const SkHalfPlane& plane) {
// do a quick bounds check to see if we need to clip at all
const SkRect& bounds = path.getBounds();
// check whether the diagonal aligned with the normal crosses the plane
SkPoint diagMin, diagMax;
if (plane.fA >= 0) {
diagMin.fX = bounds.fLeft;
diagMax.fX = bounds.fRight;
} else {
diagMin.fX = bounds.fRight;
diagMax.fX = bounds.fLeft;
}
if (plane.fB >= 0) {
diagMin.fY = bounds.fTop;
diagMax.fY = bounds.fBottom;
} else {
diagMin.fY = bounds.fBottom;
diagMax.fY = bounds.fTop;
}
SkScalar test = plane(diagMin.fX, diagMin.fY);
SkScalar sign = test*plane(diagMax.fX, diagMin.fY);
if (sign > 0) {
// the path is either all on one side of the half-plane or the other
if (test < 0) {
// completely culled
return SkPath();
} else {
// no clipping necessary
return path;
}
}
// quick check failed, we have to clip
SkPoint pts[2];
if (plane.twoPts(pts)) {
return clip(path, pts[0], pts[1]);
@ -393,7 +441,7 @@ static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPla
a = 2*a/W - m; b = 2*b/W - n; d = 2*d/W - p;
e = 2*e/H - m; f = 2*f/H - n; h = 2*h/H - p;
i = 2*i - m; j = 2*j - n; l = 2*l - p;
// i = 2*i - m; j = 2*j - n; l = 2*l - p;
planes[0] = { m - a, n - b, p - d }; // w - x
planes[1] = { m + a, n + b, p + d }; // w + x
@ -403,6 +451,19 @@ static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPla
planes[5] = { m + i, n + j, p + l }; // w + z
}
static SkHalfPlane half_plane_w0(const SkMatrix44& m44, SkScalar W, SkScalar H) {
float mx[16];
m44.asColMajorf(mx);
SkScalar
// a = mx[0], b = mx[4], /* c = mx[ 8], */ d = mx[12],
// e = mx[1], f = mx[5], /* g = mx[ 9], */ h = mx[13],
// i = mx[2], j = mx[6], /* k = mx[10], */ l = mx[14],
m = mx[3], n = mx[7], /* o = mx[11], */ p = mx[15];
return { m, n, p - 0.05f }; // w = 0.05f
}
class HalfPlaneView3 : public Sample {
float fNear = 0.05f;
float fFar = 4;
@ -413,6 +474,7 @@ class HalfPlaneView3 : public Sample {
SkPoint3 fUp { 0, 1, 0 };
SkMatrix44 fRot;
SkPoint3 fTrans;
SkPath fPath;
sk_sp<SkShader> fShader;
@ -440,13 +502,15 @@ class HalfPlaneView3 : public Sample {
SkMatrix44 get44() const {
SkMatrix44 camera,
perspective,
translate,
viewport;
Sk3Perspective(&perspective, fNear, fFar, fAngle);
Sk3LookAt(&camera, fEye, fCOA, fUp);
translate.setTranslate(fTrans.fX, fTrans.fY, fTrans.fZ);
viewport.setScale(200, 200, 1).postTranslate( 200, 200, 0);
return viewport * perspective * camera * fRot * inv(viewport);
return viewport * perspective * camera * translate * fRot * inv(viewport);
}
void onDrawContent(SkCanvas* canvas) override {
@ -468,8 +532,9 @@ class HalfPlaneView3 : public Sample {
SkHalfPlane planes[6];
half_planes(mx44, 400, 400, planes);
SkHalfPlane hpw = half_plane_w0(mx44, 400, 400);
SkPath path = clip(fPath, planes[4]);
SkPath path = clip(fPath, hpw);//planes[4]);
canvas->save();
canvas->concat(mx);
canvas->drawPath(path, paint);
@ -479,7 +544,8 @@ class HalfPlaneView3 : public Sample {
// draw_halfplane(canvas, p, SK_ColorRED);
// }
draw_halfplane(canvas, planes[4], SK_ColorBLUE);
draw_halfplane(canvas, planes[5], SK_ColorGREEN);
// draw_halfplane(canvas, planes[5], SK_ColorGREEN);
draw_halfplane(canvas, hpw, SK_ColorRED);
}
bool onChar(SkUnichar uni) override {
@ -492,8 +558,8 @@ class HalfPlaneView3 : public Sample {
case '-': this->rotate(0, 0, delta); return true;
case '+': this->rotate(0, 0, -delta); return true;
case 'i': fEye.fZ += 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
case 'k': fEye.fZ -= 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
case 'i': fTrans.fZ += 0.1f; SkDebugf("ez %g\n", fTrans.fZ); return true;
case 'k': fTrans.fZ -= 0.1f; SkDebugf("ez %g\n", fTrans.fZ); return true;
case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;