add lineclipper, and test case

git-svn-id: http://skia.googlecode.com/svn/trunk@427 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@android.com 2009-11-16 20:39:43 +00:00
Родитель e72fee513a
Коммит 70149060a7
3 изменённых файлов: 271 добавлений и 0 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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);

115
src/core/SkLineClipper.cpp Normal file
Просмотреть файл

@ -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;
}