From e28ff55d980d2992618b6b721c848aba96cf759a Mon Sep 17 00:00:00 2001 From: "reed@android.com" Date: Thu, 19 Nov 2009 20:46:39 +0000 Subject: [PATCH] retool clipping in hairlines to catch huge coordinates git-svn-id: http://skia.googlecode.com/svn/trunk@436 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkLineClipper.h | 11 ++++ samplecode/SampleLineClipper.cpp | 34 ++++++++++-- src/core/SkLineClipper.cpp | 62 ++++++++++++++++++++++ src/core/SkScan_Antihair.cpp | 89 ++++++++++++++------------------ src/core/SkScan_Hairline.cpp | 69 ++++++++++++++----------- 5 files changed, 181 insertions(+), 84 deletions(-) diff --git a/include/core/SkLineClipper.h b/include/core/SkLineClipper.h index d938946275..4c23781e67 100644 --- a/include/core/SkLineClipper.h +++ b/include/core/SkLineClipper.h @@ -23,6 +23,17 @@ public: */ static int ClipLine(const SkPoint pts[2], const SkRect& clip, SkPoint lines[kMaxPoints]); + + /* Intersect the line segment against the rect. If there is a non-empty + resulting segment, return true and set dst[] to that segment. If not, + return false and ignore dst[]. + + ClipLine is specialized for scan-conversion, as it adds vertical + segments on the sides to show where the line extended beyond the + left or right sides. IntersectLine does not. + */ + static bool IntersectLine(const SkPoint src[2], const SkRect& clip, + SkPoint dst[2]); }; #endif diff --git a/samplecode/SampleLineClipper.cpp b/samplecode/SampleLineClipper.cpp index bb3e340916..09606f6b89 100644 --- a/samplecode/SampleLineClipper.cpp +++ b/samplecode/SampleLineClipper.cpp @@ -17,6 +17,18 @@ #include "SkLineClipper.h" #include "SkEdgeClipper.h" +#define AUTO_ANIMATE true + +static int test0(SkPoint pts[], SkRect* clip) { + pts[0].set(200000, 140); + pts[1].set(-740000, 483); + pts[2].set(1.00000102e-06f, 9.10000017e-05f); + clip->set(0, 0, 640, 480); + return 2; +} + +/////////////////////////////////////////////////////////////////////////////// + static void drawQuad(SkCanvas* canvas, const SkPoint pts[3], const SkPaint& p) { SkPath path; path.moveTo(pts[0]); @@ -47,10 +59,21 @@ static void check_clipper(int count, const SkPoint pts[], const SkRect& clip) { } } -static void line_clipper(const SkPoint src[], const SkRect& clip, - SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) { +static void line_intersector(const SkPoint src[], const SkRect& clip, + SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) { canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1); + + SkPoint dst[2]; + if (SkLineClipper::IntersectLine(src, clip, dst)) { + check_clipper(2, dst, clip); + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, dst, p0); + } +} +static void line_clipper(const SkPoint src[], const SkRect& clip, + SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) { + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1); + SkPoint dst[SkLineClipper::kMaxPoints]; int count = SkLineClipper::ClipLine(src, clip, dst); for (int i = 0; i < count; i++) { @@ -110,6 +133,7 @@ static void cubic_clipper(const SkPoint src[], const SkRect& clip, } static const clipper_proc gProcs[] = { + line_intersector, line_clipper, quad_clipper, cubic_clipper @@ -139,7 +163,7 @@ class LineClipperView : public SkView { public: LineClipperView() { - fProcIndex = 2; + fProcIndex = 0; fCounter = 0; int x = (640 - W)/2; @@ -172,6 +196,8 @@ protected: virtual void onDraw(SkCanvas* canvas) { this->drawBG(canvas); + + // fProcIndex = test0(fPts, &fClip); SkPaint paint, paint1; @@ -194,7 +220,7 @@ protected: paint1.setStyle(SkPaint::kStroke_Style); gProcs[fProcIndex](fPts, fClip, canvas, paint, paint1); - if (true) { + if (AUTO_ANIMATE) { this->randPts(); this->inval(NULL); } diff --git a/src/core/SkLineClipper.cpp b/src/core/SkLineClipper.cpp index ef37a14bb0..9164f7c13f 100644 --- a/src/core/SkLineClipper.cpp +++ b/src/core/SkLineClipper.cpp @@ -22,6 +22,68 @@ static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) { } } +/////////////////////////////////////////////////////////////////////////////// + +bool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip, + SkPoint dst[2]) { + SkRect bounds; + + bounds.set(src, 2); + if (bounds.fLeft >= clip.fRight || clip.fLeft >= bounds.fRight || + bounds.fTop >= clip.fBottom || clip.fTop >= bounds.fBottom) { + return false; + } + if (clip.contains(bounds)) { + if (src != dst) { + memcpy(dst, src, 2 * sizeof(SkPoint)); + } + return true; + } + + int index0, index1; + + if (src[0].fY < src[1].fY) { + index0 = 0; + index1 = 1; + } else { + index0 = 1; + index1 = 0; + } + + SkPoint tmp[2]; + memcpy(tmp, src, sizeof(tmp)); + + // now compute Y intersections + if (tmp[index0].fY < clip.fTop) { + tmp[index0].set(sect_with_horizontal(src, clip.fTop), clip.fTop); + } + if (tmp[index1].fY > clip.fBottom) { + tmp[index1].set(sect_with_horizontal(src, clip.fBottom), clip.fBottom); + } + + if (tmp[0].fX < tmp[1].fX) { + index0 = 0; + index1 = 1; + } else { + index0 = 1; + index1 = 0; + } + + // check for quick-reject in X again, now that we may have been chopped + if (tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight) { + return false; + } + + if (tmp[index0].fX < clip.fLeft) { + tmp[index0].set(clip.fLeft, sect_with_vertical(src, clip.fLeft)); + } + if (tmp[index1].fX > clip.fRight) { + tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight)); + } + memcpy(dst, tmp, sizeof(tmp)); + return true; +} + int SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip, SkPoint lines[]) { int index0, index1; diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp index 2f285283ac..13cdc09e79 100644 --- a/src/core/SkScan_Antihair.cpp +++ b/src/core/SkScan_Antihair.cpp @@ -18,6 +18,7 @@ #include "SkScan.h" #include "SkBlitter.h" #include "SkColorPriv.h" +#include "SkLineClipper.h" #include "SkRegion.h" #include "SkFDot6.h" @@ -423,33 +424,41 @@ void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1, build_gamma_table(); #endif - SkFDot6 x0 = SkScalarToFDot6(pt0.fX); - SkFDot6 y0 = SkScalarToFDot6(pt0.fY); - SkFDot6 x1 = SkScalarToFDot6(pt1.fX); - SkFDot6 y1 = SkScalarToFDot6(pt1.fY); + SkPoint pts[2] = { pt0, pt1 }; - if (clip) - { - SkFDot6 left = SkMin32(x0, x1); - SkFDot6 top = SkMin32(y0, y1); - SkFDot6 right = SkMax32(x0, x1); - SkFDot6 bottom = SkMax32(y0, y1); - SkIRect ir; + if (clip) { + SkRect clipBounds; + clipBounds.set(clip->getBounds()); + if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) { + return; + } + } + + SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); + SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); + SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); + SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); + + if (clip) { + SkFDot6 left = SkMin32(x0, x1); + SkFDot6 top = SkMin32(y0, y1); + SkFDot6 right = SkMax32(x0, x1); + SkFDot6 bottom = SkMax32(y0, y1); + SkIRect ir; ir.set( SkFDot6Floor(left) - 1, SkFDot6Floor(top) - 1, SkFDot6Ceil(right) + 1, SkFDot6Ceil(bottom) + 1); - if (clip->quickReject(ir)) + if (clip->quickReject(ir)) { return; - if (!clip->quickContains(ir)) - { + } + if (!clip->quickContains(ir)) { SkRegion::Cliperator iter(*clip, ir); const SkIRect* r = &iter.rect(); - while (!iter.done()) - { + while (!iter.done()) { do_anti_hairline(x0, y0, x1, y1, r, blitter); iter.next(); } @@ -462,19 +471,6 @@ void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1, void SkScan::AntiHairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) { - if (clip) - { - SkIRect ir; - SkRect r = rect; - - r.inset(-SK_Scalar1/2, -SK_Scalar1/2); - r.roundOut(&ir); - if (clip->quickReject(ir)) - return; - if (clip->quickContains(ir)) - clip = NULL; - } - SkPoint p0, p1; p0.set(rect.fLeft, rect.fTop); @@ -631,41 +627,32 @@ static void antifillrect(const SkRect& r, SkBlitter* blitter) { those to our version of antifillrect, which converts it into an XRect and then calls the blit. */ -void SkScan::AntiFillRect(const SkRect& r, const SkRegion* clip, +void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip, SkBlitter* blitter) { if (clip) { + SkRect newR; + newR.set(clip->getBounds()); + if (!newR.intersect(origR)) { + return; + } + SkIRect outerBounds; - r.roundOut(&outerBounds); + newR.roundOut(&outerBounds); if (clip->isRect()) { - const SkIRect& clipBounds = clip->getBounds(); - - if (clipBounds.contains(outerBounds)) { - antifillrect(r, blitter); - } else { - SkRect tmpR; - // this keeps our original edges fractional - tmpR.set(clipBounds); - if (tmpR.intersect(r)) { - antifillrect(tmpR, blitter); - } - } + antifillrect(newR, blitter); } else { SkRegion::Cliperator clipper(*clip, outerBounds); - const SkIRect& rr = clipper.rect(); - while (!clipper.done()) { - SkRect tmpR; - // this keeps our original edges fractional - tmpR.set(rr); - if (tmpR.intersect(r)) { - antifillrect(tmpR, blitter); + newR.set(clipper.rect()); + if (newR.intersect(origR)) { + antifillrect(newR, blitter); } clipper.next(); } } } else { - antifillrect(r, blitter); + antifillrect(origR, blitter); } } diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp index b95996820e..0dee814513 100644 --- a/src/core/SkScan_Hairline.cpp +++ b/src/core/SkScan_Hairline.cpp @@ -19,6 +19,7 @@ #include "SkBlitter.h" #include "SkRegion.h" #include "SkFDot6.h" +#include "SkLineClipper.h" static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter) { @@ -43,38 +44,47 @@ static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitte void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* clip, SkBlitter* blitter) { SkBlitterClipper clipper; + SkRect r; + SkIRect clipR, ptsR; + SkPoint pts[2] = { pt0, pt1 }; + + if (clip) { + // Perform a clip in scalar space, so we catch huge values which might + // be missed after we convert to SkFDot6 (overflow) + r.set(clip->getBounds()); + if (!SkLineClipper::IntersectLine(pts, r, pts)) { + return; + } + } - SkFDot6 x0 = SkScalarToFDot6(pt0.fX); - SkFDot6 y0 = SkScalarToFDot6(pt0.fY); - SkFDot6 x1 = SkScalarToFDot6(pt1.fX); - SkFDot6 y1 = SkScalarToFDot6(pt1.fY); - - if (clip) - { - SkRect r; - SkIRect ir; - SkPoint pts[2]; - - pts[0] = pt0; - pts[1] = pt1; - r.set(pts, 2); - r.roundOut(&ir); - - // if we're given a horizontal or vertical line - // this rect could be empty (in area), in which case - // clip->quickReject() will always return true. - // hence we bloat the rect to avoid that case - if (ir.width() == 0) - ir.fRight += 1; - if (ir.height() == 0) - ir.fBottom += 1; - - if (clip->quickReject(ir)) + SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); + SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); + SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); + SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); + + if (clip) { + // now perform clipping again, as the rounding to dot6 can wiggle us + // our rects are really dot6 rects, but since we've already used + // lineclipper, we know they will fit in 32bits (26.6) + const SkIRect& bounds = clip->getBounds(); + + clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop), + SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom)); + ptsR.set(x0, y0, x1, y1); + ptsR.sort(); + + // outset the right and bottom, to account for how hairlines are + // actually drawn, which may hit the pixel to the right or below of + // the coordinate + ptsR.fRight += SK_FDot61; + ptsR.fBottom += SK_FDot61; + + if (!SkIRect::Intersects(ptsR, clipR)) { return; - if (clip->quickContains(ir)) + } + if (clip->isRect() && clipR.contains(ptsR)) { clip = NULL; - else - { + } else { blitter = clipper.apply(blitter, clip); } } @@ -120,6 +130,7 @@ void SkScan::HairLine(const SkPoint& pt0, const SkPoint& pt1, const SkRegion* cl // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right // and double-hit the top-left. +// TODO: handle huge coordinates on rect (before calling SkScalarToFixed) void SkScan::HairRect(const SkRect& rect, const SkRegion* clip, SkBlitter* blitter) { SkBlitterClipper clipper; -- 2.34.1