add lineclipper, and test case
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 16 Nov 2009 20:39:43 +0000 (20:39 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 16 Nov 2009 20:39:43 +0000 (20:39 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@427 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkLineClipper.h [new file with mode: 0644]
samplecode/SampleLineClipper.cpp [new file with mode: 0644]
src/core/SkLineClipper.cpp [new file with mode: 0644]

diff --git a/include/core/SkLineClipper.h b/include/core/SkLineClipper.h
new file mode 100644 (file)
index 0000000..d938946
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef SkLineClipper_DEFINED
+#define SkLineClipper_DEFINED
+
+#include "SkRect.h"
+#include "SkPoint.h"
+
+class SkLineClipper {
+public:
+    enum {
+        kMaxPoints = 4
+    };
+
+    /*  Clip the line pts[0]...pts[1] against clip, ignoring segments that
+        lie completely above or below the clip. For portions to the left or
+        right, turn those into vertical line segments that are aligned to the
+        edge of the clip.
+         
+        Return the number of line segments that result, and store the end-points
+        of those segments sequentially in lines as follows:
+            1st segment: lines[0]..lines[1]
+            2nd segment: lines[1]..lines[2]
+            3rd segment: lines[2]..lines[3]
+     */
+    static int ClipLine(const SkPoint pts[2], const SkRect& clip,
+                        SkPoint lines[kMaxPoints]);
+};
+
+#endif
+
diff --git a/samplecode/SampleLineClipper.cpp b/samplecode/SampleLineClipper.cpp
new file mode 100644 (file)
index 0000000..e990c86
--- /dev/null
@@ -0,0 +1,127 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkRandom.h"
+#include "SkLineClipper.h"
+
+enum {
+    W = 640/4,
+    H = 480/4
+};
+
+class LineClipperView : public SkView {
+    SkRect      fClip;
+    SkRandom    fRand;
+    SkPoint     fPts[2];
+
+    void randPts() {
+        fPts[0].set(fRand.nextUScalar1() * 640, fRand.nextUScalar1() * 480);
+        fPts[1].set(fRand.nextUScalar1() * 640, fRand.nextUScalar1() * 480);
+    }
+
+public:
+       LineClipperView() {
+        int x = (640 - W)/2;
+        int y = (480 - H)/2;
+        fClip.set(x, y, x + W, y + H);
+        this->randPts();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "LineClipper");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    static void drawVLine(SkCanvas* canvas, SkScalar x, const SkPaint& paint) {
+        canvas->drawLine(x, -999, x, 999, paint);
+    }
+    
+    static void drawHLine(SkCanvas* canvas, SkScalar y, const SkPaint& paint) {
+        canvas->drawLine(-999, y, 999, y, paint);
+    }
+    
+    static void check_lineclipper(int count, const SkPoint pts[],
+                                  const SkRect& clip) {
+        if (count > 0) {
+            for (int i = 0; i <= count; i++) {
+                SkASSERT(pts[i].fX >= clip.fLeft);
+                SkASSERT(pts[i].fX <= clip.fRight);
+                SkASSERT(pts[i].fY >= clip.fTop);
+                SkASSERT(pts[i].fY <= clip.fBottom);
+            }
+        }
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+
+        SkPaint paint;
+        
+        drawVLine(canvas, fClip.fLeft + SK_ScalarHalf, paint);
+        drawVLine(canvas, fClip.fRight - SK_ScalarHalf, paint);
+        drawHLine(canvas, fClip.fTop + SK_ScalarHalf, paint);
+        drawHLine(canvas, fClip.fBottom - SK_ScalarHalf, paint);
+        
+        paint.setColor(SK_ColorLTGRAY);
+        canvas->drawRect(fClip, paint);
+        
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorBLUE);
+        paint.setStrokeWidth(SkIntToScalar(3));
+        paint.setStrokeCap(SkPaint::kRound_Cap);
+        SkPoint pts[SkLineClipper::kMaxPoints];
+        int count = SkLineClipper::ClipLine(fPts, fClip, pts);
+        check_lineclipper(count, pts, fClip);
+        for (int i = 0; i < count; i++) {
+            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, &pts[i], paint);
+        }
+
+        paint.setColor(SK_ColorRED);
+        paint.setStrokeWidth(0);
+        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, paint);
+        
+        if (true) {
+            this->randPts();
+            this->inval(NULL);
+        }
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->randPts();
+        this->inval(NULL);
+        return NULL;
+    }
+        
+    virtual bool onClick(Click* click) {
+        return false;
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LineClipperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/src/core/SkLineClipper.cpp b/src/core/SkLineClipper.cpp
new file mode 100644 (file)
index 0000000..ef37a14
--- /dev/null
@@ -0,0 +1,115 @@
+#include "SkLineClipper.h"
+
+// return X coordinate of intersection with horizontal line at Y
+static SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) {
+    SkScalar dy = src[1].fY - src[0].fY;
+    if (SkScalarNearlyZero(dy)) {
+        return SkScalarAve(src[0].fX, src[1].fX);
+    } else {
+        return src[0].fX + SkScalarMulDiv(Y - src[0].fY, src[1].fX - src[0].fX,
+                                          dy);
+    }
+}
+
+// return Y coordinate of intersection with vertical line at X
+static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) {
+    SkScalar dx = src[1].fX - src[0].fX;
+    if (SkScalarNearlyZero(dx)) {
+        return SkScalarAve(src[0].fY, src[1].fY);
+    } else {
+        return src[0].fY + SkScalarMulDiv(X - src[0].fX, src[1].fY - src[0].fY,
+                                          dx);
+    }
+}
+
+int SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip,
+                            SkPoint lines[]) {
+    int index0, index1;
+
+    if (pts[0].fY < pts[1].fY) {
+        index0 = 0;
+        index1 = 1;
+    } else {
+        index0 = 1;
+        index1 = 0;
+    }
+
+    // Check if we're completely clipped out in Y (above or below
+
+    if (pts[index1].fY <= clip.fTop) {  // we're above the clip
+        return 0;
+    }
+    if (pts[index0].fY >= clip.fBottom) {  // we're below the clip
+        return 0;
+    }
+    
+    // Chop in Y to produce a single segment, stored in tmp[0..1]
+
+    SkPoint tmp[2];
+    memcpy(tmp, pts, sizeof(tmp));
+
+    // now compute intersections
+    if (pts[index0].fY < clip.fTop) {
+        tmp[index0].set(sect_with_horizontal(pts, clip.fTop), clip.fTop);
+    }
+    if (tmp[index1].fY > clip.fBottom) {
+        tmp[index1].set(sect_with_horizontal(pts, clip.fBottom), clip.fBottom);
+    }
+
+    // Chop it into 1..3 segments that are wholly within the clip in X.
+
+    // temp storage for up to 3 segments
+    SkPoint resultStorage[kMaxPoints];
+    SkPoint* result;    // points to our results, either tmp or resultStorage
+    int lineCount = 1;
+
+    if (pts[0].fX < pts[1].fX) {
+        index0 = 0;
+        index1 = 1;
+    } else {
+        index0 = 1;
+        index1 = 0;
+    }
+    
+    if (tmp[index1].fX <= clip.fLeft) {  // wholly to the left
+        tmp[0].fX = tmp[1].fX = clip.fLeft;
+        result = tmp;
+    } else if (tmp[index0].fX >= clip.fRight) {    // wholly to the right
+        tmp[0].fX = tmp[1].fX = clip.fRight;
+        result = tmp;
+    } else {
+        result = resultStorage;
+        SkPoint* r = result;
+        
+        if (tmp[index0].fX < clip.fLeft) {
+            r->set(clip.fLeft, tmp[index0].fY);
+            r += 1;
+            r->set(clip.fLeft, sect_with_vertical(pts, clip.fLeft));
+        } else {
+            *r = tmp[index0];
+        }
+        r += 1;
+
+        if (tmp[index1].fX > clip.fRight) {
+            r->set(clip.fRight, sect_with_vertical(pts, clip.fRight));
+            r += 1;
+            r->set(clip.fRight, tmp[index1].fY);
+        } else {
+            *r = tmp[index1];
+        }
+
+        lineCount = r - result;
+    }
+
+    // Now copy the results into the caller's lines[] parameter
+    if (0 == index1) {
+        // copy the pts in reverse order to maintain winding order
+        for (int i = 0; i <= lineCount; i++) {
+            lines[lineCount - i] = result[i];
+        }
+    } else {
+        memcpy(lines, result, (lineCount + 1) * sizeof(SkPoint));
+    }
+    return lineCount;
+}
+