*/
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
#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]);
}
}
-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++) {
}
static const clipper_proc gProcs[] = {
+ line_intersector,
line_clipper,
quad_clipper,
cubic_clipper
public:
LineClipperView() {
- fProcIndex = 2;
+ fProcIndex = 0;
fCounter = 0;
int x = (640 - W)/2;
virtual void onDraw(SkCanvas* canvas) {
this->drawBG(canvas);
+
+ // fProcIndex = test0(fPts, &fClip);
SkPaint paint, paint1;
paint1.setStyle(SkPaint::kStroke_Style);
gProcs[fProcIndex](fPts, fClip, canvas, paint, paint1);
- if (true) {
+ if (AUTO_ANIMATE) {
this->randPts();
this->inval(NULL);
}
}
}
+///////////////////////////////////////////////////////////////////////////////
+
+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;
#include "SkScan.h"
#include "SkBlitter.h"
#include "SkColorPriv.h"
+#include "SkLineClipper.h"
#include "SkRegion.h"
#include "SkFDot6.h"
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();
}
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);
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);
}
}
#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)
{
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);
}
}
// 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;