--- /dev/null
+#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
+
--- /dev/null
+#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);
+
--- /dev/null
+#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;
+}
+