fix cubic clip in y

Follow on to https://codereview.chromium.org/1299243002/

Clip using a geometric solution if the algebraic solution
fails in Y as well as in X.

If the root finder discovers real roots that are sufficiently
far apart, the root in the range of 0..1 can contain so much
error that it is computed to be slightly smaller than 0 or
larger than 1.

In this case, binary search the mono curve for the actual
answer.

R=reed@google.com

Review URL: https://codereview.chromium.org/1303873003
This commit is contained in:
caryclark 2015-08-20 10:35:43 -07:00 committed by Commit bot
parent 63926683c5
commit 05424f77fa
2 changed files with 71 additions and 55 deletions

View File

@ -68,7 +68,7 @@ protected:
void onDraw(SkCanvas* canvas) override {
canvas->save();
canvas->translate(-2, 20);
canvas->translate(-2, 120);
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 150));
canvas->translate(0, 170);
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 100));
@ -77,6 +77,16 @@ protected:
canvas->translate(0, 170);
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 10, 150));
canvas->restore();
canvas->save();
canvas->translate(20, -2);
drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 80));
canvas->translate(170, 0);
drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 100, 80));
canvas->translate(170, 0);
drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 30));
canvas->translate(170, 0);
drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 10));
canvas->restore();
}
void drawOne(SkCanvas* canvas, const SkPath& path, const SkRect& clip) {
@ -97,9 +107,18 @@ protected:
fPath.cubicTo( 11.608591683531916f, 87.33115f, -0.010765133872116195f, 109.16448333333332f,
-0.013089005235602302f, 131);
fPath.close();
fFlipped = fPath;
SkMatrix matrix;
matrix.reset();
matrix.setScaleX(0);
matrix.setScaleY(0);
matrix.setSkewX(1);
matrix.setSkewY(1);
fFlipped.transform(matrix);
}
SkPath fPath;
SkPath fFlipped;
private:
typedef skiagm::GM INHERITED;
};

View File

@ -225,63 +225,15 @@ bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) {
///////////////////////////////////////////////////////////////////////////////
// Modify pts[] in place so that it is clipped in Y to the clip rect
static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
// are we partially above
if (pts[0].fY < clip.fTop) {
SkPoint tmp[7];
if (SkChopMonoCubicAtY(pts, clip.fTop, tmp)) {
// tmp[3, 4].fY should all be to the below clip.fTop.
// Since we can't trust the numerics of
// the chopper, we force those conditions now
tmp[3].fY = clip.fTop;
clamp_ge(tmp[4].fY, clip.fTop);
pts[0] = tmp[3];
pts[1] = tmp[4];
pts[2] = tmp[5];
} else {
// if chopMonoCubicAtY failed, then we may have hit inexact numerics
// so we just clamp against the top
for (int i = 0; i < 4; i++) {
clamp_ge(pts[i].fY, clip.fTop);
}
}
}
// are we partially below
if (pts[3].fY > clip.fBottom) {
SkPoint tmp[7];
if (SkChopMonoCubicAtY(pts, clip.fBottom, tmp)) {
tmp[3].fY = clip.fBottom;
clamp_le(tmp[2].fY, clip.fBottom);
pts[1] = tmp[1];
pts[2] = tmp[2];
pts[3] = tmp[3];
} else {
// if chopMonoCubicAtY failed, then we may have hit inexact numerics
// so we just clamp against the bottom
for (int i = 0; i < 4; i++) {
clamp_le(pts[i].fY, clip.fBottom);
}
}
}
}
static void chop_mono_cubic_at_x(SkPoint pts[4], SkScalar x, SkPoint tmp[7]) {
if (SkChopMonoCubicAtX(pts, x, tmp)) {
return;
}
static SkScalar mono_cubic_closestT(const SkScalar src[], SkScalar x) {
SkScalar t = 0.5f;
SkScalar lastT;
SkScalar bestT SK_INIT_TO_AVOID_WARNING;
SkScalar step = 0.25f;
SkScalar D = pts[0].fX;
SkScalar A = pts[3].fX + 3*(pts[1].fX - pts[2].fX) - D;
SkScalar B = 3*(pts[2].fX - pts[1].fX - pts[1].fX + D);
SkScalar C = 3*(pts[1].fX - D);
SkScalar D = src[0];
SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
SkScalar B = 3*(src[4] - src[2] - src[2] + D);
SkScalar C = 3*(src[2] - D);
x -= D;
SkScalar closest = SK_ScalarMax;
do {
@ -295,7 +247,52 @@ static void chop_mono_cubic_at_x(SkPoint pts[4], SkScalar x, SkPoint tmp[7]) {
t += loc < x ? step : -step;
step *= 0.5f;
} while (closest > 0.25f && lastT != t);
SkChopCubicAt(pts, tmp, bestT);
return bestT;
}
static void chop_mono_cubic_at_y(SkPoint src[4], SkScalar y, SkPoint dst[7]) {
if (SkChopMonoCubicAtY(src, y, dst)) {
return;
}
SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fY, y));
}
// Modify pts[] in place so that it is clipped in Y to the clip rect
static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
// are we partially above
if (pts[0].fY < clip.fTop) {
SkPoint tmp[7];
chop_mono_cubic_at_y(pts, clip.fTop, tmp);
// tmp[3, 4].fY should all be to the below clip.fTop.
// Since we can't trust the numerics of
// the chopper, we force those conditions now
tmp[3].fY = clip.fTop;
clamp_ge(tmp[4].fY, clip.fTop);
pts[0] = tmp[3];
pts[1] = tmp[4];
pts[2] = tmp[5];
}
// are we partially below
if (pts[3].fY > clip.fBottom) {
SkPoint tmp[7];
chop_mono_cubic_at_y(pts, clip.fBottom, tmp);
tmp[3].fY = clip.fBottom;
clamp_le(tmp[2].fY, clip.fBottom);
pts[1] = tmp[1];
pts[2] = tmp[2];
pts[3] = tmp[3];
}
}
static void chop_mono_cubic_at_x(SkPoint src[4], SkScalar x, SkPoint dst[7]) {
if (SkChopMonoCubicAtX(src, x, dst)) {
return;
}
SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fX, x));
}
// srcPts[] must be monotonic in X and Y