diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-12-21 19:36:21 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-12-21 19:36:21 +0000 |
commit | ee068aae552e8cfb3e23f9c972a377e75a07e822 (patch) | |
tree | da528dd89364d2921b8404f0cd85f9e76002f526 | |
parent | e42b1d54bf9373c578dcf6067409fb4cc5529297 (diff) |
add unittest for invariants for empty paths, still need to think about
hairlines in those cases
git-svn-id: http://skia.googlecode.com/svn/trunk@2919 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | gyp/tests.gyp | 1 | ||||
-rw-r--r-- | src/core/SkScan_AntiPath.cpp | 3 | ||||
-rw-r--r-- | tests/EmptyPathTest.cpp | 154 |
3 files changed, 158 insertions, 0 deletions
diff --git a/gyp/tests.gyp b/gyp/tests.gyp index e47bec6936..187eea5dbf 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -29,6 +29,7 @@ '../tests/DataRefTest.cpp', '../tests/DequeTest.cpp', '../tests/DrawBitmapRectTest.cpp', + '../tests/EmptyPathTest.cpp', '../tests/FillPathTest.cpp', '../tests/FlateTest.cpp', '../tests/GeometryTest.cpp', diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp index bf9866ec97..7b81716b6d 100644 --- a/src/core/SkScan_AntiPath.cpp +++ b/src/core/SkScan_AntiPath.cpp @@ -567,6 +567,9 @@ void SkScan::AntiFillPath(const SkPath& path, const SkRegion& clip, SkIRect ir; path.getBounds().roundOut(&ir); if (ir.isEmpty()) { + if (path.isInverseFillType()) { + blitter->blitRegion(clip); + } return; } diff --git a/tests/EmptyPathTest.cpp b/tests/EmptyPathTest.cpp new file mode 100644 index 0000000000..59fafbbc48 --- /dev/null +++ b/tests/EmptyPathTest.cpp @@ -0,0 +1,154 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkPath.h" +#include "SkCanvas.h" + +static void appendStr(SkString* str, const SkPaint& paint) { + str->appendf(" style[%d] cap[%d] join[%d] antialias[%d]", + paint.getStyle(), paint.getStrokeCap(), + paint.getStrokeJoin(), paint.isAntiAlias()); +} + +static void appendStr(SkString* str, const SkPath& path) { + str->appendf(" filltype[%d] ptcount[%d]", + path.getFillType(), path.countPoints()); +} + +#define DIMENSION 32 + +static void drawAndTest(skiatest::Reporter* reporter, const SkPath& path, + const SkPaint& paint, bool shouldDraw) { + SkBitmap bm; + // explicitly specify a trim rowbytes, so we have no padding on each row + bm.setConfig(SkBitmap::kARGB_8888_Config, DIMENSION, DIMENSION, DIMENSION*4); + bm.allocPixels(); + bm.eraseColor(0); + + SkCanvas canvas(bm); + SkPaint p(paint); + p.setColor(SK_ColorWHITE); + + canvas.drawPath(path, p); + + size_t count = DIMENSION * DIMENSION; + const SkPMColor* ptr = bm.getAddr32(0, 0); + + SkPMColor andValue = ~0; + SkPMColor orValue = 0; + for (size_t i = 0; i < count; ++i) { + SkPMColor c = ptr[i]; + andValue &= c; + orValue |= c; + } + + // success means we drew everywhere or nowhere (depending on shouldDraw) + bool success = shouldDraw ? (~0 == andValue) : (0 == orValue); + + if (!success) { + SkString str; + if (shouldDraw) { + str.set("Path expected to draw everywhere, but didn't. "); + } else { + str.set("Path expected to draw nowhere, but did. "); + } + appendStr(&str, paint); + appendStr(&str, path); + reporter->report(str.c_str(), skiatest::Reporter::kFailed); + +// uncomment this if you want to step in to see the failure +// canvas.drawPath(path, p); + } +} + +static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw) { + static const SkPaint::Cap gCaps[] = { + SkPaint::kButt_Cap, + SkPaint::kRound_Cap, + SkPaint::kSquare_Cap + }; + static const SkPaint::Join gJoins[] = { + SkPaint::kMiter_Join, + SkPaint::kRound_Join, + SkPaint::kBevel_Join + }; + static const SkPaint::Style gStyles[] = { + SkPaint::kFill_Style, + SkPaint::kStroke_Style, + SkPaint::kStrokeAndFill_Style + }; + for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) { + for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) { + for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { + SkPaint paint; + paint.setStrokeWidth(SkIntToScalar(10)); + + paint.setStrokeCap(gCaps[cap]); + paint.setStrokeJoin(gJoins[join]); + paint.setStyle(gStyles[style]); + + paint.setAntiAlias(false); + drawAndTest(reporter, path, paint, shouldDraw); + paint.setAntiAlias(true); + drawAndTest(reporter, path, paint, shouldDraw); + } + } + } +} + +#define CX (SkIntToScalar(DIMENSION) / 2) +#define CY (SkIntToScalar(DIMENSION) / 2) + +static void make_empty(SkPath* path) {} +static void make_move(SkPath* path) { path->moveTo(CX, CY); } +static void make_line(SkPath* path) { path->moveTo(CX, CY); path->lineTo(CX, CY); } +static void make_quad(SkPath* path) { path->moveTo(CX, CY); path->quadTo(CX, CY, CX, CY); } +static void make_cubic(SkPath* path) { path->moveTo(CX, CY); path->cubicTo(CX, CY, CX, CY, CX, CY); } + +/* Two invariants are tested: How does an empty/degenerate path draw? + * - if the path is drawn inverse, it should draw everywhere + * - if the path is drawn non-inverse, it should draw nowhere + * + * Things to iterate on: + * - path (empty, degenerate line/quad/cubic w/ and w/o close + * - paint style + * - path filltype + * - path stroke variants (e.g. caps, joins, width) + */ +static void test_emptydrawing(skiatest::Reporter* reporter) { + static void (*gMakeProc[])(SkPath*) = { + make_empty, make_move, make_line, make_quad, make_cubic + }; + static SkPath::FillType gFills[] = { + SkPath::kWinding_FillType, + SkPath::kEvenOdd_FillType, + SkPath::kInverseWinding_FillType, + SkPath::kInverseEvenOdd_FillType + }; + for (int doClose = 0; doClose < 2; ++doClose) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProc); ++i) { + SkPath path; + gMakeProc[i](&path); + if (doClose) { + path.close(); + } + for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { + path.setFillType(gFills[fill]); + bool shouldDraw = path.isInverseFillType(); + iter_paint(reporter, path, shouldDraw); + } + } + } +} + +static void TestEmptyPath(skiatest::Reporter* reporter) { + test_emptydrawing(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("EmptyPath", TestEmptyPathClass, TestEmptyPath) |