subdivide path when side-clipping fails

Please review concept; I'm OK not to check this in.

If the root finder fails, subdivide the curve and try again.
This is complicated by the reversed nature of the curves;
maybe it can be simpler, but how to do that escapes me.

R=reed@google.com
BUG=514246

Review URL: https://codereview.chromium.org/1299243002
This commit is contained in:
caryclark 2015-08-20 08:23:52 -07:00 committed by Commit bot
parent 725c620543
commit 7d173403f4
2 changed files with 96 additions and 26 deletions

View File

@ -53,6 +53,58 @@ private:
typedef skiagm::GM INHERITED;
};
class ClippedCubic2GM : public skiagm::GM {
public:
ClippedCubic2GM() {}
protected:
SkString onShortName() {
return SkString("clippedcubic2");
}
SkISize onISize() { return SkISize::Make(1240, 390); }
void onDraw(SkCanvas* canvas) override {
canvas->save();
canvas->translate(-2, 20);
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 150));
canvas->translate(0, 170);
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 100));
canvas->translate(0, 170);
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 30, 150));
canvas->translate(0, 170);
drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 10, 150));
canvas->restore();
}
void drawOne(SkCanvas* canvas, const SkPath& path, const SkRect& clip) {
SkPaint framePaint, fillPaint;
framePaint.setStyle(SkPaint::kStroke_Style);
canvas->drawRect(clip, framePaint);
canvas->drawPath(path, framePaint);
canvas->save();
canvas->clipRect(clip);
canvas->drawPath(path, fillPaint);
canvas->restore();
}
void onOnceBeforeDraw() override {
fPath.moveTo(69.7030518991886f, 0);
fPath.cubicTo( 69.7030518991886f, 21.831149999999997f,
58.08369508178456f, 43.66448333333333f, 34.8449814469765f, 65.5f);
fPath.cubicTo( 11.608591683531916f, 87.33115f, -0.010765133872116195f, 109.16448333333332f,
-0.013089005235602302f, 131);
fPath.close();
}
SkPath fPath;
private:
typedef skiagm::GM INHERITED;
};
class CubicPathGM : public skiagm::GM {
public:
CubicPathGM() {}
@ -347,3 +399,4 @@ private:
DEF_GM( return new CubicPathGM; )
DEF_GM( return new CubicClosePathGM; )
DEF_GM( return new ClippedCubicGM; )
DEF_GM( return new ClippedCubic2GM; )

View File

@ -270,6 +270,34 @@ static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
}
}
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 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);
x -= D;
SkScalar closest = SK_ScalarMax;
do {
SkScalar loc = ((A * t + B) * t + C) * t;
SkScalar dist = SkScalarAbs(loc - x);
if (closest > dist) {
closest = dist;
bestT = t;
}
lastT = t;
t += loc < x ? step : -step;
step *= 0.5f;
} while (closest > 0.25f && lastT != t);
SkChopCubicAt(pts, tmp, bestT);
}
// srcPts[] must be monotonic in X and Y
void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
SkPoint pts[4];
@ -305,40 +333,29 @@ void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
// are we partially to the left
if (pts[0].fX < clip.fLeft) {
SkPoint tmp[7];
if (SkChopMonoCubicAtX(pts, clip.fLeft, tmp)) {
this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
chop_mono_cubic_at_x(pts, clip.fLeft, tmp);
this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
// tmp[3, 4].fX should all be to the right of clip.fLeft.
// Since we can't trust the numerics of
// the chopper, we force those conditions now
tmp[3].fX = clip.fLeft;
clamp_ge(tmp[4].fX, clip.fLeft);
// tmp[3, 4].fX should all be to the right of clip.fLeft.
// Since we can't trust the numerics of
// the chopper, we force those conditions now
tmp[3].fX = clip.fLeft;
clamp_ge(tmp[4].fX, clip.fLeft);
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 left
this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
return;
}
pts[0] = tmp[3];
pts[1] = tmp[4];
pts[2] = tmp[5];
}
// are we partially to the right
if (pts[3].fX > clip.fRight) {
SkPoint tmp[7];
if (SkChopMonoCubicAtX(pts, clip.fRight, tmp)) {
tmp[3].fX = clip.fRight;
clamp_le(tmp[2].fX, clip.fRight);
chop_mono_cubic_at_x(pts, clip.fRight, tmp);
tmp[3].fX = clip.fRight;
clamp_le(tmp[2].fX, clip.fRight);
this->appendCubic(tmp, reverse);
this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
} else {
// if chopMonoCubicAtX failed, then we may have hit inexact numerics
// so we just clamp against the right
this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
}
this->appendCubic(tmp, reverse);
this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
} else { // wholly inside the clip
this->appendCubic(pts, reverse);
}