retool clipping in hairlines to catch huge coordinates
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 19 Nov 2009 20:46:39 +0000 (20:46 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 19 Nov 2009 20:46:39 +0000 (20:46 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@436 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkLineClipper.h
samplecode/SampleLineClipper.cpp
src/core/SkLineClipper.cpp
src/core/SkScan_Antihair.cpp
src/core/SkScan_Hairline.cpp

index d9389462750d602946082bd52aaea724fc174e41..4c23781e67a7f2fe83eb15a5ee3350f4df2ed468 100644 (file)
@@ -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
index bb3e3409162e3fb713c2af7b056fe3e7efa9d230..09606f6b89a27d3d918ac5db2faf5625ad617d23 100644 (file)
 #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);
         }
index ef37a14bb0a788ab290c3bc15608fedc715f1906..9164f7c13f3087fd2a608a8ad49745b3ec3dee95 100644 (file)
@@ -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;
index 2f285283ac8ef23399830931d6628d46bd13ea06..13cdc09e79d575caf12c10820b59b19ab7d913c2 100644 (file)
@@ -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);
     }
 }
 
index b95996820e59c7cb7cafff539f1ad335b7532110..0dee814513ff3fe9b18d8ea905b00c5b7eb3bf7a 100644 (file)
@@ -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;