aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--samplecode/SampleHairline.cpp26
-rw-r--r--src/core/SkEdgeClipper.cpp20
-rw-r--r--src/core/SkLineClipper.cpp34
-rw-r--r--src/core/SkScan_Antihair.cpp11
-rw-r--r--tests/ClipperTest.cpp89
-rw-r--r--tests/tests_files.mk1
6 files changed, 160 insertions, 21 deletions
diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp
index 9b6bcd5906..6c2fc437df 100644
--- a/samplecode/SampleHairline.cpp
+++ b/samplecode/SampleHairline.cpp
@@ -181,12 +181,14 @@ static int cycle_hairproc_index(int index) {
}
class HairlineView : public SkView {
+ SkMSec fNow;
int fProcIndex;
bool fDoAA;
public:
HairlineView() {
fProcIndex = 0;
fDoAA = true;
+ fNow = 0;
}
protected:
@@ -218,7 +220,9 @@ protected:
virtual void onDraw(SkCanvas* canvas) {
this->drawBG(canvas);
- if (true) {
+ gRand.setSeed(fNow);
+
+ if (false) {
test_chromium_9005();
}
@@ -242,15 +246,19 @@ protected:
bm2.eraseColor(0);
gProcs[fProcIndex].fProc(&c2, paint, bm);
canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), NULL);
-
- fCounter += 1;
- fDoAA = !fDoAA;
- if (fCounter > 50) {
- fProcIndex = cycle_hairproc_index(fProcIndex);
- // todo: signal that we want to rebuild our TITLE
- fCounter = 0;
+
+ SkMSec now = SampleCode::GetAnimTime();
+ if (fNow != now) {
+ fNow = now;
+ fCounter += 1;
+ fDoAA = !fDoAA;
+ if (fCounter > 50) {
+ fProcIndex = cycle_hairproc_index(fProcIndex);
+ // todo: signal that we want to rebuild our TITLE
+ fCounter = 0;
+ }
+ this->inval(NULL);
}
- this->inval(NULL);
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
diff --git a/src/core/SkEdgeClipper.cpp b/src/core/SkEdgeClipper.cpp
index dc136a6b56..d41291d4b7 100644
--- a/src/core/SkEdgeClipper.cpp
+++ b/src/core/SkEdgeClipper.cpp
@@ -235,23 +235,31 @@ static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
// SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3);
SkASSERT(c0 < target && target < c3);
- SkScalar D = c0;
+ SkScalar D = c0 - target;
SkScalar A = c3 + 3*(c1 - c2) - c0;
SkScalar B = 3*(c2 - c1 - c1 + c0);
SkScalar C = 3*(c1 - c0);
+ const SkScalar TOLERANCE = SK_Scalar1 / 4096;
SkScalar minT = 0;
SkScalar maxT = SK_Scalar1;
- for (int i = 0; i < 8; i++) {
- SkScalar mid = SkScalarAve(minT, maxT);
- SkScalar coord = eval_cubic_coeff(A, B, C, D, mid);
- if (coord < target) {
+ SkScalar mid;
+ int i;
+ for (i = 0; i < 16; i++) {
+ mid = SkScalarAve(minT, maxT);
+ SkScalar delta = eval_cubic_coeff(A, B, C, D, mid);
+ if (delta < 0) {
minT = mid;
+ delta = -delta;
} else {
maxT = mid;
}
+ if (delta < TOLERANCE) {
+ break;
+ }
}
- *t = SkScalarAve(minT, maxT);
+ *t = mid;
+// SkDebugf("-- evalCubicAt %d delta %g\n", i, eval_cubic_coeff(A, B, C, D, *t));
return true;
}
diff --git a/src/core/SkLineClipper.cpp b/src/core/SkLineClipper.cpp
index 58bf6033a7..057f546d77 100644
--- a/src/core/SkLineClipper.cpp
+++ b/src/core/SkLineClipper.cpp
@@ -24,21 +24,37 @@ static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) {
///////////////////////////////////////////////////////////////////////////////
+static inline bool nestedLT(SkScalar a, SkScalar b, SkScalar dim) {
+ return a <= b && (a < b || dim > 0);
+}
+
+// returns true if outer contains inner, even if inner is empty.
+// note: outer.contains(inner) always returns false if inner is empty.
+static inline bool containsNoEmptyCheck(const SkRect& outer,
+ const SkRect& inner) {
+ return outer.fLeft <= inner.fLeft && outer.fTop <= inner.fTop &&
+ outer.fRight >= inner.fRight && outer.fBottom >= inner.fBottom;
+}
+
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 (containsNoEmptyCheck(clip, bounds)) {
if (src != dst) {
memcpy(dst, src, 2 * sizeof(SkPoint));
}
return true;
}
+ // check for no overlap, and only permit coincident edges if the line
+ // and the edge are colinear
+ if (nestedLT(bounds.fRight, clip.fLeft, bounds.width()) ||
+ nestedLT(clip.fRight, bounds.fLeft, bounds.width()) ||
+ nestedLT(bounds.fBottom, clip.fTop, bounds.height()) ||
+ nestedLT(clip.fBottom, bounds.fTop, bounds.height())) {
+ return false;
+ }
int index0, index1;
@@ -70,7 +86,9 @@ bool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip,
}
// 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) {
+ if ((tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight) &&
+ tmp[index0].fX < tmp[index1].fX) {
+ // only reject if we have a non-zero width
return false;
}
@@ -80,6 +98,10 @@ bool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip,
if (tmp[index1].fX > clip.fRight) {
tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight));
}
+#ifdef SK_DEBUG
+ bounds.set(tmp, 2);
+ SkASSERT(containsNoEmptyCheck(clip, bounds));
+#endif
memcpy(dst, tmp, sizeof(tmp));
return true;
}
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
index 13cdc09e79..0003298b38 100644
--- a/src/core/SkScan_Antihair.cpp
+++ b/src/core/SkScan_Antihair.cpp
@@ -429,6 +429,17 @@ void SkScan::AntiHairLine(const SkPoint& pt0, const SkPoint& pt1,
if (clip) {
SkRect clipBounds;
clipBounds.set(clip->getBounds());
+ /* We perform integral clipping later on, but we do a scalar clip first
+ to ensure that our coordinates are expressible in fixed/integers.
+
+ antialiased hairlines can draw up to 1/2 of a pixel outside of
+ their bounds, so we need to outset the clip before calling the
+ clipper. To make the numerics safer, we outset by a whole pixel,
+ since the 1/2 pixel boundary is important to the antihair blitter,
+ we don't want to risk numerical fate by chopping on that edge.
+ */
+ clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
+
if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
return;
}
diff --git a/tests/ClipperTest.cpp b/tests/ClipperTest.cpp
new file mode 100644
index 0000000000..66301fcfdf
--- /dev/null
+++ b/tests/ClipperTest.cpp
@@ -0,0 +1,89 @@
+#include "Test.h"
+#include "SkPath.h"
+#include "SkLineClipper.h"
+#include "SkEdgeClipper.h"
+
+static void test_intersectline(skiatest::Reporter* reporter) {
+ static const SkScalar L = 0;
+ static const SkScalar T = 0;
+ static const SkScalar R = SkIntToScalar(100);
+ static const SkScalar B = SkIntToScalar(100);
+ static const SkScalar CX = SkScalarHalf(L + R);
+ static const SkScalar CY = SkScalarHalf(T + B);
+ static const SkRect gR = { L, T, R, B };
+
+ size_t i;
+ SkPoint dst[2];
+
+ static const SkPoint gEmpty[] = {
+ // sides
+ { L, CY }, { L - 10, CY },
+ { R, CY }, { R + 10, CY },
+ { CX, T }, { CX, T - 10 },
+ { CX, B }, { CX, B + 10 },
+ // corners
+ { L, T }, { L - 10, T - 10 },
+ { L, B }, { L - 10, B + 10 },
+ { R, T }, { R + 10, T - 10 },
+ { R, B }, { R + 10, B + 10 },
+ };
+ for (i = 0; i < SK_ARRAY_COUNT(gEmpty); i += 2) {
+ bool valid = SkLineClipper::IntersectLine(&gEmpty[i], gR, dst);
+ if (valid) {
+ SkDebugf("----- [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+ }
+ REPORTER_ASSERT(reporter, !valid);
+ }
+
+ static const SkPoint gFull[] = {
+ // diagonals, chords
+ { L, T }, { R, B },
+ { L, B }, { R, T },
+ { CX, T }, { CX, B },
+ { L, CY }, { R, CY },
+ { CX, T }, { R, CY },
+ { CX, T }, { L, CY },
+ { L, CY }, { CX, B },
+ { R, CY }, { CX, B },
+ // edges
+ { L, T }, { L, B },
+ { R, T }, { R, B },
+ { L, T }, { R, T },
+ { L, B }, { R, B },
+ };
+ for (i = 0; i < SK_ARRAY_COUNT(gFull); i += 2) {
+ bool valid = SkLineClipper::IntersectLine(&gFull[i], gR, dst);
+ if (!valid || memcmp(&gFull[i], dst, sizeof(dst))) {
+ SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+ }
+ REPORTER_ASSERT(reporter, valid && !memcmp(&gFull[i], dst, sizeof(dst)));
+ }
+
+ static const SkPoint gPartial[] = {
+ { L - 10, CY }, { CX, CY }, { L, CY }, { CX, CY },
+ { CX, T - 10 }, { CX, CY }, { CX, T }, { CX, CY },
+ { R + 10, CY }, { CX, CY }, { R, CY }, { CX, CY },
+ { CX, B + 10 }, { CX, CY }, { CX, B }, { CX, CY },
+ // extended edges
+ { L, T - 10 }, { L, B + 10 }, { L, T }, { L, B },
+ { R, T - 10 }, { R, B + 10 }, { R, T }, { R, B },
+ { L - 10, T }, { R + 10, T }, { L, T }, { R, T },
+ { L - 10, B }, { R + 10, B }, { L, B }, { R, B },
+ };
+ for (i = 0; i < SK_ARRAY_COUNT(gPartial); i += 4) {
+ bool valid = SkLineClipper::IntersectLine(&gPartial[i], gR, dst);
+ if (!valid || memcmp(&gPartial[i+2], dst, sizeof(dst))) {
+ SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+ }
+ REPORTER_ASSERT(reporter, valid &&
+ !memcmp(&gPartial[i+2], dst, sizeof(dst)));
+ }
+
+}
+
+void TestClipper(skiatest::Reporter* reporter) {
+ test_intersectline(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Clipper", TestClipperClass, TestClipper)
diff --git a/tests/tests_files.mk b/tests/tests_files.mk
index 04fc013df5..79e7efed9c 100644
--- a/tests/tests_files.mk
+++ b/tests/tests_files.mk
@@ -1,4 +1,5 @@
SOURCE := \
+ ClipperTest.cpp \
GeometryTest.cpp \
MathTest.cpp \
MatrixTest.cpp \