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:
parent
63926683c5
commit
05424f77fa
@ -68,7 +68,7 @@ protected:
|
|||||||
|
|
||||||
void onDraw(SkCanvas* canvas) override {
|
void onDraw(SkCanvas* canvas) override {
|
||||||
canvas->save();
|
canvas->save();
|
||||||
canvas->translate(-2, 20);
|
canvas->translate(-2, 120);
|
||||||
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 150));
|
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 150));
|
||||||
canvas->translate(0, 170);
|
canvas->translate(0, 170);
|
||||||
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 100));
|
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 100));
|
||||||
@ -77,6 +77,16 @@ protected:
|
|||||||
canvas->translate(0, 170);
|
canvas->translate(0, 170);
|
||||||
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 10, 150));
|
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 10, 150));
|
||||||
canvas->restore();
|
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) {
|
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,
|
fPath.cubicTo( 11.608591683531916f, 87.33115f, -0.010765133872116195f, 109.16448333333332f,
|
||||||
-0.013089005235602302f, 131);
|
-0.013089005235602302f, 131);
|
||||||
fPath.close();
|
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 fPath;
|
||||||
|
SkPath fFlipped;
|
||||||
private:
|
private:
|
||||||
typedef skiagm::GM INHERITED;
|
typedef skiagm::GM INHERITED;
|
||||||
};
|
};
|
||||||
|
@ -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 SkScalar mono_cubic_closestT(const SkScalar src[], SkScalar x) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
SkScalar t = 0.5f;
|
SkScalar t = 0.5f;
|
||||||
SkScalar lastT;
|
SkScalar lastT;
|
||||||
SkScalar bestT SK_INIT_TO_AVOID_WARNING;
|
SkScalar bestT SK_INIT_TO_AVOID_WARNING;
|
||||||
SkScalar step = 0.25f;
|
SkScalar step = 0.25f;
|
||||||
SkScalar D = pts[0].fX;
|
SkScalar D = src[0];
|
||||||
SkScalar A = pts[3].fX + 3*(pts[1].fX - pts[2].fX) - D;
|
SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
|
||||||
SkScalar B = 3*(pts[2].fX - pts[1].fX - pts[1].fX + D);
|
SkScalar B = 3*(src[4] - src[2] - src[2] + D);
|
||||||
SkScalar C = 3*(pts[1].fX - D);
|
SkScalar C = 3*(src[2] - D);
|
||||||
x -= D;
|
x -= D;
|
||||||
SkScalar closest = SK_ScalarMax;
|
SkScalar closest = SK_ScalarMax;
|
||||||
do {
|
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;
|
t += loc < x ? step : -step;
|
||||||
step *= 0.5f;
|
step *= 0.5f;
|
||||||
} while (closest > 0.25f && lastT != t);
|
} 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
|
// srcPts[] must be monotonic in X and Y
|
||||||
|
Loading…
Reference in New Issue
Block a user