diff options
author | Derek Sollenberger <djsollen@google.com> | 2012-01-18 08:56:56 -0500 |
---|---|---|
committer | Derek Sollenberger <derek@android.com> | 2012-02-06 14:14:40 -0500 |
commit | 1cab2921ab279367f8206cdadc9259d12e603548 (patch) | |
tree | 2852f9dc2481f639122e18fc7831ae6ca43d6d5a /tests | |
parent | d7176fd5571bc9878d3cdac8696eaa35ec170d9d (diff) | |
download | external_skia-1cab2921ab279367f8206cdadc9259d12e603548.zip external_skia-1cab2921ab279367f8206cdadc9259d12e603548.tar.gz external_skia-1cab2921ab279367f8206cdadc9259d12e603548.tar.bz2 |
Skia merge (revision 3022)
This CL has companion changes to account for API updates in...
(1) frameworks/base
(2) external/webkit
Change-Id: Ibb989e76e8bd24313849f9631dbef42cdef9eb7d
Diffstat (limited to 'tests')
63 files changed, 4618 insertions, 302 deletions
diff --git a/tests/AAClipTest.cpp b/tests/AAClipTest.cpp new file mode 100644 index 0000000..b3051fd --- /dev/null +++ b/tests/AAClipTest.cpp @@ -0,0 +1,287 @@ +/* + * 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 "SkAAClip.h" +#include "SkCanvas.h" +#include "SkMask.h" +#include "SkPath.h" +#include "SkRandom.h" + +static bool operator==(const SkMask& a, const SkMask& b) { + if (a.fFormat != b.fFormat || a.fBounds != b.fBounds) { + return false; + } + if (!a.fImage && !b.fImage) { + return true; + } + if (!a.fImage || !b.fImage) { + return false; + } + + size_t wbytes = a.fBounds.width(); + switch (a.fFormat) { + case SkMask::kBW_Format: + wbytes = (wbytes + 7) >> 3; + break; + case SkMask::kA8_Format: + case SkMask::k3D_Format: + break; + case SkMask::kLCD16_Format: + wbytes <<= 1; + break; + case SkMask::kLCD32_Format: + case SkMask::kARGB32_Format: + wbytes <<= 2; + break; + default: + SkASSERT(!"unknown mask format"); + return false; + } + + const int h = a.fBounds.height(); + const char* aptr = (const char*)a.fImage; + const char* bptr = (const char*)b.fImage; + for (int y = 0; y < h; ++y) { + if (memcmp(aptr, bptr, wbytes)) { + return false; + } + aptr += wbytes; + bptr += wbytes; + } + return true; +} + +static void copyToMask(const SkRegion& rgn, SkMask* mask) { + mask->fFormat = SkMask::kA8_Format; + + if (rgn.isEmpty()) { + mask->fBounds.setEmpty(); + mask->fRowBytes = 0; + mask->fImage = NULL; + return; + } + + mask->fBounds = rgn.getBounds(); + mask->fRowBytes = mask->fBounds.width(); + mask->fImage = SkMask::AllocImage(mask->computeImageSize()); + sk_bzero(mask->fImage, mask->computeImageSize()); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kA8_Config, mask->fBounds.width(), + mask->fBounds.height(), mask->fRowBytes); + bitmap.setPixels(mask->fImage); + + // canvas expects its coordinate system to always be 0,0 in the top/left + // so we translate the rgn to match that before drawing into the mask. + // + SkRegion tmpRgn(rgn); + tmpRgn.translate(-rgn.getBounds().fLeft, -rgn.getBounds().fTop); + + SkCanvas canvas(bitmap); + canvas.clipRegion(tmpRgn); + canvas.drawColor(SK_ColorBLACK); +} + +static SkIRect rand_rect(SkRandom& rand, int n) { + int x = rand.nextS() % n; + int y = rand.nextS() % n; + int w = rand.nextU() % n; + int h = rand.nextU() % n; + return SkIRect::MakeXYWH(x, y, w, h); +} + +static void make_rand_rgn(SkRegion* rgn, SkRandom& rand) { + int count = rand.nextU() % 20; + for (int i = 0; i < count; ++i) { + rgn->op(rand_rect(rand, 100), SkRegion::kXOR_Op); + } +} + +static bool operator==(const SkRegion& rgn, const SkAAClip& aaclip) { + SkMask mask0, mask1; + + copyToMask(rgn, &mask0); + aaclip.copyToMask(&mask1); + bool eq = (mask0 == mask1); + + SkMask::FreeImage(mask0.fImage); + SkMask::FreeImage(mask1.fImage); + return eq; +} + +static bool equalsAAClip(const SkRegion& rgn) { + SkAAClip aaclip; + aaclip.setRegion(rgn); + return rgn == aaclip; +} + +static void setRgnToPath(SkRegion* rgn, const SkPath& path) { + SkIRect ir; + path.getBounds().round(&ir); + rgn->setPath(path, SkRegion(ir)); +} + +// aaclip.setRegion should create idential masks to the region +static void test_rgn(skiatest::Reporter* reporter) { + SkRandom rand; + for (int i = 0; i < 1000; i++) { + SkRegion rgn; + make_rand_rgn(&rgn, rand); + REPORTER_ASSERT(reporter, equalsAAClip(rgn)); + } + + { + SkRegion rgn; + SkPath path; + path.addCircle(0, 0, SkIntToScalar(30)); + setRgnToPath(&rgn, path); + REPORTER_ASSERT(reporter, equalsAAClip(rgn)); + + path.reset(); + path.moveTo(0, 0); + path.lineTo(SkIntToScalar(100), 0); + path.lineTo(SkIntToScalar(100 - 20), SkIntToScalar(20)); + path.lineTo(SkIntToScalar(20), SkIntToScalar(20)); + setRgnToPath(&rgn, path); + REPORTER_ASSERT(reporter, equalsAAClip(rgn)); + } +} + +static const SkRegion::Op gRgnOps[] = { + SkRegion::kDifference_Op, + SkRegion::kIntersect_Op, + SkRegion::kUnion_Op, + SkRegion::kXOR_Op, + SkRegion::kReverseDifference_Op, + SkRegion::kReplace_Op +}; + +static const char* gRgnOpNames[] = { + "DIFF", "INTERSECT", "UNION", "XOR", "REVERSE_DIFF", "REPLACE" +}; + +static void imoveTo(SkPath& path, int x, int y) { + path.moveTo(SkIntToScalar(x), SkIntToScalar(y)); +} + +static void icubicTo(SkPath& path, int x0, int y0, int x1, int y1, int x2, int y2) { + path.cubicTo(SkIntToScalar(x0), SkIntToScalar(y0), + SkIntToScalar(x1), SkIntToScalar(y1), + SkIntToScalar(x2), SkIntToScalar(y2)); +} + +static void test_path_bounds(skiatest::Reporter* reporter) { + SkPath path; + SkAAClip clip; + const int height = 40; + const SkScalar sheight = SkIntToScalar(height); + + path.addOval(SkRect::MakeWH(sheight, sheight)); + REPORTER_ASSERT(reporter, sheight == path.getBounds().height()); + clip.setPath(path, NULL, true); + REPORTER_ASSERT(reporter, height == clip.getBounds().height()); + + // this is the trimmed height of this cubic (with aa). The critical thing + // for this test is that it is less than height, which represents just + // the bounds of the path's control-points. + // + // This used to fail until we tracked the MinY in the BuilderBlitter. + // + const int teardrop_height = 12; + path.reset(); + imoveTo(path, 0, 20); + icubicTo(path, 40, 40, 40, 0, 0, 20); + REPORTER_ASSERT(reporter, sheight == path.getBounds().height()); + clip.setPath(path, NULL, true); + REPORTER_ASSERT(reporter, teardrop_height == clip.getBounds().height()); +} + +static void test_empty(skiatest::Reporter* reporter) { + SkAAClip clip0, clip1; + + REPORTER_ASSERT(reporter, clip0.isEmpty()); + REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty()); + REPORTER_ASSERT(reporter, clip1 == clip0); + + clip0.translate(10, 10); // should have no effect on empty + REPORTER_ASSERT(reporter, clip0.isEmpty()); + REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty()); + REPORTER_ASSERT(reporter, clip1 == clip0); + + SkIRect r = { 10, 10, 40, 50 }; + clip0.setRect(r); + REPORTER_ASSERT(reporter, !clip0.isEmpty()); + REPORTER_ASSERT(reporter, !clip0.getBounds().isEmpty()); + REPORTER_ASSERT(reporter, clip0 != clip1); + REPORTER_ASSERT(reporter, clip0.getBounds() == r); + + clip0.setEmpty(); + REPORTER_ASSERT(reporter, clip0.isEmpty()); + REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty()); + REPORTER_ASSERT(reporter, clip1 == clip0); + + SkMask mask; + mask.fImage = NULL; + clip0.copyToMask(&mask); + REPORTER_ASSERT(reporter, NULL == mask.fImage); + REPORTER_ASSERT(reporter, mask.fBounds.isEmpty()); +} + +static void rand_irect(SkIRect* r, int N, SkRandom& rand) { + r->setXYWH(0, 0, rand.nextU() % N, rand.nextU() % N); + int dx = rand.nextU() % (2*N); + int dy = rand.nextU() % (2*N); + // use int dx,dy to make the subtract be signed + r->offset(N - dx, N - dy); +} + +static void test_irect(skiatest::Reporter* reporter) { + SkRandom rand; + + for (int i = 0; i < 10000; i++) { + SkAAClip clip0, clip1; + SkRegion rgn0, rgn1; + SkIRect r0, r1; + + rand_irect(&r0, 10, rand); + rand_irect(&r1, 10, rand); + clip0.setRect(r0); + clip1.setRect(r1); + rgn0.setRect(r0); + rgn1.setRect(r1); + for (size_t j = 0; j < SK_ARRAY_COUNT(gRgnOps); ++j) { + SkRegion::Op op = gRgnOps[j]; + SkAAClip clip2; + SkRegion rgn2; + bool nonEmptyAA = clip2.op(clip0, clip1, op); + bool nonEmptyBW = rgn2.op(rgn0, rgn1, op); + if (nonEmptyAA != nonEmptyBW || clip2.getBounds() != rgn2.getBounds()) { + SkDebugf("[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n", + r0.fLeft, r0.fTop, r0.right(), r0.bottom(), + gRgnOpNames[j], + r1.fLeft, r1.fTop, r1.right(), r1.bottom(), + rgn2.getBounds().fLeft, rgn2.getBounds().fTop, + rgn2.getBounds().right(), rgn2.getBounds().bottom(), + clip2.getBounds().fLeft, clip2.getBounds().fTop, + clip2.getBounds().right(), clip2.getBounds().bottom()); + } + REPORTER_ASSERT(reporter, nonEmptyAA == nonEmptyBW); + REPORTER_ASSERT(reporter, clip2.getBounds() == rgn2.getBounds()); + } + } +} + +static void TestAAClip(skiatest::Reporter* reporter) { + test_empty(reporter); + test_path_bounds(reporter); + test_irect(reporter); + test_rgn(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("AAClip", AAClipTestClass, TestAAClip) diff --git a/tests/Android.mk b/tests/Android.mk index 4db6c75..14d23f8 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -3,23 +3,32 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + AAClipTest.cpp \ BitmapCopyTest.cpp \ BitmapGetColorTest.cpp \ BlitRowTest.cpp \ + BlurTest.cpp \ + CanvasTest.cpp \ ClampRangeTest.cpp \ ClipCubicTest.cpp \ ClipStackTest.cpp \ ClipperTest.cpp \ ColorFilterTest.cpp \ ColorTest.cpp \ + DataRefTest.cpp \ DequeTest.cpp \ DrawBitmapRectTest.cpp \ + EmptyPathTest.cpp \ FillPathTest.cpp \ FlateTest.cpp \ GeometryTest.cpp \ + GLInterfaceValidation.cpp \ + GLProgramsTest.cpp \ InfRectTest.cpp \ MathTest.cpp \ MatrixTest.cpp \ + Matrix44Test.cpp \ + MemsetTest.cpp \ MetaDataTest.cpp \ PackBitsTest.cpp \ PaintTest.cpp \ @@ -27,37 +36,53 @@ LOCAL_SRC_FILES:= \ PathCoverageTest.cpp \ PathMeasureTest.cpp \ PathTest.cpp \ + PointTest.cpp \ + QuickRejectTest.cpp \ Reader32Test.cpp \ + ReadPixelsTest.cpp \ RefDictTest.cpp \ RegionTest.cpp \ + ScalarTest.cpp \ + ShaderOpacityTest.cpp \ Sk64Test.cpp \ + skia_test.cpp \ SortTest.cpp \ SrcOverTest.cpp \ StreamTest.cpp \ StringTest.cpp \ Test.cpp \ + Test.h \ TestSize.cpp \ + UnicodeTest.cpp \ UtilsTest.cpp \ + WArrayTest.cpp \ + WritePixelsTest.cpp \ Writer32Test.cpp \ XfermodeTest.cpp -# The name of the file with a main function must -# match native test's naming rule: xxx_test.cpp. -LOCAL_SRC_FILES += \ - skia_test.cpp +# TODO: tests that currently are causing build problems +#LOCAL_SRC_FILES += \ +# BitSetTest.cpp \ +# PDFPrimitivesTest.cpp \ +# ToUnicode.cpp LOCAL_MODULE:= skia_test LOCAL_C_INCLUDES := \ - external/skia/include/core \ - external/skia/include/effects \ - external/skia/include/images \ - external/skia/include/ports \ - external/skia/include/utils \ - external/skia/src/core + external/freetype/include \ + external/skia/include/core \ + external/skia/include/config \ + external/skia/include/effects \ + external/skia/include/gpu \ + external/skia/include/images \ + external/skia/include/pdf \ + external/skia/include/ports \ + external/skia/include/utils \ + external/skia/src/core \ + external/skia/src/gpu -LOCAL_SHARED_LIBRARIES := \ - libskia libcutils +LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2 libEGL +LOCAL_STATIC_LIBRARIES := libskiagpu LOCAL_MODULE_TAGS := eng tests diff --git a/tests/BitSetTest.cpp b/tests/BitSetTest.cpp new file mode 100644 index 0000000..7139495 --- /dev/null +++ b/tests/BitSetTest.cpp @@ -0,0 +1,79 @@ + +/* + * 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 "SkBitSet.h" + +static void TestBitSet(skiatest::Reporter* reporter) { + SkBitSet set0(65536); + REPORTER_ASSERT(reporter, set0.isBitSet(0) == false); + REPORTER_ASSERT(reporter, set0.isBitSet(32767) == false); + REPORTER_ASSERT(reporter, set0.isBitSet(65535) == false); + + SkBitSet set1(65536); + REPORTER_ASSERT(reporter, set0 == set1); + + set0.setBit(22, true); + REPORTER_ASSERT(reporter, set0.isBitSet(22) == true); + set0.setBit(24, true); + REPORTER_ASSERT(reporter, set0.isBitSet(24) == true); + set0.setBit(35, true); // on a different DWORD + REPORTER_ASSERT(reporter, set0.isBitSet(35) == true); + set0.setBit(22, false); + REPORTER_ASSERT(reporter, set0.isBitSet(22) == false); + REPORTER_ASSERT(reporter, set0.isBitSet(24) == true); + REPORTER_ASSERT(reporter, set0.isBitSet(35) == true); + + SkTDArray<unsigned int> data; + set0.exportTo(&data); + REPORTER_ASSERT(reporter, data.count() == 2); + REPORTER_ASSERT(reporter, data[0] == 24); + REPORTER_ASSERT(reporter, data[1] == 35); + + set1.setBit(12345, true); + set1.orBits(set0); + REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false); + REPORTER_ASSERT(reporter, set1.isBitSet(12345) == true); + REPORTER_ASSERT(reporter, set1.isBitSet(22) == false); + REPORTER_ASSERT(reporter, set1.isBitSet(24) == true); + REPORTER_ASSERT(reporter, set0.isBitSet(35) == true); + REPORTER_ASSERT(reporter, set1 != set0); + + set1.clearAll(); + REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false); + REPORTER_ASSERT(reporter, set1.isBitSet(12345) == false); + REPORTER_ASSERT(reporter, set1.isBitSet(22) == false); + REPORTER_ASSERT(reporter, set1.isBitSet(24) == false); + REPORTER_ASSERT(reporter, set1.isBitSet(35) == false); + + set1.orBits(set0); + REPORTER_ASSERT(reporter, set1 == set0); + + SkBitSet set2(1); + SkBitSet set3(1); + SkBitSet set4(4); + SkBitSet set5(33); + + REPORTER_ASSERT(reporter, set2 == set3); + REPORTER_ASSERT(reporter, set2 != set4); + REPORTER_ASSERT(reporter, set2 != set5); + + set2.setBit(0, true); + REPORTER_ASSERT(reporter, set2 != set5); + set5.setBit(0, true); + REPORTER_ASSERT(reporter, set2 != set5); + REPORTER_ASSERT(reporter, set2 != set3); + set3.setBit(0, true); + REPORTER_ASSERT(reporter, set2 == set3); + set3.clearAll(); + set3 = set2; + set2 = set2; + REPORTER_ASSERT(reporter, set2 == set3); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("BitSet", BitSetTest, TestBitSet) diff --git a/tests/BitmapCopyTest.cpp b/tests/BitmapCopyTest.cpp index 6ddb548..b8d16bf 100644 --- a/tests/BitmapCopyTest.cpp +++ b/tests/BitmapCopyTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkBitmap.h" #include "SkRect.h" @@ -543,6 +550,7 @@ static void TestBitmapCopy(skiatest::Reporter* reporter) { reportCopyVerification(subset, bufBm, coords, "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter); +#if 0 // copyPixelsFrom is gone // Test #5 //////////////////////////////////////////// // Tests the case where the source stride is too small // for the source configuration. @@ -577,6 +585,7 @@ static void TestBitmapCopy(skiatest::Reporter* reporter) { false); delete [] buf; +#endif } } } // for (size_t copyCase ... diff --git a/tests/BitmapGetColorTest.cpp b/tests/BitmapGetColorTest.cpp index 81cf412..4204ddf 100644 --- a/tests/BitmapGetColorTest.cpp +++ b/tests/BitmapGetColorTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkBitmap.h" diff --git a/tests/BlitRowTest.cpp b/tests/BlitRowTest.cpp index d18cb8a..b37b47e 100644 --- a/tests/BlitRowTest.cpp +++ b/tests/BlitRowTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkBitmap.h" #include "SkCanvas.h" diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp new file mode 100644 index 0000000..c0d16f8 --- /dev/null +++ b/tests/BlurTest.cpp @@ -0,0 +1,162 @@ + +/* + * 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 "SkBlurMaskFilter.h" +#include "SkCanvas.h" +#include "SkMath.h" +#include "SkPaint.h" +#include "SkRandom.h" + +/////////////////////////////////////////////////////////////////////////////// + +#define ILLEGAL_MODE ((SkXfermode::Mode)-1) + +static const int outset = 100; +static const SkColor bgColor = SK_ColorWHITE; +static const int strokeWidth = 4; + +static void create(SkBitmap* bm, SkIRect bound, SkBitmap::Config config) { + bm->setConfig(config, bound.width(), bound.height()); + bm->allocPixels(); +} + +static void drawBG(SkCanvas* canvas) { + canvas->drawColor(bgColor); +} + + +struct BlurTest { + void (*addPath)(SkPath*); + int viewLen; + SkIRect views[9]; +}; + +//Path Draw Procs +//Beware that paths themselves my draw differently depending on the clip. +static void draw50x50Rect(SkPath* path) { + path->addRect(0, 0, SkIntToScalar(50), SkIntToScalar(50)); +} + +//Tests +static BlurTest tests[] = { + { draw50x50Rect, 3, { + //inner half of blur + { 0, 0, 50, 50 }, + //blur, but no path. + { 50 + strokeWidth/2, 50 + strokeWidth/2, 100, 100 }, + //just an edge + { 40, strokeWidth, 60, 50 - strokeWidth }, + }}, +}; + +/** Assumes that the ref draw was completely inside ref canvas -- + implies that everything outside is "bgColor". + Checks that all overlap is the same and that all non-overlap on the + ref is "bgColor". + */ +static bool compare(const SkBitmap& ref, const SkIRect& iref, + const SkBitmap& test, const SkIRect& itest) +{ + const int xOff = itest.fLeft - iref.fLeft; + const int yOff = itest.fTop - iref.fTop; + + SkAutoLockPixels alpRef(ref); + SkAutoLockPixels alpTest(test); + + for (int y = 0; y < test.height(); ++y) { + for (int x = 0; x < test.width(); ++x) { + SkColor testColor = test.getColor(x, y); + int refX = x + xOff; + int refY = y + yOff; + SkColor refColor; + if (refX >= 0 && refX < ref.width() && + refY >= 0 && refY < ref.height()) + { + refColor = ref.getColor(refX, refY); + } else { + refColor = bgColor; + } + if (refColor != testColor) { + return false; + } + } + } + return true; +} + +static void test_blur(skiatest::Reporter* reporter) { + + SkPaint paint; + paint.setColor(SK_ColorGRAY); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(strokeWidth)); + + SkScalar radius = SkIntToScalar(5); + for (int style = 0; style < SkBlurMaskFilter::kBlurStyleCount; ++style) { + SkBlurMaskFilter::BlurStyle blurStyle = + static_cast<SkBlurMaskFilter::BlurStyle>(style); + + const uint32_t flagPermutations = SkBlurMaskFilter::kAll_BlurFlag; + for (uint32_t flags = 0; flags < flagPermutations; ++flags) { + SkMaskFilter* filter; + filter = SkBlurMaskFilter::Create(radius, blurStyle, flags); + + SkMaskFilter::BlurInfo info; + sk_bzero(&info, sizeof(info)); + SkMaskFilter::BlurType type = filter->asABlur(&info); + + REPORTER_ASSERT(reporter, type == + static_cast<SkMaskFilter::BlurType>(style + 1)); + REPORTER_ASSERT(reporter, info.fRadius == radius); + REPORTER_ASSERT(reporter, info.fIgnoreTransform == + SkToBool(flags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag)); + REPORTER_ASSERT(reporter, info.fHighQuality == + SkToBool(flags & SkBlurMaskFilter::kHighQuality_BlurFlag)); + + paint.setMaskFilter(filter); + filter->unref(); + + for (size_t test = 0; test < SK_ARRAY_COUNT(tests); ++test) { + SkPath path; + tests[test].addPath(&path); + SkPath strokedPath; + paint.getFillPath(path, &strokedPath); + SkRect refBound = strokedPath.getBounds(); + SkIRect iref; + refBound.roundOut(&iref); + iref.inset(-outset, -outset); + SkBitmap refBitmap; + create(&refBitmap, iref, SkBitmap::kARGB_8888_Config); + + SkCanvas refCanvas(refBitmap); + refCanvas.translate(SkIntToScalar(-iref.fLeft), + SkIntToScalar(-iref.fTop)); + drawBG(&refCanvas); + refCanvas.drawPath(path, paint); + + for (int view = 0; view < tests[test].viewLen; ++view) { + SkIRect itest = tests[test].views[view]; + SkBitmap testBitmap; + create(&testBitmap, itest, SkBitmap::kARGB_8888_Config); + + SkCanvas testCanvas(testBitmap); + testCanvas.translate(SkIntToScalar(-itest.fLeft), + SkIntToScalar(-itest.fTop)); + drawBG(&testCanvas); + testCanvas.drawPath(path, paint); + + REPORTER_ASSERT(reporter, + compare(refBitmap, iref, testBitmap, itest)); + } + } + } + } +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("BlurMaskFilter", BlurTestClass, test_blur) diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp new file mode 100644 index 0000000..da1fafd --- /dev/null +++ b/tests/CanvasTest.cpp @@ -0,0 +1,67 @@ + +/* + * 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 "SkBitmap.h" +#include "SkCanvas.h" + +static void test_isDrawingToLayer(skiatest::Reporter* reporter) { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 256, 256); + bm.allocPixels(); + + SkCanvas canvas(bm); + + REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer()); + canvas.save(); + REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer()); + + const SkRect* bounds = NULL; // null means include entire bounds + const SkPaint* paint = NULL; + + canvas.saveLayer(bounds, paint); + REPORTER_ASSERT(reporter, canvas.isDrawingToLayer()); + canvas.restore(); + REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer()); + + canvas.saveLayer(bounds, paint); + canvas.saveLayer(bounds, paint); + REPORTER_ASSERT(reporter, canvas.isDrawingToLayer()); + canvas.restore(); + REPORTER_ASSERT(reporter, canvas.isDrawingToLayer()); + canvas.restore(); + // now layer count should be 0 + REPORTER_ASSERT(reporter, !canvas.isDrawingToLayer()); +} + +static void TestCanvas(skiatest::Reporter* reporter) { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 256, 256); + bm.allocPixels(); + + SkCanvas canvas(bm); + int n; + + REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount()); + n = canvas.save(); + REPORTER_ASSERT(reporter, 1 == n); + REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount()); + canvas.save(); + canvas.save(); + REPORTER_ASSERT(reporter, 4 == canvas.getSaveCount()); + canvas.restoreToCount(2); + REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount()); + + // should this pin to 1, or be a no-op, or crash? + canvas.restoreToCount(0); + REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount()); + + test_isDrawingToLayer(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Canvas", TestCanvasClass, TestCanvas) diff --git a/tests/ClampRangeTest.cpp b/tests/ClampRangeTest.cpp index be9c6ec..226d030 100644 --- a/tests/ClampRangeTest.cpp +++ b/tests/ClampRangeTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkClampRange.h" #include "SkRandom.h" diff --git a/tests/ClipCubicTest.cpp b/tests/ClipCubicTest.cpp index e38a22f..931b61e 100644 --- a/tests/ClipCubicTest.cpp +++ b/tests/ClipCubicTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkCubicClipper.h" diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp index eafdd69..40738df 100644 --- a/tests/ClipStackTest.cpp +++ b/tests/ClipStackTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkClipStack.h" #include "SkPath.h" @@ -5,6 +12,7 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) { SkClipStack s; + bool doAA = false; // Build up a clip stack with a path, an empty clip, and a rect. s.save(); @@ -13,17 +21,17 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) { p.lineTo(7, 8); p.lineTo(5, 9); p.close(); - s.clipDevPath(p); + s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); s.save(); SkRect r = SkRect::MakeLTRB(1, 2, 3, 4); - s.clipDevRect(r); + s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); r = SkRect::MakeLTRB(10, 11, 12, 13); - s.clipDevRect(r); + s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); s.save(); r = SkRect::MakeLTRB(14, 15, 16, 17); - s.clipDevRect(r, SkRegion::kUnion_Op); + s.clipDevRect(r, SkRegion::kUnion_Op, doAA); // Test that assignment works. SkClipStack copy = s; @@ -36,14 +44,14 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) { // Test that an equal, but not copied version is equal. s.save(); r = SkRect::MakeLTRB(14, 15, 16, 17); - s.clipDevRect(r, SkRegion::kUnion_Op); + s.clipDevRect(r, SkRegion::kUnion_Op, doAA); REPORTER_ASSERT(reporter, s == copy); // Test that a different op on one level triggers not equal. s.restore(); s.save(); r = SkRect::MakeLTRB(14, 15, 16, 17); - s.clipDevRect(r); + s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); REPORTER_ASSERT(reporter, s != copy); // Test that different state (clip type) triggers not equal. @@ -51,14 +59,14 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) { s.save(); SkPath rp; rp.addRect(r); - s.clipDevPath(rp, SkRegion::kUnion_Op); + s.clipDevPath(rp, SkRegion::kUnion_Op, doAA); REPORTER_ASSERT(reporter, s != copy); // Test that different rects triggers not equal. s.restore(); s.save(); r = SkRect::MakeLTRB(24, 25, 26, 27); - s.clipDevRect(r, SkRegion::kUnion_Op); + s.clipDevRect(r, SkRegion::kUnion_Op, doAA); REPORTER_ASSERT(reporter, s != copy); // Sanity check @@ -73,7 +81,7 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) { s.restore(); s.save(); p.addRect(r); - s.clipDevPath(p); + s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); REPORTER_ASSERT(reporter, s != copy); } @@ -101,7 +109,7 @@ static void TestClipStack(skiatest::Reporter* reporter) { { 0, 0, 75, 75 } }; for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { - stack.clipDevRect(gRects[i]); + stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op); } // all of the above rects should have been intersected, leaving only 1 rect diff --git a/tests/ClipperTest.cpp b/tests/ClipperTest.cpp index 66301fc..7bb2254 100644 --- a/tests/ClipperTest.cpp +++ b/tests/ClipperTest.cpp @@ -1,8 +1,33 @@ + +/* + * 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 "SkLineClipper.h" #include "SkEdgeClipper.h" +static void test_edgeclipper(skiatest::Reporter* reporter) { + SkEdgeClipper clipper; + + const SkPoint pts[] = { + { SkFloatToScalar(3.0995476e+010), SkFloatToScalar(42.929779) }, + { SkFloatToScalar(-3.0995163e+010), SkFloatToScalar(51.050385) }, + { SkFloatToScalar(-3.0995157e+010), SkFloatToScalar(51.050392) }, + { SkFloatToScalar(-3.0995134e+010), SkFloatToScalar(51.050400) }, + }; + + const SkRect clip = { 0, 0, SkIntToScalar(300), SkIntToScalar(200) }; + + // this should not assert, even though our choppers do a poor numerical + // job when computing their t values. + // http://code.google.com/p/skia/issues/detail?id=444 + clipper.clipCubic(pts, clip); +} + static void test_intersectline(skiatest::Reporter* reporter) { static const SkScalar L = 0; static const SkScalar T = 0; @@ -83,6 +108,7 @@ static void test_intersectline(skiatest::Reporter* reporter) { void TestClipper(skiatest::Reporter* reporter) { test_intersectline(reporter); + test_edgeclipper(reporter); } #include "TestClassDef.h" diff --git a/tests/ColorFilterTest.cpp b/tests/ColorFilterTest.cpp index 77575eb..f98deb0 100644 --- a/tests/ColorFilterTest.cpp +++ b/tests/ColorFilterTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkColor.h" #include "SkColorFilter.h" diff --git a/tests/ColorTest.cpp b/tests/ColorTest.cpp index ef4b0b6..0efb892 100644 --- a/tests/ColorTest.cpp +++ b/tests/ColorTest.cpp @@ -1,5 +1,13 @@ + +/* + * 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 "SkColor.h" +#include "SkMath.h" #include "SkUnPreMultiply.h" static void test_premul(skiatest::Reporter* reporter) { diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp new file mode 100644 index 0000000..6a42485 --- /dev/null +++ b/tests/DataRefTest.cpp @@ -0,0 +1,61 @@ + +/* + * 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 "SkData.h" + +static void* gGlobal; + +static void delete_int_proc(const void* ptr, size_t len, void* context) { + int* data = (int*)ptr; + SkASSERT(context == gGlobal); + delete[] data; +} + +static void assert_len(skiatest::Reporter* reporter, SkData* ref, size_t len) { + REPORTER_ASSERT(reporter, ref->size() == len); +} + +static void assert_data(skiatest::Reporter* reporter, SkData* ref, + const void* data, size_t len) { + REPORTER_ASSERT(reporter, ref->size() == len); + REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len)); +} + +void TestDataRef(skiatest::Reporter* reporter) { + const char* str = "We the people, in order to form a more perfect union."; + const int N = 10; + + SkData* r0 = SkData::NewEmpty(); + SkData* r1 = SkData::NewWithCopy(str, strlen(str)); + SkData* r2 = SkData::NewWithProc(new int[N], N*sizeof(int), + delete_int_proc, gGlobal); + SkData* r3 = SkData::NewSubset(r1, 7, 6); + + SkAutoUnref aur0(r0); + SkAutoUnref aur1(r1); + SkAutoUnref aur2(r2); + SkAutoUnref aur3(r3); + + assert_len(reporter, r0, 0); + assert_len(reporter, r1, strlen(str)); + assert_len(reporter, r2, N * sizeof(int)); + assert_len(reporter, r3, 6); + + assert_data(reporter, r1, str, strlen(str)); + assert_data(reporter, r3, "people", 6); + + SkData* tmp = SkData::NewSubset(r1, strlen(str), 10); + assert_len(reporter, tmp, 0); + tmp->unref(); + tmp = SkData::NewSubset(r1, 0, 0); + assert_len(reporter, tmp, 0); + tmp->unref(); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("DataRef", DataRefTestClass, TestDataRef) diff --git a/tests/DequeTest.cpp b/tests/DequeTest.cpp index e74fd28..4e1c1b7 100644 --- a/tests/DequeTest.cpp +++ b/tests/DequeTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkDeque.h" diff --git a/tests/DrawBitmapRectTest.cpp b/tests/DrawBitmapRectTest.cpp index fc382ce..843feb5 100644 --- a/tests/DrawBitmapRectTest.cpp +++ b/tests/DrawBitmapRectTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkBitmap.h" #include "SkCanvas.h" diff --git a/tests/EmptyPathTest.cpp b/tests/EmptyPathTest.cpp new file mode 100644 index 0000000..b126076 --- /dev/null +++ b/tests/EmptyPathTest.cpp @@ -0,0 +1,156 @@ +/* + * 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 ? (~0U == 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_M(SkPath* path) { path->moveTo(CX, CY); } +static void make_MM(SkPath* path) { path->moveTo(CX, CY); path->moveTo(CX, CY); } +static void make_MZM(SkPath* path) { path->moveTo(CX, CY); path->close(); path->moveTo(CX, CY); } +static void make_L(SkPath* path) { path->moveTo(CX, CY); path->lineTo(CX, CY); } +static void make_Q(SkPath* path) { path->moveTo(CX, CY); path->quadTo(CX, CY, CX, CY); } +static void make_C(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_M, make_MM, make_MZM, make_L, make_Q, make_C + }; + 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) diff --git a/tests/FillPathTest.cpp b/tests/FillPathTest.cpp index ffc9f8e..8271851 100644 --- a/tests/FillPathTest.cpp +++ b/tests/FillPathTest.cpp @@ -1,17 +1,8 @@ /* - * Copyright (C) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010 Google Inc. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ #include "Test.h" @@ -41,12 +32,12 @@ struct FakeBlitter : public SkBlitter { // but skipped after tessellation, should be cleared by the blitter. static void TestFillPathInverse(skiatest::Reporter* reporter) { FakeBlitter blitter; - SkRegion clip; + SkIRect clip; SkPath path; int height = 100; int width = 200; int expected_lines = 5; - clip.setRect(0, height - expected_lines, width, height); + clip.set(0, height - expected_lines, width, height); path.moveTo(0.0, 0.0); path.quadTo(width/2, height, width, 0.0); path.close(); diff --git a/tests/FlateTest.cpp b/tests/FlateTest.cpp index fe2bb4a..8697df9 100644 --- a/tests/FlateTest.cpp +++ b/tests/FlateTest.cpp @@ -1,23 +1,17 @@ + /* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2011 Google Inc. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + #include <stdlib.h> #include <string.h> #include "Test.h" +#include "SkData.h" #include "SkFlate.h" #include "SkStream.h" @@ -67,7 +61,9 @@ static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream, else REPORTER_ASSERT(reporter, compressed.getOffset() > 1024); - testStream->setMemory(compressed.getStream(), compressed.getOffset(), true); + SkAutoDataUnref data1(compressed.copyToData()); + + testStream->setData(data1.get())->unref(); SkDynamicMemoryWStream uncompressed; status = SkFlate::Inflate(testStream, &uncompressed); REPORTER_ASSERT(reporter, status); @@ -76,15 +72,14 @@ static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream, inputSize = testStream->getLength(); if (inputSize == 0) inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey); - REPORTER_ASSERT(reporter, compressed.getOffset() == inputSize); + REPORTER_ASSERT(reporter, data1.size() == inputSize); REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(), - compressed.getStream(), - compressed.getOffset()) == 0); + data1.data(), data1.size()) == 0); // Check that the uncompressed data matches the source data. + SkAutoDataUnref data2(uncompressed.copyToData()); REPORTER_ASSERT(reporter, testData.getLength() == uncompressed.getOffset()); - REPORTER_ASSERT(reporter, memcmp(testData.getMemoryBase(), - uncompressed.getStream(), + REPORTER_ASSERT(reporter, memcmp(testData.getMemoryBase(), data2.data(), testData.getLength()) == 0); } diff --git a/tests/GLInterfaceValidation.cpp b/tests/GLInterfaceValidation.cpp new file mode 100755 index 0000000..2be13f0 --- /dev/null +++ b/tests/GLInterfaceValidation.cpp @@ -0,0 +1,64 @@ + +/* + * 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 "SkNativeGLContext.h" +#include "SkMesaGLContext.h" + +static void GLInterfaceValidationTest(skiatest::Reporter* reporter) { + typedef const GrGLInterface* (*interfaceFactory)(); + struct { + interfaceFactory fFactory; + const char* fName; + } interfaceFactories[] = { + {GrGLCreateNativeInterface, "Native"}, +#if SK_MESA + {GrGLCreateMesaInterface, "Mesa"}, +#endif + {GrGLCreateNullInterface, "Null"}, + }; + + // On some platforms GrGLCreateNativeInterface will fail unless an OpenGL + // context has been created. Also, preserve the current context that may + // be in use by outer test harness. + SkNativeGLContext::AutoContextRestore nglacr; + SkNativeGLContext nglctx; + static const int gBOGUS_SIZE = 16; + bool nativeContextInit = nglctx.init(gBOGUS_SIZE, gBOGUS_SIZE); + REPORTER_ASSERT(reporter, nativeContextInit); + if (!nativeContextInit) { + return; + } +#if SK_MESA + // We must have a current OSMesa context to initialize an OSMesa + // GrGLInterface + SkMesaGLContext::AutoContextRestore mglacr; + SkMesaGLContext mglctx; + bool mesaContextInit = mglctx.init(gBOGUS_SIZE, gBOGUS_SIZE); + REPORTER_ASSERT(reporter, mesaContextInit); + if(!mesaContextInit) { + return; + } +#endif + + SkAutoTUnref<const GrGLInterface> iface; + for (size_t i = 0; i < SK_ARRAY_COUNT(interfaceFactories); ++i) { + iface.reset(interfaceFactories[i].fFactory()); + REPORTER_ASSERT(reporter, NULL != iface.get()); + if (iface.get()) { + REPORTER_ASSERT(reporter, iface.get()->validate()); + } + } +} + + +#include "TestClassDef.h" +DEFINE_TESTCLASS("GLInterfaceValidation", + GLInterfaceValidationTestClass, + GLInterfaceValidationTest) + diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp new file mode 100644 index 0000000..5cacade --- /dev/null +++ b/tests/GLProgramsTest.cpp @@ -0,0 +1,21 @@ + +/* + * 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 "GrContext.h" +#include "GrGpuGLShaders.h" + +static void GLProgramsTest(skiatest::Reporter* reporter, GrContext* context) { + GrGpuGLShaders* shadersGpu = (GrGpuGLShaders*) context->getGpu(); + REPORTER_ASSERT(reporter, shadersGpu->programUnitTest()); +} + + +#include "TestClassDef.h" +DEFINE_GPUTESTCLASS("GLPrograms", GLProgramsTestClass, GLProgramsTest) + diff --git a/tests/GeometryTest.cpp b/tests/GeometryTest.cpp index 5b05952..6158a20 100644 --- a/tests/GeometryTest.cpp +++ b/tests/GeometryTest.cpp @@ -1,6 +1,17 @@ + +/* + * 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 "SkGeometry.h" +static bool nearly_equal(const SkPoint& a, const SkPoint& b) { + return SkScalarNearlyEqual(a.fX, b.fX) && SkScalarNearlyEqual(a.fY, b.fY); +} + static void TestGeometry(skiatest::Reporter* reporter) { SkPoint pts[3], dst[5]; @@ -10,6 +21,20 @@ static void TestGeometry(skiatest::Reporter* reporter) { int count = SkChopQuadAtMaxCurvature(pts, dst); REPORTER_ASSERT(reporter, count == 1 || count == 2); + + pts[0].set(0, 0); + pts[1].set(SkIntToScalar(3), 0); + pts[2].set(SkIntToScalar(3), SkIntToScalar(3)); + SkConvertQuadToCubic(pts, dst); + const SkPoint cubic[] = { + { 0, 0, }, + { SkIntToScalar(2), 0, }, + { SkIntToScalar(3), SkIntToScalar(1), }, + { SkIntToScalar(3), SkIntToScalar(3) }, + }; + for (int i = 0; i < 4; ++i) { + REPORTER_ASSERT(reporter, nearly_equal(cubic[i], dst[i])); + } } #include "TestClassDef.h" diff --git a/tests/InfRectTest.cpp b/tests/InfRectTest.cpp index 1e15023..12356d9 100644 --- a/tests/InfRectTest.cpp +++ b/tests/InfRectTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkRect.h" @@ -11,10 +18,10 @@ static void check_invalid(skiatest::Reporter* reporter, SkScalar l, SkScalar t, SkScalar r, SkScalar b) { SkRect rect; rect.set(l, t, r, b); - REPORTER_ASSERT(reporter, !rect.hasValidCoordinates()); + REPORTER_ASSERT(reporter, !rect.isFinite()); } -// Tests that hasValidCoordinates() will reject any rect with +/-inf values +// Tests that isFinite() will reject any rect with +/-inf values // as one of its coordinates. static void TestInfRect(skiatest::Reporter* reporter) { #ifdef SK_SCALAR_IS_FLOAT @@ -26,7 +33,7 @@ static void TestInfRect(skiatest::Reporter* reporter) { SkScalar big = SkIntToScalar(100); SkRect rect = SkRect::MakeXYWH(small, small, big, big); - REPORTER_ASSERT(reporter, rect.hasValidCoordinates()); + REPORTER_ASSERT(reporter, rect.isFinite()); check_invalid(reporter, small, small, big, invalid); check_invalid(reporter, small, small, invalid, big); diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp index efdad3a..fef93cd 100644 --- a/tests/MathTest.cpp +++ b/tests/MathTest.cpp @@ -1,41 +1,97 @@ + +/* + * 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 "SkFloatingPoint.h" +#include "SkMath.h" #include "SkPoint.h" #include "SkRandom.h" +#include "SkColorPriv.h" -#if 0 -static U8CPU premul_fast(U8CPU a, U8CPU x) { - return a * x * 32897 >> 23; +static float float_blend(int src, int dst, float unit) { + return dst + (src - dst) * unit; } -static U8CPU premul_trunc(U8CPU a, U8CPU x) { - double result = a * x; - result /= 255.0; - return (unsigned)floor(result + 0.0); +static int blend31(int src, int dst, int a31) { + return dst + ((src - dst) * a31 * 2114 >> 16); + // return dst + ((src - dst) * a31 * 33 >> 10); } -static U8CPU premul_round(U8CPU a, U8CPU x) { - double result = a * x; - result /= 255.0; - return (unsigned)floor(result + 0.5); +static int blend31_slow(int src, int dst, int a31) { + int prod = src * a31 + (31 - a31) * dst + 16; + prod = (prod + (prod >> 5)) >> 5; + return prod; } -static void test_premul(skiatest::Reporter* reporter) { - for (int a = 0; a <= 255; a++) { - for (int x = 0; x <= 255; x++) { - unsigned curr_trunc = SkMulDiv255Trunc(a, x); - unsigned curr_round = SkMulDiv255Round(a, x); - unsigned fast = premul_fast(a, x); - unsigned slow_round = premul_round(a, x); - unsigned slow_trunc = premul_trunc(a, x); - if (fast != slow || curr != fast) { - SkDebugf("---- premul(%d %d) curr=%d fast=%d slow=%d\n", a, x, - curr, fast, slow); +static int blend31_round(int src, int dst, int a31) { + int prod = (src - dst) * a31 + 16; + prod = (prod + (prod >> 5)) >> 5; + return dst + prod; +} + +static int blend31_old(int src, int dst, int a31) { + a31 += a31 >> 4; + return dst + ((src - dst) * a31 >> 5); +} + +static void test_blend31() { + int failed = 0; + int death = 0; + for (int src = 0; src <= 255; src++) { + for (int dst = 0; dst <= 255; dst++) { + for (int a = 0; a <= 31; a++) { +// int r0 = blend31(src, dst, a); +// int r0 = blend31_round(src, dst, a); +// int r0 = blend31_old(src, dst, a); + int r0 = blend31_slow(src, dst, a); + + float f = float_blend(src, dst, a / 31.f); + int r1 = (int)f; + int r2 = SkScalarRoundToInt(SkFloatToScalar(f)); + + if (r0 != r1 && r0 != r2) { + printf("src:%d dst:%d a:%d result:%d float:%g\n", + src, dst, a, r0, f); + failed += 1; + } + if (r0 > 255) { + death += 1; + printf("death src:%d dst:%d a:%d result:%d float:%g\n", + src, dst, a, r0, f); + } } } } + SkDebugf("---- failed %d death %d\n", failed, death); } + +static void test_blend(skiatest::Reporter* reporter) { + for (int src = 0; src <= 255; src++) { + for (int dst = 0; dst <= 255; dst++) { + for (int a = 0; a <= 255; a++) { + int r0 = SkAlphaBlend255(src, dst, a); + float f1 = float_blend(src, dst, a / 255.f); + int r1 = SkScalarRoundToInt(SkFloatToScalar(f1)); + + if (r0 != r1) { + float diff = sk_float_abs(f1 - r1); + diff = sk_float_abs(diff - 0.5f); + if (diff > (1 / 255.f)) { +#ifdef SK_DEBUG + SkDebugf("src:%d dst:%d a:%d result:%d float:%g\n", + src, dst, a, r0, f1); #endif + REPORTER_ASSERT(reporter, false); + } + } + } + } + } +} #if defined(SkLONGLONG) static int symmetric_fixmul(int a, int b) { @@ -493,7 +549,12 @@ static void TestMath(skiatest::Reporter* reporter) { SkDebugf("SinCos: maximum error = %d\n", maxDiff); #endif -// test_premul(reporter); +#ifdef SK_SCALAR_IS_FLOAT + test_blend(reporter); +#endif + + // disable for now +// test_blend31(); } #include "TestClassDef.h" diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp index 8755bd3..485b38b 100644 --- a/tests/Matrix44Test.cpp +++ b/tests/Matrix44Test.cpp @@ -1,7 +1,14 @@ + +/* + * 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 "SkMatrix44.h" -static bool nearly_equal_scalar(SkScalar a, SkScalar b) { +static bool nearly_equal_scalar(SkMScalar a, SkMScalar b) { // Note that we get more compounded error for multiple operations when // SK_SCALAR_IS_FIXED. #ifdef SK_SCALAR_IS_FLOAT @@ -13,11 +20,37 @@ static bool nearly_equal_scalar(SkScalar a, SkScalar b) { return SkScalarAbs(a - b) <= tolerance; } +template <typename T> void assert16(skiatest::Reporter* reporter, const T data[], + T m0, T m1, T m2, T m3, + T m4, T m5, T m6, T m7, + T m8, T m9, T m10, T m11, + T m12, T m13, T m14, T m15) { + REPORTER_ASSERT(reporter, data[0] == m0); + REPORTER_ASSERT(reporter, data[1] == m1); + REPORTER_ASSERT(reporter, data[2] == m2); + REPORTER_ASSERT(reporter, data[3] == m3); + + REPORTER_ASSERT(reporter, data[4] == m4); + REPORTER_ASSERT(reporter, data[5] == m5); + REPORTER_ASSERT(reporter, data[6] == m6); + REPORTER_ASSERT(reporter, data[7] == m7); + + REPORTER_ASSERT(reporter, data[8] == m8); + REPORTER_ASSERT(reporter, data[9] == m9); + REPORTER_ASSERT(reporter, data[10] == m10); + REPORTER_ASSERT(reporter, data[11] == m11); + + REPORTER_ASSERT(reporter, data[12] == m12); + REPORTER_ASSERT(reporter, data[13] == m13); + REPORTER_ASSERT(reporter, data[14] == m14); + REPORTER_ASSERT(reporter, data[15] == m15); +} + static bool nearly_equal(const SkMatrix44& a, const SkMatrix44& b) { for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { if (!nearly_equal_scalar(a.get(i, j), b.get(i, j))) { - printf("not equal %g %g\n", (float)a.get(i, j), (float)b.get(i, j)); + printf("not equal %g %g\n", a.get(i, j), b.get(i, j)); return false; } } @@ -31,8 +64,20 @@ static bool is_identity(const SkMatrix44& m) { return nearly_equal(m, identity); } +static void test_common_angles(skiatest::Reporter* reporter) { + SkMatrix44 rot; + // Test precision of rotation in common cases + int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 }; + for (int i = 0; i < 9; ++i) { + rot.setRotateDegreesAbout(0, 0, -1, common_angles[i]); + + SkMatrix rot3x3 = rot; + REPORTER_ASSERT(reporter, rot3x3.rectStaysRect()); + } +} void TestMatrix44(skiatest::Reporter* reporter) { +#ifdef SK_SCALAR_IS_FLOAT SkMatrix44 mat, inverse, iden1, iden2, rot; mat.reset(); @@ -64,6 +109,40 @@ void TestMatrix44(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, is_identity(iden1)); iden2.setConcat(inverse, mat); REPORTER_ASSERT(reporter, is_identity(iden2)); + + // test rol/col Major getters + { + mat.setTranslate(2, 3, 4); + float dataf[16]; + double datad[16]; + + mat.asColMajorf(dataf); + assert16<float>(reporter, dataf, + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 2, 3, 4, 1); + mat.asColMajord(datad); + assert16<double>(reporter, datad, 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 2, 3, 4, 1); + mat.asRowMajorf(dataf); + assert16<float>(reporter, dataf, 1, 0, 0, 2, + 0, 1, 0, 3, + 0, 0, 1, 4, + 0, 0, 0, 1); + mat.asRowMajord(datad); + assert16<double>(reporter, datad, 1, 0, 0, 2, + 0, 1, 0, 3, + 0, 0, 1, 4, + 0, 0, 0, 1); + } + +#if 0 // working on making this pass + test_common_angles(reporter); +#endif +#endif } #include "TestClassDef.h" diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp index 4125f9f..c9a696c 100644 --- a/tests/MatrixTest.cpp +++ b/tests/MatrixTest.cpp @@ -1,5 +1,14 @@ + +/* + * 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 "SkMath.h" #include "SkMatrix.h" +#include "SkRandom.h" static bool nearly_equal_scalar(SkScalar a, SkScalar b) { // Note that we get more compounded error for multiple operations when @@ -48,6 +57,94 @@ static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) { REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0); } +void test_matrix_max_stretch(skiatest::Reporter* reporter) { + SkMatrix identity; + identity.reset(); + REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch()); + + SkMatrix scale; + scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4); + REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch()); + + SkMatrix rot90Scale; + rot90Scale.setRotate(90 * SK_Scalar1); + rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2); + REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch()); + + SkMatrix rotate; + rotate.setRotate(128 * SK_Scalar1); + REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero); + + SkMatrix translate; + translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1); + REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch()); + + SkMatrix perspX; + perspX.reset(); + perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000)); + REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch()); + + SkMatrix perspY; + perspY.reset(); + perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500)); + REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch()); + + SkMatrix baseMats[] = {scale, rot90Scale, rotate, + translate, perspX, perspY}; + SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)]; + for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) { + mats[i] = baseMats[i]; + bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]); + REPORTER_ASSERT(reporter, invertable); + } + SkRandom rand; + for (int m = 0; m < 1000; ++m) { + SkMatrix mat; + mat.reset(); + for (int i = 0; i < 4; ++i) { + int x = rand.nextU() % SK_ARRAY_COUNT(mats); + mat.postConcat(mats[x]); + } + SkScalar stretch = mat.getMaxStretch(); + + if ((stretch < 0) != mat.hasPerspective()) { + stretch = mat.getMaxStretch(); + } + + REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective()); + + if (mat.hasPerspective()) { + m -= 1; // try another non-persp matrix + continue; + } + + // test a bunch of vectors. None should be scaled by more than stretch + // (modulo some error) and we should find a vector that is scaled by + // almost stretch. + static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100; + static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100; + SkScalar max = 0; + SkVector vectors[1000]; + for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { + vectors[i].fX = rand.nextSScalar1(); + vectors[i].fY = rand.nextSScalar1(); + if (!vectors[i].normalize()) { + i -= 1; + continue; + } + } + mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors)); + for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) { + SkScalar d = vectors[i].length(); + REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol); + if (max < d) { + max = d; + } + } + REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol); + } +} + void TestMatrix(skiatest::Reporter* reporter) { SkMatrix mat, inverse, iden1, iden2; @@ -117,6 +214,30 @@ void TestMatrix(skiatest::Reporter* reporter) { m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect); } } + + mat.reset(); + mat.set(SkMatrix::kMScaleX, SkIntToScalar(1)); + mat.set(SkMatrix::kMSkewX, SkIntToScalar(2)); + mat.set(SkMatrix::kMTransX, SkIntToScalar(3)); + mat.set(SkMatrix::kMSkewY, SkIntToScalar(4)); + mat.set(SkMatrix::kMScaleY, SkIntToScalar(5)); + mat.set(SkMatrix::kMTransY, SkIntToScalar(6)); + SkScalar affine[6]; + REPORTER_ASSERT(reporter, mat.asAffine(affine)); + + #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e) + REPORTER_ASSERT(reporter, affineEqual(ScaleX)); + REPORTER_ASSERT(reporter, affineEqual(SkewY)); + REPORTER_ASSERT(reporter, affineEqual(SkewX)); + REPORTER_ASSERT(reporter, affineEqual(ScaleY)); + REPORTER_ASSERT(reporter, affineEqual(TransX)); + REPORTER_ASSERT(reporter, affineEqual(TransY)); + #undef affineEqual + + mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2)); + REPORTER_ASSERT(reporter, !mat.asAffine(affine)); + + test_matrix_max_stretch(reporter); } #include "TestClassDef.h" diff --git a/tests/MemsetTest.cpp b/tests/MemsetTest.cpp new file mode 100644 index 0000000..9c1fc92 --- /dev/null +++ b/tests/MemsetTest.cpp @@ -0,0 +1,91 @@ + +/* + * 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 "SkUtils.h" + +static void set_zero(void* dst, size_t bytes) { + char* ptr = (char*)dst; + for (size_t i = 0; i < bytes; ++i) { + ptr[i] = 0; + } +} + +#define MAX_ALIGNMENT 64 +#define MAX_COUNT ((MAX_ALIGNMENT) * 32) +#define PAD 32 +#define TOTAL (PAD + MAX_ALIGNMENT + MAX_COUNT + PAD) + +#define VALUE16 0x1234 +#define VALUE32 0x12345678 + +static bool compare16(const uint16_t base[], uint16_t value, int count) { + for (int i = 0; i < count; ++i) { + if (base[i] != value) { + SkDebugf("[%d] expected %x found %x\n", i, value, base[i]); + return false; + } + } + return true; +} + +static bool compare32(const uint32_t base[], uint32_t value, int count) { + for (int i = 0; i < count; ++i) { + if (base[i] != value) { + SkDebugf("[%d] expected %x found %x\n", i, value, base[i]); + return false; + } + } + return true; +} + +static void test_16(skiatest::Reporter* reporter) { + uint16_t buffer[TOTAL]; + + for (int count = 0; count < MAX_COUNT; ++count) { + for (int alignment = 0; alignment < MAX_ALIGNMENT; ++alignment) { + set_zero(buffer, sizeof(buffer)); + + uint16_t* base = &buffer[PAD + alignment]; + sk_memset16(base, VALUE16, count); + + compare16(buffer, 0, PAD + alignment); + compare16(base, VALUE16, count); + compare16(base + count, 0, TOTAL - count - PAD - alignment); + } + } +} + +static void test_32(skiatest::Reporter* reporter) { + uint32_t buffer[TOTAL]; + + for (int count = 0; count < MAX_COUNT; ++count) { + for (int alignment = 0; alignment < MAX_ALIGNMENT; ++alignment) { + set_zero(buffer, sizeof(buffer)); + + uint32_t* base = &buffer[PAD + alignment]; + sk_memset32(base, VALUE32, count); + + compare32(buffer, 0, PAD + alignment); + compare32(base, VALUE32, count); + compare32(base + count, 0, TOTAL - count - PAD - alignment); + } + } +} + +/** + * Test sk_memset16 and sk_memset32. + * For performance considerations, implementations may take different paths + * depending on the alignment of the dst, and/or the size of the count. + */ +static void TestMemset(skiatest::Reporter* reporter) { + test_16(reporter); + test_32(reporter); +}; + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Memset", TestMemsetClass, TestMemset) diff --git a/tests/MetaDataTest.cpp b/tests/MetaDataTest.cpp index 70829d4..1cc3cca 100644 --- a/tests/MetaDataTest.cpp +++ b/tests/MetaDataTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkMetaData.h" diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index 9a58fa6..82686ef 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -1,39 +1,67 @@ + /* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2010 The Android Open Source Project * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + #include <string> #include "Test.h" +#include "SkData.h" +#include "SkFlate.h" #include "SkPDFCatalog.h" #include "SkPDFStream.h" #include "SkPDFTypes.h" #include "SkScalar.h" #include "SkStream.h" +#include "SkTypes.h" + +class SkPDFTestDict : public SkPDFDict { +public: + void getResources(SkTDArray<SkPDFObject*>* resourceList) { + resourceList->setReserve(resourceList->count() + fResources.count()); + for (int i = 0; i < fResources.count(); i++) { + resourceList->push(fResources[i]); + fResources[i]->ref(); + } + } + + void addResource(SkPDFObject* object) { + fResources.append(1, &object); + } + +private: + SkTDArray<SkPDFObject*> fResources; +}; + +static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, + const void* buffer, size_t len) { + SkAutoDataUnref data(stream.copyToData()); + if (offset + len > data.size()) { + return false; + } + return memcmp(data.bytes() + offset, buffer, len) == 0; +} static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, - const std::string& representation, - bool indirect) { - size_t directSize = obj->getOutputSize(NULL, false); - REPORTER_ASSERT(reporter, directSize == representation.size()); + const char* expectedData, size_t expectedSize, + bool indirect, bool compression) { + SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0; + if (!compression) { + docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flag); + } + SkPDFCatalog catalog(docFlags); + size_t directSize = obj->getOutputSize(&catalog, false); + REPORTER_ASSERT(reporter, directSize == expectedSize); SkDynamicMemoryWStream buffer; - obj->emitObject(&buffer, NULL, false); + obj->emit(&buffer, &catalog, false); REPORTER_ASSERT(reporter, directSize == buffer.getOffset()); - REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), representation.c_str(), - directSize) == 0); + REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData, + directSize)); if (indirect) { // Indirect output. @@ -42,7 +70,6 @@ static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, static char footer[] = "\nendobj\n"; static size_t footerLen = strlen(footer); - SkPDFCatalog catalog; catalog.addObject(obj, false); size_t indirectSize = obj->getOutputSize(&catalog, true); @@ -50,21 +77,77 @@ static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj, indirectSize == directSize + headerLen + footerLen); buffer.reset(); - obj->emitObject(&buffer, &catalog, true); + obj->emit(&buffer, &catalog, true); REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset()); - REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), header, - headerLen) == 0); - REPORTER_ASSERT(reporter, - memcmp(buffer.getStream() + headerLen, - representation.c_str(), directSize) == 0); - REPORTER_ASSERT(reporter, - memcmp(buffer.getStream() + headerLen + directSize, - footer, footerLen) == 0); + REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen)); + REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData, + directSize)); + REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize, + footer, footerLen)); + } +} + +static void SimpleCheckObjectOutput(skiatest::Reporter* reporter, + SkPDFObject* obj, + const std::string& expectedResult) { + CheckObjectOutput(reporter, obj, expectedResult.c_str(), + expectedResult.length(), true, false); +} + +static void TestPDFStream(skiatest::Reporter* reporter) { + char streamBytes[] = "Test\nFoo\tBar"; + SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream( + streamBytes, strlen(streamBytes), true); + streamData->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get()); + stream->unref(); // SkRefPtr and new both took a reference. + SimpleCheckObjectOutput( + reporter, stream.get(), + "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream"); + stream->insert("Attribute", new SkPDFInt(42))->unref(); + SimpleCheckObjectOutput(reporter, stream.get(), + "<</Length 12\n/Attribute 42\n>> stream\n" + "Test\nFoo\tBar\nendstream"); + + if (SkFlate::HaveFlate()) { + char streamBytes2[] = "This is a longer string, so that compression " + "can do something with it. With shorter strings, " + "the short circuit logic cuts in and we end up " + "with an uncompressed string."; + SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2, + strlen(streamBytes2))); + SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData2.get()); + stream->unref(); // SkRefPtr and new both took a reference. + + SkDynamicMemoryWStream compressedByteStream; + SkFlate::Deflate(streamData2.get(), &compressedByteStream); + SkAutoDataUnref compressedData(compressedByteStream.copyToData()); + + // Check first without compression. + SkDynamicMemoryWStream expectedResult1; + expectedResult1.writeText("<</Length 167\n>> stream\n"); + expectedResult1.writeText(streamBytes2); + expectedResult1.writeText("\nendstream"); + SkAutoDataUnref expectedResultData1(expectedResult1.copyToData()); + CheckObjectOutput(reporter, stream.get(), + (const char*) expectedResultData1.data(), + expectedResultData1.size(), true, false); + + // Then again with compression. + SkDynamicMemoryWStream expectedResult2; + expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n" + ">> stream\n"); + expectedResult2.write(compressedData.data(), compressedData.size()); + expectedResult2.writeText("\nendstream"); + SkAutoDataUnref expectedResultData2(expectedResult2.copyToData()); + CheckObjectOutput(reporter, stream.get(), + (const char*) expectedResultData2.data(), + expectedResultData2.size(), true, true); } } static void TestCatalog(skiatest::Reporter* reporter) { - SkPDFCatalog catalog; + SkPDFCatalog catalog((SkPDFDocument::Flags)0); SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1); int1->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2); @@ -87,8 +170,8 @@ static void TestCatalog(skiatest::Reporter* reporter) { catalog.emitObjectNumber(&buffer, int3.get()); catalog.emitObjectNumber(&buffer, int1Again.get()); char expectedResult[] = "1 02 03 01 0"; - REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), expectedResult, - strlen(expectedResult)) == 0); + REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, + strlen(expectedResult))); } static void TestObjectRef(skiatest::Reporter* reporter) { @@ -99,7 +182,7 @@ static void TestObjectRef(skiatest::Reporter* reporter) { SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get()); int2ref->unref(); // SkRefPtr and new both took a reference. - SkPDFCatalog catalog; + SkPDFCatalog catalog((SkPDFDocument::Flags)0); catalog.addObject(int1.get(), false); catalog.addObject(int2.get(), false); REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3); @@ -109,101 +192,128 @@ static void TestObjectRef(skiatest::Reporter* reporter) { SkDynamicMemoryWStream buffer; int2ref->emitObject(&buffer, &catalog, false); REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult)); - REPORTER_ASSERT(reporter, memcmp(buffer.getStream(), expectedResult, - buffer.getOffset()) == 0); + REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, + buffer.getOffset())); +} + +static void TestSubstitute(skiatest::Reporter* reporter) { + SkRefPtr<SkPDFTestDict> proxy = new SkPDFTestDict(); + proxy->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFTestDict> stub = new SkPDFTestDict(); + stub->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFInt> int33 = new SkPDFInt(33); + int33->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFDict> stubResource = new SkPDFDict(); + stubResource->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFInt> int44 = new SkPDFInt(44); + int44->unref(); // SkRefPtr and new both took a reference. + + stub->insert("Value", int33.get()); + stubResource->insert("InnerValue", int44.get()); + stub->addResource(stubResource.get()); + + SkPDFCatalog catalog((SkPDFDocument::Flags)0); + catalog.addObject(proxy.get(), false); + catalog.setSubstitute(proxy.get(), stub.get()); + + SkDynamicMemoryWStream buffer; + proxy->emit(&buffer, &catalog, false); + catalog.emitSubstituteResources(&buffer, false); + + char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n"; + REPORTER_ASSERT( + reporter, + catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult)); + + char expectedResult[] = + "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n"; + REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult)); + REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, + buffer.getOffset())); } static void TestPDFPrimitives(skiatest::Reporter* reporter) { SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42); int42->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, int42.get(), "42", true); + SimpleCheckObjectOutput(reporter, int42.get(), "42"); SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf); realHalf->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, realHalf.get(), "0.5", true); + SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5"); #if defined(SK_SCALAR_IS_FLOAT) SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75); bigScalar->unref(); // SkRefPtr and new both took a reference. #if !defined(SK_ALLOW_LARGE_PDF_SCALARS) - CheckObjectOutput(reporter, bigScalar.get(), "111000", true); + SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000"); #else - CheckObjectOutput(reporter, bigScalar.get(), "110999.75", true); + SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75"); SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1); biggerScalar->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true); + SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000"); SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536); smallestScalar->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true); + SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526"); #endif #endif SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo"); stringSimple->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, stringSimple.get(), "(test \\) string \\( foo)", - true); + SimpleCheckObjectOutput(reporter, stringSimple.get(), + "(test \\) string \\( foo)"); SkRefPtr<SkPDFString> stringComplex = new SkPDFString("\ttest ) string ( foo"); stringComplex->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, stringComplex.get(), - "<0974657374202920737472696E67202820666F6F>", true); + SimpleCheckObjectOutput(reporter, stringComplex.get(), + "<0974657374202920737472696E67202820666F6F>"); SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab"); name->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, name.get(), "/Test#20name#09with#23tab", false); + const char expectedResult[] = "/Test#20name#09with#23tab"; + CheckObjectOutput(reporter, name.get(), expectedResult, + strlen(expectedResult), false, false); SkRefPtr<SkPDFArray> array = new SkPDFArray; array->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, array.get(), "[]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[]"); array->append(int42.get()); - CheckObjectOutput(reporter, array.get(), "[42]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[42]"); array->append(realHalf.get()); - CheckObjectOutput(reporter, array.get(), "[42 0.5]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]"); SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0); int0->unref(); // SkRefPtr and new both took a reference. array->append(int0.get()); - CheckObjectOutput(reporter, array.get(), "[42 0.5 0]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]"); SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1); int1->unref(); // SkRefPtr and new both took a reference. array->setAt(0, int1.get()); - CheckObjectOutput(reporter, array.get(), "[1 0.5 0]", true); + SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]"); SkRefPtr<SkPDFDict> dict = new SkPDFDict; dict->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, dict.get(), "<<>>", true); + SimpleCheckObjectOutput(reporter, dict.get(), "<<>>"); SkRefPtr<SkPDFName> n1 = new SkPDFName("n1"); n1->unref(); // SkRefPtr and new both took a reference. dict->insert(n1.get(), int42.get()); - CheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>", true); + SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>"); SkRefPtr<SkPDFName> n2 = new SkPDFName("n2"); n2->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> n3 = new SkPDFName("n3"); n3->unref(); // SkRefPtr and new both took a reference. dict->insert(n2.get(), realHalf.get()); dict->insert(n3.get(), array.get()); - CheckObjectOutput(reporter, dict.get(), - "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>", true); + SimpleCheckObjectOutput(reporter, dict.get(), + "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>"); - char streamBytes[] = "Test\nFoo\tBar"; - SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream( - streamBytes, strlen(streamBytes), true); - streamData->unref(); // SkRefPtr and new both took a reference. - SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get()); - stream->unref(); // SkRefPtr and new both took a reference. - CheckObjectOutput(reporter, stream.get(), - "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream", - true); - stream->insert(n1.get(), int42.get()); - CheckObjectOutput(reporter, stream.get(), - "<</Length 12\n/n1 42\n>> stream\nTest\nFoo\tBar" - "\nendstream", - true); + TestPDFStream(reporter); TestCatalog(reporter); TestObjectRef(reporter); + + TestSubstitute(reporter); } #include "TestClassDef.h" diff --git a/tests/PackBitsTest.cpp b/tests/PackBitsTest.cpp index a22590c..f7d4b8e 100644 --- a/tests/PackBitsTest.cpp +++ b/tests/PackBitsTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkPackBits.h" diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp index 5b19090..6350eb5 100644 --- a/tests/PaintTest.cpp +++ b/tests/PaintTest.cpp @@ -1,6 +1,67 @@ + +/* + * 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 "SkPaint.h" +#include "SkLayerDrawLooper.h" +#include "SkBlurMaskFilter.h" + +static void test_copy(skiatest::Reporter* reporter) { + SkPaint paint; + // set a few member variables + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setTextAlign(SkPaint::kLeft_Align); + paint.setStrokeWidth(SkIntToScalar(2)); + // set a few pointers + SkLayerDrawLooper* looper = new SkLayerDrawLooper(); + paint.setLooper(looper)->unref(); + SkMaskFilter* mask = SkBlurMaskFilter::Create(1, SkBlurMaskFilter::kNormal_BlurStyle); + paint.setMaskFilter(mask)->unref(); + + // copy the paint using the copy constructor and check they are the same + SkPaint copiedPaint = paint; + REPORTER_ASSERT(reporter, paint == copiedPaint); + +#ifdef SK_BUILD_FOR_ANDROID + // the copy constructor should preserve the Generation ID + int32_t paintGenID = paint.getGenerationID(); + int32_t copiedPaintGenID = copiedPaint.getGenerationID(); + REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID); + REPORTER_ASSERT(reporter, !memcmp(&paint, &copiedPaint, sizeof(paint))); +#endif + + // copy the paint using the equal operator and check they are the same + copiedPaint = paint; + REPORTER_ASSERT(reporter, paint == copiedPaint); + +#ifdef SK_BUILD_FOR_ANDROID + // the equals operator should increment the Generation ID + REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID); + REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID); + copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value + REPORTER_ASSERT(reporter, memcmp(&paint, &copiedPaint, sizeof(paint))); +#endif + + // clean the paint and check they are back to their initial states + SkPaint cleanPaint; + paint.reset(); + copiedPaint.reset(); + REPORTER_ASSERT(reporter, cleanPaint == paint); + REPORTER_ASSERT(reporter, cleanPaint == copiedPaint); + +#ifdef SK_BUILD_FOR_ANDROID + // the reset function should increment the Generation ID + REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID); + REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID); + REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &paint, sizeof(cleanPaint))); + REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &copiedPaint, sizeof(cleanPaint))); +#endif +} // found and fixed for webkit: mishandling when we hit recursion limit on // mostly degenerate cubic flatness test @@ -38,6 +99,7 @@ static void regression_cubic(skiatest::Reporter* reporter) { static void TestPaint(skiatest::Reporter* reporter) { // TODO add general paint tests + test_copy(reporter); // regression tests regression_cubic(reporter); diff --git a/tests/ParsePathTest.cpp b/tests/ParsePathTest.cpp index 917b0cb..831cd8e 100644 --- a/tests/ParsePathTest.cpp +++ b/tests/ParsePathTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkParsePath.h" diff --git a/tests/PathCoverageTest.cpp b/tests/PathCoverageTest.cpp index 8676029..91de178 100644 --- a/tests/PathCoverageTest.cpp +++ b/tests/PathCoverageTest.cpp @@ -1,15 +1,30 @@ + +/* + * 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 "SkMath.h" #include "SkPoint.h" #include "SkScalar.h" #include "Test.h" /* Duplicates lots of code from gpu/src/GrPathUtils.cpp - It'd be nice not to do so, but that code's set up currently to only have a single implementation. + It'd be nice not to do so, but that code's set up currently to only have + a single implementation. */ +// Sk uses 6, Gr (implicitly) used 10, both apparently arbitrarily. #define MAX_COEFF_SHIFT 6 static const uint32_t MAX_POINTS_PER_CURVE = 1 << MAX_COEFF_SHIFT; +// max + 0.5 min has error [0.0, 0.12] +// max + 0.375 min has error [-.03, 0.07] +// 0.96043387 max + 0.397824735 min has error [-.06, +.05] +// For determining the maximum possible number of points to use in +// drawing a quadratic, we want to err on the high side. static inline int cheap_distance(SkScalar dx, SkScalar dy) { int idx = SkAbs32(SkScalarRound(dx)); int idy = SkAbs32(SkScalarRound(dy)); @@ -21,30 +36,27 @@ static inline int cheap_distance(SkScalar dx, SkScalar dy) { return idx; } -static inline int diff_to_shift(SkScalar dx, SkScalar dy) { - int dist = cheap_distance(dx, dy); - return (32 - SkCLZ(dist)); +static inline int estimate_distance(const SkPoint points[]) { + return cheap_distance(points[1].fX * 2 - points[2].fX - points[0].fX, + points[1].fY * 2 - points[2].fY - points[0].fY); } -uint32_t estimatedQuadraticPointCount(const SkPoint points[], SkScalar tol) { - int shift = diff_to_shift(points[1].fX * 2 - points[2].fX - points[0].fX, - points[1].fY * 2 - points[2].fY - points[0].fY); - SkASSERT(shift >= 0); - //SkDebugf("Quad shift %d;", shift); - // bias to more closely approximate exact value, then clamp to zero - shift -= 2; - shift &= ~(shift>>31); +static inline SkScalar compute_distance(const SkPoint points[]) { + return points[1].distanceToLineSegmentBetween(points[0], points[2]); +} +static inline uint32_t estimate_pointCount(int distance) { + // Includes -2 bias because this estimator runs 4x high? + int shift = 30 - SkCLZ(distance); + // Clamp to zero if above subtraction went negative. + shift &= ~(shift>>31); if (shift > MAX_COEFF_SHIFT) { shift = MAX_COEFF_SHIFT; } - uint32_t count = 1 << shift; - //SkDebugf(" biased shift %d, scale %u\n", shift, count); - return count; + return 1 << shift; } -uint32_t computedQuadraticPointCount(const SkPoint points[], SkScalar tol) { - SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]); +static inline uint32_t compute_pointCount(SkScalar d, SkScalar tol) { if (d < tol) { return 1; } else { @@ -54,6 +66,26 @@ uint32_t computedQuadraticPointCount(const SkPoint points[], SkScalar tol) { } } +uint32_t quadraticPointCount_EE(const SkPoint points[], SkScalar tol) { + int distance = estimate_distance(points); + return estimate_pointCount(distance); +} + +uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) { + int distance = estimate_distance(points); + return compute_pointCount(SkIntToScalar(distance), tol); +} + +uint32_t quadraticPointCount_CE(const SkPoint points[], SkScalar tol) { + SkScalar distance = compute_distance(points); + return estimate_pointCount(SkScalarRound(distance)); +} + +uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) { + SkScalar distance = compute_distance(points); + return compute_pointCount(distance, tol); +} + // Curve from samplecode/SampleSlides.cpp static const int gXY[] = { 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4 @@ -82,18 +114,21 @@ static bool one_d_pe(const int* array, const unsigned int count, path[1] = SkPoint::Make(SkIntToScalar(array[0]), SkIntToScalar(array[1])); path[2] = SkPoint::Make(SkIntToScalar(array[2]), SkIntToScalar(array[3])); int numErrors = 0; - for (unsigned i = 4; i < (count); i += 2) { + for (unsigned i = 4; i < count; i += 2) { path[0] = path[1]; path[1] = path[2]; path[2] = SkPoint::Make(SkIntToScalar(array[i]), SkIntToScalar(array[i+1])); uint32_t computedCount = - computedQuadraticPointCount(path, SkIntToScalar(1)); + quadraticPointCount_CC(path, SkIntToScalar(1)); uint32_t estimatedCount = - estimatedQuadraticPointCount(path, SkIntToScalar(1)); - // Allow estimated to be off by a factor of two, but no more. - if ((estimatedCount > 2 * computedCount) || - (computedCount > estimatedCount * 2)) { + quadraticPointCount_EE(path, SkIntToScalar(1)); + // Allow estimated to be high by a factor of two, but no less than + // the computed value. + bool isAccurate = (estimatedCount >= computedCount) && + (estimatedCount <= 2 * computedCount); + + if (!isAccurate) { SkString errorDescription; errorDescription.printf( "Curve from %.2f %.2f through %.2f %.2f to %.2f %.2f " @@ -105,8 +140,6 @@ static bool one_d_pe(const int* array, const unsigned int count, } } - if (numErrors > 0) - printf("%d curve segments differ\n", numErrors); return (numErrors == 0); } diff --git a/tests/PathMeasureTest.cpp b/tests/PathMeasureTest.cpp index de18764..d454e37 100644 --- a/tests/PathMeasureTest.cpp +++ b/tests/PathMeasureTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkPathMeasure.h" diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 7e4e6bc..fadb0d9 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -1,7 +1,221 @@ + +/* + * 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 "SkPaint.h" #include "SkPath.h" #include "SkParse.h" +#include "SkParsePath.h" +#include "SkRandom.h" +#include "SkReader32.h" #include "SkSize.h" +#include "SkWriter32.h" + +static void test_direction(skiatest::Reporter* reporter) { + size_t i; + SkPath path; + REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); + REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction)); + REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction)); + + static const char* gDegen[] = { + "M 10 10", + "M 10 10 M 20 20", + "M 10 10 L 20 20", + "M 10 10 L 10 10 L 10 10", + "M 10 10 Q 10 10 10 10", + "M 10 10 C 10 10 10 10 10 10", + }; + for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) { + path.reset(); + bool valid = SkParsePath::FromSVGString(gDegen[i], &path); + REPORTER_ASSERT(reporter, valid); + REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); + } + + static const char* gCW[] = { + "M 10 10 L 10 10 Q 20 10 20 20", + "M 10 10 C 20 10 20 20 20 20", + }; + for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) { + path.reset(); + bool valid = SkParsePath::FromSVGString(gCW[i], &path); + REPORTER_ASSERT(reporter, valid); + REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction)); + } + + static const char* gCCW[] = { + "M 10 10 L 10 10 Q 20 10 20 -20", + "M 10 10 C 20 10 20 -20 20 -20", + }; + for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) { + path.reset(); + bool valid = SkParsePath::FromSVGString(gCCW[i], &path); + REPORTER_ASSERT(reporter, valid); + REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction)); + } +} + +static void add_rect(SkPath* path, const SkRect& r) { + path->moveTo(r.fLeft, r.fTop); + path->lineTo(r.fRight, r.fTop); + path->lineTo(r.fRight, r.fBottom); + path->lineTo(r.fLeft, r.fBottom); + path->close(); +} + +static void test_bounds(skiatest::Reporter* reporter) { + static const SkRect rects[] = { + { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) }, + { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) }, + { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) }, + { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) }, + }; + + SkPath path0, path1; + for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) { + path0.addRect(rects[i]); + add_rect(&path1, rects[i]); + } + + REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds()); +} + +static void stroke_cubic(const SkPoint pts[4]) { + SkPath path; + path.moveTo(pts[0]); + path.cubicTo(pts[1], pts[2], pts[3]); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SK_Scalar1 * 2); + + SkPath fill; + paint.getFillPath(path, &fill); +} + +// just ensure this can run w/o any SkASSERTS firing in the debug build +// we used to assert due to differences in how we determine a degenerate vector +// but that was fixed with the introduction of SkPoint::CanNormalize +static void stroke_tiny_cubic() { + SkPoint p0[] = { + { 372.0f, 92.0f }, + { 372.0f, 92.0f }, + { 372.0f, 92.0f }, + { 372.0f, 92.0f }, + }; + + stroke_cubic(p0); + + SkPoint p1[] = { + { 372.0f, 92.0f }, + { 372.0007f, 92.000755f }, + { 371.99927f, 92.003922f }, + { 371.99826f, 92.003899f }, + }; + + stroke_cubic(p1); +} + +static void check_close(skiatest::Reporter* reporter, const SkPath& path) { + for (int i = 0; i < 2; ++i) { + SkPath::Iter iter(path, (bool)i); + SkPoint mv; + SkPoint pts[4]; + SkPath::Verb v; + int nMT = 0; + int nCL = 0; + mv.set(0, 0); + while (SkPath::kDone_Verb != (v = iter.next(pts))) { + switch (v) { + case SkPath::kMove_Verb: + mv = pts[0]; + ++nMT; + break; + case SkPath::kClose_Verb: + REPORTER_ASSERT(reporter, mv == pts[0]); + ++nCL; + break; + default: + break; + } + } + // if we force a close on the interator we should have a close + // for every moveTo + REPORTER_ASSERT(reporter, !i || nMT == nCL); + } +} + +static void test_close(skiatest::Reporter* reporter) { + SkPath closePt; + closePt.moveTo(0, 0); + closePt.close(); + check_close(reporter, closePt); + + SkPath openPt; + openPt.moveTo(0, 0); + check_close(reporter, openPt); + + SkPath empty; + check_close(reporter, empty); + empty.close(); + check_close(reporter, empty); + + SkPath rect; + rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); + check_close(reporter, rect); + rect.close(); + check_close(reporter, rect); + + SkPath quad; + quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); + check_close(reporter, quad); + quad.close(); + check_close(reporter, quad); + + SkPath cubic; + quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, + 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1); + check_close(reporter, cubic); + cubic.close(); + check_close(reporter, cubic); + + SkPath line; + line.moveTo(SK_Scalar1, SK_Scalar1); + line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1); + check_close(reporter, line); + line.close(); + check_close(reporter, line); + + SkPath rect2; + rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); + rect2.close(); + rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); + check_close(reporter, rect2); + rect2.close(); + check_close(reporter, rect2); + + SkPath oval3; + oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100)); + oval3.close(); + oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200)); + check_close(reporter, oval3); + oval3.close(); + check_close(reporter, oval3); + + SkPath moves; + moves.moveTo(SK_Scalar1, SK_Scalar1); + moves.moveTo(5 * SK_Scalar1, SK_Scalar1); + moves.moveTo(SK_Scalar1, 10 * SK_Scalar1); + moves.moveTo(10 *SK_Scalar1, SK_Scalar1); + check_close(reporter, moves); + + stroke_tiny_cubic(); +} static void check_convexity(skiatest::Reporter* reporter, const SkPath& path, SkPath::Convexity expected) { @@ -16,30 +230,30 @@ static void test_convexity2(skiatest::Reporter* reporter) { check_convexity(reporter, pt, SkPath::kConvex_Convexity); SkPath line; - line.moveTo(12, 20); - line.lineTo(-12, -20); + line.moveTo(12*SK_Scalar1, 20*SK_Scalar1); + line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1); line.close(); check_convexity(reporter, pt, SkPath::kConvex_Convexity); SkPath triLeft; triLeft.moveTo(0, 0); - triLeft.lineTo(1, 0); - triLeft.lineTo(1, 1); + triLeft.lineTo(SK_Scalar1, 0); + triLeft.lineTo(SK_Scalar1, SK_Scalar1); triLeft.close(); check_convexity(reporter, triLeft, SkPath::kConvex_Convexity); SkPath triRight; triRight.moveTo(0, 0); - triRight.lineTo(-1, 0); - triRight.lineTo(1, 1); + triRight.lineTo(-SK_Scalar1, 0); + triRight.lineTo(SK_Scalar1, SK_Scalar1); triRight.close(); check_convexity(reporter, triRight, SkPath::kConvex_Convexity); SkPath square; square.moveTo(0, 0); - square.lineTo(1, 0); - square.lineTo(1, 1); - square.lineTo(0, 1); + square.lineTo(SK_Scalar1, 0); + square.lineTo(SK_Scalar1, SK_Scalar1); + square.lineTo(0, SK_Scalar1); square.close(); check_convexity(reporter, square, SkPath::kConvex_Convexity); @@ -47,15 +261,15 @@ static void test_convexity2(skiatest::Reporter* reporter) { redundantSquare.moveTo(0, 0); redundantSquare.lineTo(0, 0); redundantSquare.lineTo(0, 0); - redundantSquare.lineTo(1, 0); - redundantSquare.lineTo(1, 0); - redundantSquare.lineTo(1, 0); - redundantSquare.lineTo(1, 1); - redundantSquare.lineTo(1, 1); - redundantSquare.lineTo(1, 1); - redundantSquare.lineTo(0, 1); - redundantSquare.lineTo(0, 1); - redundantSquare.lineTo(0, 1); + redundantSquare.lineTo(SK_Scalar1, 0); + redundantSquare.lineTo(SK_Scalar1, 0); + redundantSquare.lineTo(SK_Scalar1, 0); + redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); + redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); + redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); + redundantSquare.lineTo(0, SK_Scalar1); + redundantSquare.lineTo(0, SK_Scalar1); + redundantSquare.lineTo(0, SK_Scalar1); redundantSquare.close(); check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity); @@ -63,35 +277,35 @@ static void test_convexity2(skiatest::Reporter* reporter) { bowTie.moveTo(0, 0); bowTie.lineTo(0, 0); bowTie.lineTo(0, 0); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 0); - bowTie.lineTo(1, 0); - bowTie.lineTo(1, 0); - bowTie.lineTo(0, 1); - bowTie.lineTo(0, 1); - bowTie.lineTo(0, 1); + bowTie.lineTo(SK_Scalar1, SK_Scalar1); + bowTie.lineTo(SK_Scalar1, SK_Scalar1); + bowTie.lineTo(SK_Scalar1, SK_Scalar1); + bowTie.lineTo(SK_Scalar1, 0); + bowTie.lineTo(SK_Scalar1, 0); + bowTie.lineTo(SK_Scalar1, 0); + bowTie.lineTo(0, SK_Scalar1); + bowTie.lineTo(0, SK_Scalar1); + bowTie.lineTo(0, SK_Scalar1); bowTie.close(); check_convexity(reporter, bowTie, SkPath::kConcave_Convexity); SkPath spiral; spiral.moveTo(0, 0); - spiral.lineTo(100, 0); - spiral.lineTo(100, 100); - spiral.lineTo(0, 100); - spiral.lineTo(0, 50); - spiral.lineTo(50, 50); - spiral.lineTo(50, 75); + spiral.lineTo(100*SK_Scalar1, 0); + spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1); + spiral.lineTo(0, 100*SK_Scalar1); + spiral.lineTo(0, 50*SK_Scalar1); + spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1); + spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1); spiral.close(); check_convexity(reporter, spiral, SkPath::kConcave_Convexity); SkPath dent; - dent.moveTo(SkIntToScalar(0), SkIntToScalar(0)); - dent.lineTo(SkIntToScalar(100), SkIntToScalar(100)); - dent.lineTo(SkIntToScalar(0), SkIntToScalar(100)); - dent.lineTo(SkIntToScalar(-50), SkIntToScalar(200)); - dent.lineTo(SkIntToScalar(-200), SkIntToScalar(100)); + dent.moveTo(0, 0); + dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1); + dent.lineTo(0, 100*SK_Scalar1); + dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1); + dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1); dent.close(); check_convexity(reporter, dent, SkPath::kConcave_Convexity); } @@ -137,16 +351,18 @@ static void test_convexity(skiatest::Reporter* reporter) { SkPath path; REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); - path.addCircle(0, 0, 10); + path.addCircle(0, 0, SkIntToScalar(10)); REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); - path.addCircle(0, 0, 10); // 2nd circle + path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path)); path.reset(); - path.addRect(0, 0, 10, 10, SkPath::kCCW_Direction); + path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction); REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); + REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction)); path.reset(); - path.addRect(0, 0, 10, 10, SkPath::kCW_Direction); + path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction); REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); + REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction)); static const struct { const char* fPathStr; @@ -170,6 +386,631 @@ static void test_convexity(skiatest::Reporter* reporter) { } } +// Simple isRect test is inline TestPath, below. +// test_isRect provides more extensive testing. +static void test_isRect(skiatest::Reporter* reporter) { + // passing tests (all moveTo / lineTo... + SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; + SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; + SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}}; + SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}}; + SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; + SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; + SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}}; + SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}}; + SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; + SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, + {1, 0}, {.5f, 0}}; + SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, + {0, 1}, {0, .5f}}; + SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; + SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; + SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; + + // failing tests + SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points + SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal + SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps + SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up + SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots + SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots + SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots + SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L' + + // failing, no close + SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match + SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto + + size_t testLen[] = { + sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6), + sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc), + sizeof(rd), sizeof(re), + sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6), + sizeof(f7), sizeof(f8), + sizeof(c1), sizeof(c2) + }; + SkPoint* tests[] = { + r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, + f1, f2, f3, f4, f5, f6, f7, f8, + c1, c2 + }; + SkPoint* lastPass = re; + SkPoint* lastClose = f8; + bool fail = false; + bool close = true; + const size_t testCount = sizeof(tests) / sizeof(tests[0]); + size_t index; + for (size_t testIndex = 0; testIndex < testCount; ++testIndex) { + SkPath path; + path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY); + for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) { + path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY); + } + if (close) { + path.close(); + } + REPORTER_ASSERT(reporter, fail ^ path.isRect(0)); + if (tests[testIndex] == lastPass) { + fail = true; + } + if (tests[testIndex] == lastClose) { + close = false; + } + } + + // fail, close then line + SkPath path1; + path1.moveTo(r1[0].fX, r1[0].fY); + for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { + path1.lineTo(r1[index].fX, r1[index].fY); + } + path1.close(); + path1.lineTo(1, 0); + REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); + + // fail, move in the middle + path1.reset(); + path1.moveTo(r1[0].fX, r1[0].fY); + for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { + if (index == 2) { + path1.moveTo(1, .5f); + } + path1.lineTo(r1[index].fX, r1[index].fY); + } + path1.close(); + REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); + + // fail, move on the edge + path1.reset(); + for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { + path1.moveTo(r1[index - 1].fX, r1[index - 1].fY); + path1.lineTo(r1[index].fX, r1[index].fY); + } + path1.close(); + REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); + + // fail, quad + path1.reset(); + path1.moveTo(r1[0].fX, r1[0].fY); + for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { + if (index == 2) { + path1.quadTo(1, .5f, 1, .5f); + } + path1.lineTo(r1[index].fX, r1[index].fY); + } + path1.close(); + REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); + + // fail, cubic + path1.reset(); + path1.moveTo(r1[0].fX, r1[0].fY); + for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { + if (index == 2) { + path1.cubicTo(1, .5f, 1, .5f, 1, .5f); + } + path1.lineTo(r1[index].fX, r1[index].fY); + } + path1.close(); + REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); +} + +static void test_flattening(skiatest::Reporter* reporter) { + SkPath p; + + static const SkPoint pts[] = { + { 0, 0 }, + { SkIntToScalar(10), SkIntToScalar(10) }, + { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 }, + { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) } + }; + p.moveTo(pts[0]); + p.lineTo(pts[1]); + p.quadTo(pts[2], pts[3]); + p.cubicTo(pts[4], pts[5], pts[6]); + + SkWriter32 writer(100); + p.flatten(writer); + size_t size = writer.size(); + SkAutoMalloc storage(size); + writer.flatten(storage.get()); + SkReader32 reader(storage.get(), size); + + SkPath p1; + REPORTER_ASSERT(reporter, p1 != p); + p1.unflatten(reader); + REPORTER_ASSERT(reporter, p1 == p); +} + +static void test_transform(skiatest::Reporter* reporter) { + SkPath p, p1; + + static const SkPoint pts[] = { + { 0, 0 }, + { SkIntToScalar(10), SkIntToScalar(10) }, + { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 }, + { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) } + }; + p.moveTo(pts[0]); + p.lineTo(pts[1]); + p.quadTo(pts[2], pts[3]); + p.cubicTo(pts[4], pts[5], pts[6]); + + SkMatrix matrix; + matrix.reset(); + p.transform(matrix, &p1); + REPORTER_ASSERT(reporter, p == p1); + + matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3); + p.transform(matrix, &p1); + SkPoint pts1[7]; + int count = p1.getPoints(pts1, 7); + REPORTER_ASSERT(reporter, 7 == count); + for (int i = 0; i < count; ++i) { + SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3); + REPORTER_ASSERT(reporter, newPt == pts1[i]); + } +} + +static void test_zero_length_paths(skiatest::Reporter* reporter) { + SkPath p; + SkPoint pt; + SkRect bounds; + + // Lone moveTo case + p.moveTo(SK_Scalar1, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 1 == p.countPoints()); + p.getLastPt(&pt); + REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1); + REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1); + bounds.set(0, 0, 0, 0); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // MoveTo-MoveTo case + p.moveTo(SK_Scalar1*2, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 2 == p.countPoints()); + p.getLastPt(&pt); + REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2); + REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1); + bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-close case + p.reset(); + p.moveTo(SK_Scalar1, SK_Scalar1); + p.close(); + bounds.set(0, 0, 0, 0); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 1 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-close-moveTo-close case + p.moveTo(SK_Scalar1*2, SK_Scalar1); + p.close(); + bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 2 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-line case + p.reset(); + p.moveTo(SK_Scalar1, SK_Scalar1); + p.lineTo(SK_Scalar1, SK_Scalar1); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 2 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-lineTo-moveTo-lineTo case + p.moveTo(SK_Scalar1*2, SK_Scalar1); + p.lineTo(SK_Scalar1*2, SK_Scalar1); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 4 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-line-close case + p.reset(); + p.moveTo(SK_Scalar1, SK_Scalar1); + p.lineTo(SK_Scalar1, SK_Scalar1); + p.close(); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 2 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-line-close-moveTo-line-close case + p.moveTo(SK_Scalar1*2, SK_Scalar1); + p.lineTo(SK_Scalar1*2, SK_Scalar1); + p.close(); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 4 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-quadTo case + p.reset(); + p.moveTo(SK_Scalar1, SK_Scalar1); + p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 3 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-quadTo-close case + p.close(); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 3 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-quadTo-moveTo-quadTo case + p.reset(); + p.moveTo(SK_Scalar1, SK_Scalar1); + p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1); + p.moveTo(SK_Scalar1*2, SK_Scalar1); + p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 6 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-cubicTo case + p.reset(); + p.moveTo(SK_Scalar1, SK_Scalar1); + p.cubicTo(SK_Scalar1, SK_Scalar1, + SK_Scalar1, SK_Scalar1, + SK_Scalar1, SK_Scalar1); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 4 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-quadTo-close case + p.close(); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 4 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); + + // moveTo-quadTo-moveTo-quadTo case + p.reset(); + p.moveTo(SK_Scalar1, SK_Scalar1); + p.cubicTo(SK_Scalar1, SK_Scalar1, + SK_Scalar1, SK_Scalar1, + SK_Scalar1, SK_Scalar1); + p.moveTo(SK_Scalar1*2, SK_Scalar1); + p.cubicTo(SK_Scalar1*2, SK_Scalar1, + SK_Scalar1*2, SK_Scalar1, + SK_Scalar1*2, SK_Scalar1); + bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1); + REPORTER_ASSERT(reporter, !p.isEmpty()); + REPORTER_ASSERT(reporter, 8 == p.countPoints()); + REPORTER_ASSERT(reporter, bounds == p.getBounds()); +} + +struct SegmentInfo { + SkPath fPath; + int fPointCount; +}; + +#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask) + +static void test_segment_masks(skiatest::Reporter* reporter) { + SkPath p; + p.moveTo(0, 0); + p.quadTo(100, 100, 200, 200); + REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks()); + REPORTER_ASSERT(reporter, !p.isEmpty()); + p.cubicTo(100, 100, 200, 200, 300, 300); + REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks()); + REPORTER_ASSERT(reporter, !p.isEmpty()); + p.reset(); + p.moveTo(0, 0); + p.cubicTo(100, 100, 200, 200, 300, 300); + REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks()); + REPORTER_ASSERT(reporter, !p.isEmpty()); +} + +static void test_iter(skiatest::Reporter* reporter) { + SkPath p; + SkPoint pts[4]; + + // Test an iterator with no path + SkPath::Iter noPathIter; + REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); + // Test that setting an empty path works + noPathIter.setPath(p, false); + REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); + // Test that close path makes no difference for an empty path + noPathIter.setPath(p, true); + REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); + + // Test an iterator with an initial empty path + SkPath::Iter iter(p, false); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // Test that close path makes no difference + SkPath::Iter forceCloseIter(p, true); + REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb); + + // Test that a move-only path produces nothing when iterated. + p.moveTo(SK_Scalar1, 0); + iter.setPath(p, false); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // No matter how many moves we add, we should still get nothing back. + p.moveTo(SK_Scalar1*2, 0); + p.moveTo(SK_Scalar1*3, 0); + p.moveTo(SK_Scalar1*4, 0); + p.moveTo(SK_Scalar1*5, 0); + iter.setPath(p, false); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // Nor should force closing + forceCloseIter.setPath(p, true); + REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb); + + // Initial closes should be ignored + p.reset(); + p.close(); + iter.setPath(p, false); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + // Even if force closed + forceCloseIter.setPath(p, true); + REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb); + + // Move/close sequences should also be ignored + p.reset(); + p.close(); + p.moveTo(SK_Scalar1, 0); + p.close(); + p.close(); + p.moveTo(SK_Scalar1*2, 0); + p.close(); + p.moveTo(SK_Scalar1*3, 0); + p.moveTo(SK_Scalar1*4, 0); + p.close(); + iter.setPath(p, false); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + // Even if force closed + forceCloseIter.setPath(p, true); + REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb); + + // The GM degeneratesegments.cpp test is more extensive +} + +static void test_raw_iter(skiatest::Reporter* reporter) { + SkPath p; + SkPoint pts[4]; + + // Test an iterator with no path + SkPath::RawIter noPathIter; + REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); + // Test that setting an empty path works + noPathIter.setPath(p); + REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); + + // Test an iterator with an initial empty path + SkPath::RawIter iter(p); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // Test that a move-only path returns the move. + p.moveTo(SK_Scalar1, 0); + iter.setPath(p); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); + REPORTER_ASSERT(reporter, pts[0].fY == 0); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // No matter how many moves we add, we should get them all back + p.moveTo(SK_Scalar1*2, SK_Scalar1); + p.moveTo(SK_Scalar1*3, SK_Scalar1*2); + iter.setPath(p); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); + REPORTER_ASSERT(reporter, pts[0].fY == 0); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); + REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3); + REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // Initial close is never ever stored + p.reset(); + p.close(); + iter.setPath(p); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // Move/close sequences + p.reset(); + p.close(); // Not stored, no purpose + p.moveTo(SK_Scalar1, 0); + p.close(); + p.close(); // Not stored, no purpose + p.moveTo(SK_Scalar1*2, SK_Scalar1); + p.close(); + p.moveTo(SK_Scalar1*3, SK_Scalar1*2); + p.moveTo(SK_Scalar1*4, SK_Scalar1*3); + p.close(); + iter.setPath(p); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); + REPORTER_ASSERT(reporter, pts[0].fY == 0); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); + REPORTER_ASSERT(reporter, pts[0].fY == 0); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); + REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); + REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3); + REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); + REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); + REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); + REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); + REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); + + // Generate random paths and verify + SkPoint randomPts[25]; + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { + randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j); + } + } + + // Max of 10 segments, max 3 points per segment + SkRandom rand(9876543); + SkPoint expectedPts[31]; // May have leading moveTo + SkPath::Verb expectedVerbs[11]; // May have leading moveTo + SkPath::Verb nextVerb; + for (int i = 0; i < 500; ++i) { + p.reset(); + bool lastWasClose = true; + bool haveMoveTo = false; + int numPoints = 0; + int numVerbs = (rand.nextU() >> 16) % 10; + int numIterVerbs = 0; + for (int j = 0; j < numVerbs; ++j) { + do { + nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb); + } while (lastWasClose && nextVerb == SkPath::kClose_Verb); + int numRequiredPts; + switch (nextVerb) { + case SkPath::kMove_Verb: + expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; + p.moveTo(expectedPts[numPoints]); + numPoints += 1; + lastWasClose = false; + haveMoveTo = true; + break; + case SkPath::kLine_Verb: + if (!haveMoveTo) { + expectedPts[numPoints++].set(0, 0); + expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; + haveMoveTo = true; + } + expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; + p.lineTo(expectedPts[numPoints]); + numPoints += 1; + lastWasClose = false; + break; + case SkPath::kQuad_Verb: + if (!haveMoveTo) { + expectedPts[numPoints++].set(0, 0); + expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; + haveMoveTo = true; + } + expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; + expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25]; + p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]); + numPoints += 2; + lastWasClose = false; + break; + case SkPath::kCubic_Verb: + if (!haveMoveTo) { + expectedPts[numPoints++].set(0, 0); + expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; + haveMoveTo = true; + } + expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; + expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25]; + expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25]; + p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1], + expectedPts[numPoints + 2]); + numPoints += 3; + lastWasClose = false; + break; + case SkPath::kClose_Verb: + p.close(); + lastWasClose = true; + break; + default:; + } + expectedVerbs[numIterVerbs++] = nextVerb; + } + + iter.setPath(p); + numVerbs = numIterVerbs; + numIterVerbs = 0; + int numIterPts = 0; + SkPoint lastMoveTo; + SkPoint lastPt; + lastMoveTo.set(0, 0); + lastPt.set(0, 0); + while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) { + REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]); + numIterVerbs++; + switch (nextVerb) { + case SkPath::kMove_Verb: + REPORTER_ASSERT(reporter, numIterPts < numPoints); + REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]); + lastPt = lastMoveTo = pts[0]; + numIterPts += 1; + break; + case SkPath::kLine_Verb: + REPORTER_ASSERT(reporter, numIterPts < numPoints + 1); + REPORTER_ASSERT(reporter, pts[0] == lastPt); + REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); + lastPt = pts[1]; + numIterPts += 1; + break; + case SkPath::kQuad_Verb: + REPORTER_ASSERT(reporter, numIterPts < numPoints + 2); + REPORTER_ASSERT(reporter, pts[0] == lastPt); + REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); + REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]); + lastPt = pts[2]; + numIterPts += 2; + break; + case SkPath::kCubic_Verb: + REPORTER_ASSERT(reporter, numIterPts < numPoints + 3); + REPORTER_ASSERT(reporter, pts[0] == lastPt); + REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); + REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]); + REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]); + lastPt = pts[3]; + numIterPts += 3; + break; + case SkPath::kClose_Verb: + REPORTER_ASSERT(reporter, pts[0] == lastMoveTo); + lastPt = lastMoveTo; + break; + default:; + } + } + REPORTER_ASSERT(reporter, numIterPts == numPoints); + REPORTER_ASSERT(reporter, numIterVerbs == numVerbs); + } +} + void TestPath(skiatest::Reporter* reporter); void TestPath(skiatest::Reporter* reporter) { { @@ -186,6 +1027,8 @@ void TestPath(skiatest::Reporter* reporter) { SkRect bounds, bounds2; REPORTER_ASSERT(reporter, p.isEmpty()); + REPORTER_ASSERT(reporter, 0 == p.countPoints()); + REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks()); REPORTER_ASSERT(reporter, p.isConvex()); REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType); REPORTER_ASSERT(reporter, !p.isInverseFillType()); @@ -198,14 +1041,24 @@ void TestPath(skiatest::Reporter* reporter) { p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1); check_convex_bounds(reporter, p, bounds); + // we have quads or cubics + REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask); + REPORTER_ASSERT(reporter, !p.isEmpty()); p.reset(); + REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks()); + REPORTER_ASSERT(reporter, p.isEmpty()); + p.addOval(bounds); check_convex_bounds(reporter, p, bounds); + REPORTER_ASSERT(reporter, !p.isEmpty()); p.reset(); p.addRect(bounds); check_convex_bounds(reporter, p, bounds); + // we have only lines + REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks()); + REPORTER_ASSERT(reporter, !p.isEmpty()); REPORTER_ASSERT(reporter, p != p2); REPORTER_ASSERT(reporter, !(p == p2)); @@ -222,9 +1075,8 @@ void TestPath(skiatest::Reporter* reporter) { p.offset(SK_Scalar1*3, SK_Scalar1*4); REPORTER_ASSERT(reporter, bounds == p.getBounds()); -#if 0 // isRect needs to be implemented REPORTER_ASSERT(reporter, p.isRect(NULL)); - bounds.setEmpty(); + bounds2.setEmpty(); REPORTER_ASSERT(reporter, p.isRect(&bounds2)); REPORTER_ASSERT(reporter, bounds == bounds2); @@ -232,16 +1084,19 @@ void TestPath(skiatest::Reporter* reporter) { bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2); p.addRect(bounds); REPORTER_ASSERT(reporter, !p.isRect(NULL)); -#endif - - SkPoint pt; - - p.moveTo(SK_Scalar1, 0); - p.getLastPt(&pt); - REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1); + test_isRect(reporter); + test_zero_length_paths(reporter); + test_direction(reporter); test_convexity(reporter); test_convexity2(reporter); + test_close(reporter); + test_segment_masks(reporter); + test_flattening(reporter); + test_transform(reporter); + test_bounds(reporter); + test_iter(reporter); + test_raw_iter(reporter); } #include "TestClassDef.h" diff --git a/tests/PointTest.cpp b/tests/PointTest.cpp new file mode 100644 index 0000000..876a272 --- /dev/null +++ b/tests/PointTest.cpp @@ -0,0 +1,46 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +// Unit tests for src/core/SkPoint.cpp and its header + +#include "SkPoint.h" +#include "Test.h" + +// Tests that SkPoint::length() and SkPoint::Length() both return +// approximately expectedLength for this (x,y). +static void test_length(skiatest::Reporter* reporter, SkScalar x, SkScalar y, + SkScalar expectedLength) { + SkPoint point; + point.set(x, y); + SkScalar s1 = point.length(); + SkScalar s2 = SkPoint::Length(x, y); + REPORTER_ASSERT(reporter, s1 == s2); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLength)); +} + +// Tests SkPoint::Normalize() for this (x,y) +static void test_Normalize(skiatest::Reporter* reporter, + SkScalar x, SkScalar y) { + SkPoint point; + point.set(x, y); + SkScalar oldLength = point.length(); + SkScalar returned = SkPoint::Normalize(&point); + SkScalar newLength = point.length(); + REPORTER_ASSERT(reporter, returned == oldLength); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1)); +} + +void PointTest(skiatest::Reporter* reporter) { + test_length(reporter, SkIntToScalar(3), SkIntToScalar(4), SkIntToScalar(5)); + test_length(reporter, SkFloatToScalar(0.6), SkFloatToScalar(0.8), + SK_Scalar1); + test_Normalize(reporter, SkIntToScalar(3), SkIntToScalar(4)); + test_Normalize(reporter, SkFloatToScalar(0.6), SkFloatToScalar(0.8)); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Point", PointTestClass, PointTest) diff --git a/tests/QuickRejectTest.cpp b/tests/QuickRejectTest.cpp new file mode 100644 index 0000000..8cde327 --- /dev/null +++ b/tests/QuickRejectTest.cpp @@ -0,0 +1,80 @@ +/* + * 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 "SkCanvas.h" +#include "SkDrawLooper.h" +#include "Test.h" + +/* + * Subclass of looper that just draws once, with an offset in X. + */ +class TestLooper : public SkDrawLooper { +public: + bool fOnce; + + virtual void init(SkCanvas*) SK_OVERRIDE { + fOnce = true; + } + + virtual bool next(SkCanvas* canvas, SkPaint*) SK_OVERRIDE { + if (fOnce) { + fOnce = false; + canvas->translate(SkIntToScalar(10), 0); + return true; + } + return false; + } + + virtual Factory getFactory() SK_OVERRIDE { + return NULL; + } +}; + +static void test_drawBitmap(skiatest::Reporter* reporter) { + SkBitmap src; + src.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); + src.allocPixels(); + src.eraseColor(SK_ColorWHITE); + + SkBitmap dst; + dst.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); + dst.allocPixels(); + dst.eraseColor(0); + + SkCanvas canvas(dst); + SkPaint paint; + + // we are initially transparent + REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5)); + + // we see the bitmap drawn + canvas.drawBitmap(src, 0, 0, &paint); + REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5)); + + // reverify we are clear again + dst.eraseColor(0); + REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5)); + + // if the bitmap is clipped out, we don't draw it + canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint); + REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5)); + + // now install our looper, which will draw, since it internally translates + // to the left. The test is to ensure that canvas' quickReject machinary + // allows us through, even though sans-looper we would look like we should + // be clipped out. + paint.setLooper(new TestLooper)->unref(); + canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint); + REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5)); +} + +static void test(skiatest::Reporter* reporter) { + test_drawBitmap(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("QuickReject", QuickRejectClass, test) diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp new file mode 100644 index 0000000..b531e92 --- /dev/null +++ b/tests/ReadPixelsTest.cpp @@ -0,0 +1,391 @@ + +/* + * 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 "SkCanvas.h" +#include "SkRegion.h" +#include "SkGpuDevice.h" + + +static const int DEV_W = 100, DEV_H = 100; +static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); +static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, + DEV_H * SK_Scalar1); + +namespace { +SkPMColor getCanvasColor(int x, int y) { + SkASSERT(x >= 0 && x < DEV_W); + SkASSERT(y >= 0 && y < DEV_H); + + U8CPU r = x; + U8CPU g = y; + U8CPU b = 0xc; + + U8CPU a = 0xff; + switch ((x+y) % 5) { + case 0: + a = 0xff; + break; + case 1: + a = 0x80; + break; + case 2: + a = 0xCC; + break; + case 4: + a = 0x01; + break; + case 3: + a = 0x00; + break; + } + return SkPremultiplyARGBInline(a, r, g, b); +} + +SkPMColor getBitmapColor(int x, int y, int w, int h) { + int n = y * w + x; + + U8CPU b = n & 0xff; + U8CPU g = (n >> 8) & 0xff; + U8CPU r = (n >> 16) & 0xff; + return SkPackARGB32(0xff, r, g , b); +} + +SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888, + uint32_t color, + bool* premul) { + const uint8_t* c = reinterpret_cast<uint8_t*>(&color); + U8CPU a,r,g,b; + *premul = false; + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + return color; + case SkCanvas::kNative_Unpremul_Config8888: + *premul = true; + a = SkGetPackedA32(color); + r = SkGetPackedR32(color); + g = SkGetPackedG32(color); + b = SkGetPackedB32(color); + break; + case SkCanvas::kBGRA_Unpremul_Config8888: + *premul = true; // fallthru + case SkCanvas::kBGRA_Premul_Config8888: + a = static_cast<U8CPU>(c[3]); + r = static_cast<U8CPU>(c[2]); + g = static_cast<U8CPU>(c[1]); + b = static_cast<U8CPU>(c[0]); + break; + case SkCanvas::kRGBA_Unpremul_Config8888: + *premul = true; // fallthru + case SkCanvas::kRGBA_Premul_Config8888: + a = static_cast<U8CPU>(c[3]); + r = static_cast<U8CPU>(c[0]); + g = static_cast<U8CPU>(c[1]); + b = static_cast<U8CPU>(c[2]); + break; + } + if (*premul) { + r = SkMulDiv255Ceiling(r, a); + g = SkMulDiv255Ceiling(g, a); + b = SkMulDiv255Ceiling(b, a); + } + return SkPackARGB32(a, r, g, b); +} + +void fillCanvas(SkCanvas* canvas) { + static SkBitmap bmp; + if (bmp.isNull()) { + bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H); + bool alloc = bmp.allocPixels(); + SkASSERT(alloc); + SkAutoLockPixels alp(bmp); + intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels()); + for (int y = 0; y < DEV_H; ++y) { + for (int x = 0; x < DEV_W; ++x) { + SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel()); + *pixel = getCanvasColor(x, y); + } + } + } + canvas->save(); + canvas->setMatrix(SkMatrix::I()); + canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op); + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + canvas->drawBitmap(bmp, 0, 0, &paint); + canvas->restore(); +} + +void fillBitmap(SkBitmap* bitmap) { + SkASSERT(bitmap->lockPixelsAreWritable()); + SkAutoLockPixels alp(*bitmap); + int w = bitmap->width(); + int h = bitmap->height(); + intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels()); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel()); + *pixel = getBitmapColor(x, y, w, h); + } + } +} + +bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { + if (!didPremulConversion) { + return a == b; + } + int32_t aA = static_cast<int32_t>(SkGetPackedA32(a)); + int32_t aR = static_cast<int32_t>(SkGetPackedR32(a)); + int32_t aG = static_cast<int32_t>(SkGetPackedG32(a)); + int32_t aB = SkGetPackedB32(a); + + int32_t bA = static_cast<int32_t>(SkGetPackedA32(b)); + int32_t bR = static_cast<int32_t>(SkGetPackedR32(b)); + int32_t bG = static_cast<int32_t>(SkGetPackedG32(b)); + int32_t bB = static_cast<int32_t>(SkGetPackedB32(b)); + + return aA == bA && + SkAbs32(aR - bR) <= 1 && + SkAbs32(aG - bG) <= 1 && + SkAbs32(aB - bB) <= 1; +} + +// checks the bitmap contains correct pixels after the readPixels +// if the bitmap was prefilled with pixels it checks that these weren't +// overwritten in the area outside the readPixels. +bool checkRead(skiatest::Reporter* reporter, + const SkBitmap& bitmap, + int x, int y, + bool checkCanvasPixels, + bool checkBitmapPixels, + SkCanvas::Config8888 config8888) { + SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config()); + SkASSERT(!bitmap.isNull()); + SkASSERT(checkCanvasPixels || checkBitmapPixels); + + int bw = bitmap.width(); + int bh = bitmap.height(); + + SkIRect srcRect = SkIRect::MakeXYWH(x, y, bw, bh); + SkIRect clippedSrcRect = DEV_RECT; + if (!clippedSrcRect.intersect(srcRect)) { + clippedSrcRect.setEmpty(); + } + bool failed = false; + SkAutoLockPixels alp(bitmap); + intptr_t pixels = reinterpret_cast<intptr_t>(bitmap.getPixels()); + for (int by = 0; by < bh; ++by) { + for (int bx = 0; bx < bw; ++bx) { + int devx = bx + srcRect.fLeft; + int devy = by + srcRect.fTop; + + uint32_t pixel = *reinterpret_cast<SkPMColor*>(pixels + by * bitmap.rowBytes() + bx * bitmap.bytesPerPixel()); + + if (clippedSrcRect.contains(devx, devy)) { + if (checkCanvasPixels) { + SkPMColor canvasPixel = getCanvasColor(devx, devy); + bool didPremul; + SkPMColor pmPixel = convertConfig8888ToPMColor(config8888, pixel, &didPremul); + bool check; + REPORTER_ASSERT(reporter, check = checkPixel(pmPixel, canvasPixel, didPremul)); + if (!check) { + failed = true; + } + } + } else if (checkBitmapPixels) { + REPORTER_ASSERT(reporter, getBitmapColor(bx, by, bw, bh) == pixel); + if (getBitmapColor(bx, by, bw, bh) != pixel) { + failed = true; + } + } + } + } + return !failed; +} + +enum BitmapInit { + kFirstBitmapInit = 0, + + kNoPixels_BitmapInit = kFirstBitmapInit, + kTight_BitmapInit, + kRowBytes_BitmapInit, + + kBitmapInitCnt +}; + +BitmapInit nextBMI(BitmapInit bmi) { + int x = bmi; + return static_cast<BitmapInit>(++x); +} + + +void init_bitmap(SkBitmap* bitmap, const SkIRect& rect, BitmapInit init) { + int w = rect.width(); + int h = rect.height(); + int rowBytes = 0; + bool alloc = true; + switch (init) { + case kNoPixels_BitmapInit: + alloc = false; + case kTight_BitmapInit: + break; + case kRowBytes_BitmapInit: + rowBytes = w * sizeof(SkPMColor) + 16 * sizeof(SkPMColor); + break; + default: + SkASSERT(0); + break; + } + bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes); + if (alloc) { + bitmap->allocPixels(); + } +} + +void ReadPixelsTest(skiatest::Reporter* reporter, GrContext* context) { + SkCanvas canvas; + + const SkIRect testRects[] = { + // entire thing + DEV_RECT, + // larger on all sides + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10), + // fully contained + SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4), + // outside top left + SkIRect::MakeLTRB(-10, -10, -1, -1), + // touching top left corner + SkIRect::MakeLTRB(-10, -10, 0, 0), + // overlapping top left corner + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4), + // overlapping top left and top right corners + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4), + // touching entire top edge + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0), + // overlapping top right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4), + // contained in x, overlapping top edge + SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4), + // outside top right corner + SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1), + // touching top right corner + SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0), + // overlapping top left and bottom left corners + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10), + // overlapping bottom left corner + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10), + // contained in y, overlapping left edge + SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4), + // outside bottom left corner + SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10), + // touching bottom left corner + SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10), + // overlapping bottom left and bottom right corners + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10), + // overlapping bottom right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // overlapping top right and bottom right corners + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), + }; + + for (int dtype = 1; dtype < 2; ++dtype) { + + if (0 == dtype) { + canvas.setDevice(new SkDevice(SkBitmap::kARGB_8888_Config, + DEV_W, + DEV_H, + false))->unref(); + } else { +#if SK_SCALAR_IS_FIXED + // GPU device known not to work in the fixed pt build. + continue; +#endif + canvas.setDevice(new SkGpuDevice(context, + SkBitmap::kARGB_8888_Config, + DEV_W, + DEV_H))->unref(); + } + fillCanvas(&canvas); + + static const SkCanvas::Config8888 gReadConfigs[] = { + SkCanvas::kNative_Premul_Config8888, + SkCanvas::kNative_Unpremul_Config8888, +/** + * There is a bug in Ganesh (http://code.google.com/p/skia/issues/detail?id=438) + * that causes the readback of pixels from BGRA canvas to an RGBA bitmap to + * fail. This should be removed as soon as the issue above is resolved. + */ +#if !defined(SK_BUILD_FOR_ANDROID) + SkCanvas::kBGRA_Premul_Config8888, + SkCanvas::kBGRA_Unpremul_Config8888, +#endif + SkCanvas::kRGBA_Premul_Config8888, + SkCanvas::kRGBA_Unpremul_Config8888, + }; + for (size_t rect = 0; rect < SK_ARRAY_COUNT(testRects); ++rect) { + const SkIRect& srcRect = testRects[rect]; + for (BitmapInit bmi = kFirstBitmapInit; + bmi < kBitmapInitCnt; + bmi = nextBMI(bmi)) { + for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) { + SkCanvas::Config8888 config8888 = gReadConfigs[c]; + SkBitmap bmp; + init_bitmap(&bmp, srcRect, bmi); + + // if the bitmap has pixels allocated before the readPixels, + // note that and fill them with pattern + bool startsWithPixels = !bmp.isNull(); + if (startsWithPixels) { + fillBitmap(&bmp); + } + + bool success = + canvas.readPixels(&bmp, srcRect.fLeft, + srcRect.fTop, config8888); + + // we expect to succeed when the read isn't fully clipped + // out. + bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT); + // determine whether we expected the read to succeed. + REPORTER_ASSERT(reporter, success == expectSuccess); + + if (success || startsWithPixels) { + checkRead(reporter, bmp, srcRect.fLeft, srcRect.fTop, + success, startsWithPixels, config8888); + } else { + // if we had no pixels beforehand and the readPixels + // failed then our bitmap should still not have pixels + REPORTER_ASSERT(reporter, bmp.isNull()); + } + } + // check the old webkit version of readPixels that clips the + // bitmap size + SkBitmap wkbmp; + bool success = canvas.readPixels(srcRect, &wkbmp); + SkIRect clippedRect = DEV_RECT; + if (clippedRect.intersect(srcRect)) { + REPORTER_ASSERT(reporter, success); + checkRead(reporter, wkbmp, clippedRect.fLeft, + clippedRect.fTop, true, false, + SkCanvas::kNative_Premul_Config8888); + } else { + REPORTER_ASSERT(reporter, !success); + } + } + } + } +} +} + +#include "TestClassDef.h" +DEFINE_GPUTESTCLASS("ReadPixels", ReadPixelsTestClass, ReadPixelsTest) + diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp index cad2d33..13f2fc4 100644 --- a/tests/Reader32Test.cpp +++ b/tests/Reader32Test.cpp @@ -1,20 +1,13 @@ -/* - Copyright 2011 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + * 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 "SkReader32.h" #include "Test.h" diff --git a/tests/RefDictTest.cpp b/tests/RefDictTest.cpp index f52541b..83d1aef 100644 --- a/tests/RefDictTest.cpp +++ b/tests/RefDictTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkRefDict.h" diff --git a/tests/RegionTest.cpp b/tests/RegionTest.cpp index ee34d8b..a1bf699 100644 --- a/tests/RegionTest.cpp +++ b/tests/RegionTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkRegion.h" #include "SkRandom.h" diff --git a/tests/ScalarTest.cpp b/tests/ScalarTest.cpp new file mode 100644 index 0000000..d2c05ab --- /dev/null +++ b/tests/ScalarTest.cpp @@ -0,0 +1,137 @@ +/* + * 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 "SkFloatingPoint.h" +#include "SkMath.h" +#include "SkPoint.h" +#include "SkRandom.h" + +#ifdef SK_CAN_USE_FLOAT + +static bool isFinite_int(float x) { + uint32_t bits = SkFloat2Bits(x); // need unsigned for our shifts + int exponent = bits << 1 >> 24; + return exponent != 0xFF; +} + +static bool isFinite_float(float x) { + return sk_float_isfinite(x); +} + +static bool isFinite_mulzero(float x) { + float y = x * 0; + return y == y; +} + +// return true if the float is finite +typedef bool (*IsFiniteProc1)(float); + +static bool isFinite2_and(float x, float y, IsFiniteProc1 proc) { + return proc(x) && proc(y); +} + +static bool isFinite2_mulzeroadd(float x, float y, IsFiniteProc1 proc) { + return proc(x * 0 + y * 0); +} + +// return true if both floats are finite +typedef bool (*IsFiniteProc2)(float, float, IsFiniteProc1); + +#endif + +enum FloatClass { + kFinite, + kInfinite, + kNaN +}; + +static void test_floatclass(skiatest::Reporter* reporter, float value, FloatClass fc) { + // our sk_float_is... function may return int instead of bool, + // hence the double ! to turn it into a bool + REPORTER_ASSERT(reporter, !!sk_float_isfinite(value) == (fc == kFinite)); + REPORTER_ASSERT(reporter, !!sk_float_isinf(value) == (fc == kInfinite)); + REPORTER_ASSERT(reporter, !!sk_float_isnan(value) == (fc == kNaN)); +} + +static void test_isfinite(skiatest::Reporter* reporter) { +#ifdef SK_CAN_USE_FLOAT + struct Rec { + float fValue; + bool fIsFinite; + }; + + float max = 3.402823466e+38f; + float inf = max * max; + float nan = inf * 0; + + test_floatclass(reporter, 0, kFinite); + test_floatclass(reporter, max, kFinite); + test_floatclass(reporter, -max, kFinite); + test_floatclass(reporter, inf, kInfinite); + test_floatclass(reporter, -inf, kInfinite); + test_floatclass(reporter, nan, kNaN); + test_floatclass(reporter, -nan, kNaN); + + const Rec data[] = { + { 0, true }, + { 1, true }, + { -1, true }, + { max * 0.75, true }, + { max, true }, + { -max * 0.75, true }, + { -max, true }, + { inf, false }, + { -inf, false }, + { nan, false }, + }; + + const IsFiniteProc1 gProc1[] = { + isFinite_int, + isFinite_float, + isFinite_mulzero + }; + const IsFiniteProc2 gProc2[] = { + isFinite2_and, + isFinite2_mulzeroadd + }; + + size_t i, n = SK_ARRAY_COUNT(data); + + for (i = 0; i < n; ++i) { + for (size_t k = 0; k < SK_ARRAY_COUNT(gProc1); ++k) { + const Rec& rec = data[i]; + bool finite = gProc1[k](rec.fValue); + REPORTER_ASSERT(reporter, rec.fIsFinite == finite); + } + } + + for (i = 0; i < n; ++i) { + const Rec& rec0 = data[i]; + for (size_t j = 0; j < n; ++j) { + const Rec& rec1 = data[j]; + for (size_t k = 0; k < SK_ARRAY_COUNT(gProc1); ++k) { + IsFiniteProc1 proc1 = gProc1[k]; + + for (size_t m = 0; m < SK_ARRAY_COUNT(gProc2); ++m) { + bool finite = gProc2[m](rec0.fValue, rec1.fValue, proc1); + bool finite2 = rec0.fIsFinite && rec1.fIsFinite; + REPORTER_ASSERT(reporter, finite2 == finite); + } + } + } + } +#endif +} + +static void TestScalar(skiatest::Reporter* reporter) { + test_isfinite(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Scalar", TestScalarClass, TestScalar) + diff --git a/tests/ShaderOpacityTest.cpp b/tests/ShaderOpacityTest.cpp new file mode 100644 index 0000000..a8177ee --- /dev/null +++ b/tests/ShaderOpacityTest.cpp @@ -0,0 +1,119 @@ + +/* + * 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 "SkShader.h" +#include "SkGradientShader.h" +#include "SkColorShader.h" + +static void test_bitmap(skiatest::Reporter* reporter) { + SkBitmap bmp; + bmp.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); + + // test 1: bitmap without pixel data + SkShader* shader = SkShader::CreateBitmapShader(bmp, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + REPORTER_ASSERT(reporter, shader); + REPORTER_ASSERT(reporter, !shader->isOpaque()); + shader->unref(); + + // From this point on, we have pixels + bmp.allocPixels(); + + // test 2: not opaque by default + shader = SkShader::CreateBitmapShader(bmp, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + REPORTER_ASSERT(reporter, shader); + REPORTER_ASSERT(reporter, !shader->isOpaque()); + shader->unref(); + + // test 3: explicitly opaque + bmp.setIsOpaque(true); + shader = SkShader::CreateBitmapShader(bmp, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + REPORTER_ASSERT(reporter, shader); + REPORTER_ASSERT(reporter, shader->isOpaque()); + shader->unref(); + + // test 4: explicitly not opaque + bmp.setIsOpaque(false); + shader = SkShader::CreateBitmapShader(bmp, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + REPORTER_ASSERT(reporter, shader); + REPORTER_ASSERT(reporter, !shader->isOpaque()); + shader->unref(); + +} + +static void test_gradient(skiatest::Reporter* reporter) +{ + SkPoint pts[2]; + pts[0].iset(0, 0); + pts[1].iset(1, 0); + SkColor colors[2]; + SkScalar pos[2] = {SkIntToScalar(0), SkIntToScalar(1)}; + int count = 2; + SkShader::TileMode mode = SkShader::kClamp_TileMode; + + // test 1: all opaque + colors[0] = SkColorSetARGB(0xFF, 0, 0, 0); + colors[1] = SkColorSetARGB(0xFF, 0, 0, 0); + SkShader* grad = SkGradientShader::CreateLinear(pts, colors, pos, count, + mode); + REPORTER_ASSERT(reporter, grad); + REPORTER_ASSERT(reporter, grad->isOpaque()); + grad->unref(); + + // test 2: all 0 alpha + colors[0] = SkColorSetARGB(0, 0, 0, 0); + colors[1] = SkColorSetARGB(0, 0, 0, 0); + grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + REPORTER_ASSERT(reporter, grad); + REPORTER_ASSERT(reporter, !grad->isOpaque()); + grad->unref(); + + // test 3: one opaque, one transparent + colors[0] = SkColorSetARGB(0xFF, 0, 0, 0); + colors[1] = SkColorSetARGB(0x40, 0, 0, 0); + grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + REPORTER_ASSERT(reporter, grad); + REPORTER_ASSERT(reporter, !grad->isOpaque()); + grad->unref(); + + // test 4: test 3, swapped + colors[0] = SkColorSetARGB(0x40, 0, 0, 0); + colors[1] = SkColorSetARGB(0xFF, 0, 0, 0); + grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); + REPORTER_ASSERT(reporter, grad); + REPORTER_ASSERT(reporter, !grad->isOpaque()); + grad->unref(); +} + +static void test_color(skiatest::Reporter* reporter) +{ + SkColorShader colorShader1(SkColorSetARGB(0,0,0,0)); + REPORTER_ASSERT(reporter, !colorShader1.isOpaque()); + SkColorShader colorShader2(SkColorSetARGB(0xFF,0,0,0)); + REPORTER_ASSERT(reporter, colorShader2.isOpaque()); + SkColorShader colorShader3(SkColorSetARGB(0x7F,0,0,0)); + REPORTER_ASSERT(reporter, !colorShader3.isOpaque()); + + // with inherrited color, shader must declare itself as opaque, + // since lack of opacity will depend solely on the paint + SkColorShader colorShader4; + REPORTER_ASSERT(reporter, colorShader4.isOpaque()); +} + +static void test_shader_opacity(skiatest::Reporter* reporter) +{ + test_gradient(reporter); + test_color(reporter); + test_bitmap(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("ShaderOpacity", ShaderOpacityTestClass, test_shader_opacity) diff --git a/tests/Sk64Test.cpp b/tests/Sk64Test.cpp index 9fb49eb..64eef75 100644 --- a/tests/Sk64Test.cpp +++ b/tests/Sk64Test.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkRandom.h" #include <math.h> diff --git a/tests/SortTest.cpp b/tests/SortTest.cpp index 2ca5553..9b4642f 100644 --- a/tests/SortTest.cpp +++ b/tests/SortTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkRandom.h" #include "SkTSearch.h" diff --git a/tests/SrcOverTest.cpp b/tests/SrcOverTest.cpp index e95485b..d1e65a9 100644 --- a/tests/SrcOverTest.cpp +++ b/tests/SrcOverTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkColorPriv.h" #include "SkXfermode.h" diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp index e62f2ed..85b1b2e 100644 --- a/tests/StreamTest.cpp +++ b/tests/StreamTest.cpp @@ -1,6 +1,14 @@ + +/* + * 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 "SkRandom.h" #include "SkStream.h" +#include "SkData.h" #define MAX_SIZE (256 * 1024) @@ -81,13 +89,51 @@ static void TestWStream(skiatest::Reporter* reporter) { for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0); } - REPORTER_ASSERT(reporter, memcmp(dst, ds.getStream(), 100*26) == 0); + + { + SkData* data = ds.copyToData(); + REPORTER_ASSERT(reporter, 100 * 26 == data->size()); + REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0); + data->unref(); + } delete[] dst; } +static void TestPackedUInt(skiatest::Reporter* reporter) { + // we know that packeduint tries to write 1, 2 or 4 bytes for the length, + // so we test values around each of those transitions (and a few others) + const size_t sizes[] = { + 0, 1, 2, 0xFC, 0xFD, 0xFE, 0xFF, 0x100, 0x101, 32767, 32768, 32769, + 0xFFFD, 0xFFFE, 0xFFFF, 0x10000, 0x10001, + 0xFFFFFD, 0xFFFFFE, 0xFFFFFF, 0x1000000, 0x1000001, + 0x7FFFFFFE, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF + }; + + + size_t i; + char buffer[sizeof(sizes) * 4]; + + SkMemoryWStream wstream(buffer, sizeof(buffer)); + for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) { + bool success = wstream.writePackedUInt(sizes[i]); + REPORTER_ASSERT(reporter, success); + } + wstream.flush(); + + SkMemoryStream rstream(buffer, sizeof(buffer)); + for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) { + size_t n = rstream.readPackedUInt(); + if (sizes[i] != n) { + SkDebugf("-- %d: sizes:%x n:%x\n", i, sizes[i], n); + } + REPORTER_ASSERT(reporter, sizes[i] == n); + } +} + static void TestStreams(skiatest::Reporter* reporter) { TestRStream(reporter); TestWStream(reporter); + TestPackedUInt(reporter); } #include "TestClassDef.h" diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp index 270ccfd..52a038d 100644 --- a/tests/StringTest.cpp +++ b/tests/StringTest.cpp @@ -1,5 +1,38 @@ + +/* + * 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 "SkString.h" +#include <stdarg.h> + + +// Windows vsnprintf doesn't 0-terminate safely), but is so far +// encapsulated in SkString that we can't test it directly. + +#ifdef SK_BUILD_FOR_WIN + #define VSNPRINTF(buffer, size, format, args) \ + vsnprintf_s(buffer, size, _TRUNCATE, format, args) +#else + #define VSNPRINTF vsnprintf +#endif + +#define ARGS_TO_BUFFER(format, buffer, size) \ + do { \ + va_list args; \ + va_start(args, format); \ + VSNPRINTF(buffer, size, format, args); \ + va_end(args); \ + } while (0) + +void printfAnalog(char* buffer, int size, const char format[], ...) { + ARGS_TO_BUFFER(format, buffer, size); +} + + static void TestString(skiatest::Reporter* reporter) { SkString a; @@ -74,8 +107,13 @@ static void TestString(skiatest::Reporter* reporter) { { -SK_Scalar1, "-1" }, { SK_Scalar1/2, "0.5" }, #ifdef SK_SCALAR_IS_FLOAT + #ifdef SK_BUILD_FOR_WIN + { 3.4028234e38f, "3.4028235e+038" }, + { -3.4028234e38f, "-3.4028235e+038" }, + #else { 3.4028234e38f, "3.4028235e+38" }, { -3.4028234e38f, "-3.4028235e+38" }, + #endif #endif }; for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { @@ -85,6 +123,19 @@ static void TestString(skiatest::Reporter* reporter) { // SkDebugf(" received <%s> expected <%s>\n", a.c_str(), gRec[i].fString); REPORTER_ASSERT(reporter, a.equals(gRec[i].fString)); } + + REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0")); + + char buffer [40]; + memset(buffer, 'a', 40); + REPORTER_ASSERT(reporter, buffer[18] == 'a'); + REPORTER_ASSERT(reporter, buffer[19] == 'a'); + REPORTER_ASSERT(reporter, buffer[20] == 'a'); + printfAnalog(buffer, 20, "%30d", 0); + REPORTER_ASSERT(reporter, buffer[18] == ' '); + REPORTER_ASSERT(reporter, buffer[19] == 0); + REPORTER_ASSERT(reporter, buffer[20] == 'a'); + } #include "TestClassDef.h" diff --git a/tests/Test.cpp b/tests/Test.cpp index 2bcd3e0..1c3b691 100644 --- a/tests/Test.cpp +++ b/tests/Test.cpp @@ -1,5 +1,16 @@ + +/* + * 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 "GrContext.h" +#include "SkNativeGLContext.h" +#include "SkTLazy.h" + using namespace skiatest; Reporter::Reporter() { @@ -63,3 +74,24 @@ bool Test::run() { return fReporter->getCurrSuccess(); } +/////////////////////////////////////////////////////////////////////////////// + + +GrContext* GpuTest::GetContext() { + // preserve this order, we want gGrContext destroyed after gEGLContext + static SkTLazy<SkNativeGLContext> gGLContext; + static SkAutoTUnref<GrContext> gGrContext; + + if (NULL == gGrContext.get()) { + gGLContext.init(); + if (gGLContext.get()->init(800, 600)) { + GrPlatform3DContext ctx = reinterpret_cast<GrPlatform3DContext>(gGLContext.get()->gl()); + gGrContext.reset(GrContext::Create(kOpenGL_Shaders_GrEngine, ctx)); + } + } + if (gGLContext.get()) { + gGLContext.get()->makeCurrent(); + } + return gGrContext.get(); +} + diff --git a/tests/Test.h b/tests/Test.h index de51801..8728040 100644 --- a/tests/Test.h +++ b/tests/Test.h @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef skiatest_Test_DEFINED #define skiatest_Test_DEFINED @@ -5,6 +12,9 @@ #include "SkString.h" #include "SkTRegistry.h" +class GrContext; +class SkGLContext; + namespace skiatest { class Test; @@ -87,6 +97,17 @@ namespace skiatest { SkString fName; }; + class GpuTest : public Test{ + public: + GpuTest() : Test() { + fContext = GetContext(); + } + protected: + GrContext* fContext; + private: + static GrContext* GetContext(); + }; + typedef SkTRegistry<Test*, void*> TestRegistry; } diff --git a/tests/TestClassDef.h b/tests/TestClassDef.h index 0773f5a..ffef2a1 100644 --- a/tests/TestClassDef.h +++ b/tests/TestClassDef.h @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ /* This file is meant to be included by .cpp files, so it can spew out a customized class + global definition. @@ -22,3 +29,18 @@ static TestRegistry gReg(classname::Factory); \ } +#define DEFINE_GPUTESTCLASS(uiname, classname, function) \ + namespace skiatest { \ + class classname : public GpuTest { \ + public: \ + static Test* Factory(void*) { return SkNEW(classname); } \ + protected: \ + virtual void onGetName(SkString* name) { name->set(uiname); } \ + virtual void onRun(Reporter* reporter) { \ + if (fContext) { \ + function(reporter, fContext); \ + } \ + } \ + }; \ + static TestRegistry gReg(classname::Factory); \ + } diff --git a/tests/TestSize.cpp b/tests/TestSize.cpp index 9e0aaaa..97a6668 100644 --- a/tests/TestSize.cpp +++ b/tests/TestSize.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkSize.h" diff --git a/tests/ToUnicode.cpp b/tests/ToUnicode.cpp new file mode 100644 index 0000000..c0a945a --- /dev/null +++ b/tests/ToUnicode.cpp @@ -0,0 +1,133 @@ + +/* + * Copyright 2010 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include <string> + +#include "Test.h" +#include "SkData.h" +#include "SkPDFTypes.h" +#include "SkPDFFont.h" +#include "SkStream.h" + +static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset, + const char* buffer, size_t len) { + SkAutoDataUnref data(stream.copyToData()); + if (offset + len > data.size()) { + return false; + } + if (len != strlen(buffer)) { + return false; + } + return memcmp(data.bytes() + offset, buffer, len) == 0; +} + +void append_cmap_sections(const SkTDArray<SkUnichar>& glyphToUnicode, + const SkPDFGlyphSet* subset, + SkDynamicMemoryWStream* cmap); + +static void TestToUnicode(skiatest::Reporter* reporter) { + SkTDArray<SkUnichar> glyphToUnicode; + SkTDArray<uint16_t> glyphsInSubset; + SkPDFGlyphSet subset; + + glyphToUnicode.push(0); // 0 + glyphToUnicode.push(0); // 1 + glyphToUnicode.push(0); // 2 + glyphsInSubset.push(3); + glyphToUnicode.push(0x20); // 3 + glyphsInSubset.push(4); + glyphToUnicode.push(0x25); // 4 + glyphsInSubset.push(5); + glyphToUnicode.push(0x27); // 5 + glyphsInSubset.push(6); + glyphToUnicode.push(0x28); // 6 + glyphsInSubset.push(7); + glyphToUnicode.push(0x29); // 7 + glyphsInSubset.push(8); + glyphToUnicode.push(0x2F); // 8 + glyphsInSubset.push(9); + glyphToUnicode.push(0x33); // 9 + glyphToUnicode.push(0); // 10 + glyphsInSubset.push(11); + glyphToUnicode.push(0x35); // 11 + glyphsInSubset.push(12); + glyphToUnicode.push(0x36); // 12 + for (uint16_t i = 13; i < 0xFE; ++i) { + glyphToUnicode.push(0); // Zero from index 0x9 to 0xFD + } + glyphsInSubset.push(0xFE); + glyphToUnicode.push(0x1010); + glyphsInSubset.push(0xFF); + glyphToUnicode.push(0x1011); + glyphsInSubset.push(0x100); + glyphToUnicode.push(0x1012); + glyphsInSubset.push(0x101); + glyphToUnicode.push(0x1013); + + SkDynamicMemoryWStream buffer; + subset.set(glyphsInSubset.begin(), glyphsInSubset.count()); + append_cmap_sections(glyphToUnicode, &subset, &buffer); + + char expectedResult[] = +"4 beginbfchar\n\ +<0003> <0020>\n\ +<0004> <0025>\n\ +<0008> <002F>\n\ +<0009> <0033>\n\ +endbfchar\n\ +4 beginbfrange\n\ +<0005> <0007> <0027>\n\ +<000B> <000C> <0035>\n\ +<00FE> <00FF> <1010>\n\ +<0100> <0101> <1012>\n\ +endbfrange\n"; + + REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult, + buffer.getOffset())); + + glyphToUnicode.reset(); + glyphsInSubset.reset(); + SkPDFGlyphSet subset2; + + // Test mapping: + // I n s t a l + // Glyph id 2c 51 56 57 44 4f + // Unicode 49 6e 73 74 61 6c + for (size_t i = 0; i < 100; ++i) { + glyphToUnicode.push(i + 29); + } + + glyphsInSubset.push(0x2C); + glyphsInSubset.push(0x44); + glyphsInSubset.push(0x4F); + glyphsInSubset.push(0x51); + glyphsInSubset.push(0x56); + glyphsInSubset.push(0x57); + + SkDynamicMemoryWStream buffer2; + subset2.set(glyphsInSubset.begin(), glyphsInSubset.count()); + append_cmap_sections(glyphToUnicode, &subset2, &buffer2); + + char expectedResult2[] = +"4 beginbfchar\n\ +<002C> <0049>\n\ +<0044> <0061>\n\ +<004F> <006C>\n\ +<0051> <006E>\n\ +endbfchar\n\ +1 beginbfrange\n\ +<0056> <0057> <0073>\n\ +endbfrange\n"; + + REPORTER_ASSERT(reporter, stream_equals(buffer2, 0, expectedResult2, + buffer2.getOffset())); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("ToUnicode", ToUnicodeTestClass, TestToUnicode) diff --git a/tests/TriangulationTest.cpp b/tests/TriangulationTest.cpp index 9d47181..8d692c7 100644 --- a/tests/TriangulationTest.cpp +++ b/tests/TriangulationTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "../../src/core/SkConcaveToTriangles.h" #include "SkGeometry.h" diff --git a/tests/UnicodeTest.cpp b/tests/UnicodeTest.cpp new file mode 100644 index 0000000..602ff81 --- /dev/null +++ b/tests/UnicodeTest.cpp @@ -0,0 +1,45 @@ + +/* + * 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 "SkUtils.h" + +// Unicode Variation Selector ranges: inclusive +#define UVS_MIN0 0x180B +#define UVS_MAX0 0x180D +#define UVS_MIN1 0xFE00 +#define UVS_MAX1 0xFE0F +#define UVS_MIN2 0xE0100 +#define UVS_MAX2 0xE01EF + +static bool isUVS(SkUnichar uni) { + return (uni >= UVS_MIN0 && uni <= UVS_MAX0) || + (uni >= UVS_MIN1 && uni <= UVS_MAX1) || + (uni >= UVS_MIN2 && uni <= UVS_MAX2); +} + +static void test_uvs(skiatest::Reporter* reporter) { + // [min, max], [min, max] ... inclusive + static const SkUnichar gRanges[] = { + UVS_MIN0, UVS_MAX0, UVS_MIN1, UVS_MAX1, UVS_MIN2, UVS_MAX2 + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRanges); i += 2) { + for (SkUnichar uni = gRanges[i] - 8; uni <= gRanges[i+1] + 8; ++uni) { + bool uvs0 = isUVS(uni); + bool uvs1 = SkUnichar_IsVariationSelector(uni); + REPORTER_ASSERT(reporter, uvs0 == uvs1); + } + } +} + +static void TestUnicode(skiatest::Reporter* reporter) { + test_uvs(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Unicode", TestUnicodeClass, TestUnicode) diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp index 4f36afe..f1b1ea9 100644 --- a/tests/UtilsTest.cpp +++ b/tests/UtilsTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkRandom.h" #include "SkRefCnt.h" diff --git a/tests/WArrayTest.cpp b/tests/WArrayTest.cpp new file mode 100644 index 0000000..428ca5f --- /dev/null +++ b/tests/WArrayTest.cpp @@ -0,0 +1,219 @@ +/* + * 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 the implementation so we can make an appropriate template instance. +#include "SkAdvancedTypefaceMetrics.cpp" + +using namespace skia_advanced_typeface_metrics_utils; + +namespace { + +// Negative values and zeros in a range plus trailing zeros. +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +const int16_t data1[] = {-1, 0, -3, 4, 5, 6, 7, 0, 0, 0, 8, 0, 0, 0, 0}; +const char* expected1 = "0[-1 0 -3 4 5 6 7 0 0 0 8]"; + +// Run with leading and trailing zeros. +// Test rules: d 0 1 2 3 4 5 6 7 8 9 10 11 +const int16_t data2[] = {0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 0, 0}; +const char* expected2 = "3 9 100"; + +// Removing 0's from a range. +// Test rules: a 0 1 2 3 4 5 6 7 8 9 10 11 +const int16_t data3[] = {1, 2, 0, 0, 0, 3, 4, 0, 0, 0, 0, 5}; +const char* expected3 = "0[1 2 0 0 0 3 4] 11[5]"; + +// Removing 0's from a run/range and between runs. +// Test rules: a, b 0 1 2 3 4 5 6 7 8 9 10 11 12 14 15 +const int16_t data4[] = {1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 0, 3, 4}; +const char* expected4 = "0[1 0 0 0 1] 5 7 2 8[3] 13[3 4]"; + +// Runs that starts outside a range. +// Test rules: a, e 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +const int16_t data5[] = {1, 1, 2, 3, 0, 0, 0, 0, 5, 5, 6, 7, 0, 0, 0, 0, 8, 0}; +const char* expected5 = "0 1 1 2[2 3] 8 9 5 10[6 7] 16[8]"; + +// Zeros and runs that should be broken out. +// Test rules: a, b, e 0 1 2 3 4 5 6 7 8 9 10 11 12 13 +const int16_t data6[] = {1, 0, 0, 0, 0, 1, 2, 3, 3, 4, 5, 5, 5, 6}; +const char* expected6 = "0[1] 5[1 2 3 3 4] 10 12 5 13[6]"; + +// Don't cares that aren't enough to break out a run. +// Test rules: c 0 1 2 3 4 5 +const int16_t data7[] = {1, 2, 10, 11, 2, 3}; +const char* expected7 = "0[1 2 10 11 2 3]"; +const uint32_t subset7[] = {0, 1, 4, 5}; +const char* expectedSubset7 = "0[1 2 0 0 2 3]"; + +// Don't cares that are enough to break out a run. +// Test rules: c 0 1 2 3 4 5 6 +const int16_t data8[] = {1, 2, 10, 11, 12, 2, 3}; +const char* expected8 = "0[1 2 10 11 12 2 3]"; +const uint32_t subset8[] = {0, 1, 5, 6}; +const char* expectedSubset8 = "0[1] 1 5 2 6[3]"; + +// Leading don't cares. +// Test rules: d 0 1 2 3 4 +const int16_t data9[] = {1, 1, 10, 2, 3}; +const char* expected9 = "0 1 1 2[10 2 3]"; +const uint32_t subset9[] = {0, 1, 3, 4}; +const char* expectedSubset9 = "0 1 1 3[2 3]"; + +// Almost run of don't cares inside a range. +// Test rules: c 0 1 2 3 4 5 +const int16_t data10[] = {1, 2, 10, 11, 12, 3}; +const char* expected10 = "0[1 2 10 11 12 3]"; +const uint32_t subset10[] = {0, 1, 5}; +const char* expectedSubset10 = "0[1 2 0 0 0 3]"; + +// Run of don't cares inside a range. +// Test rules: c 0 1 2 3 4 5 6 +const int16_t data11[] = {1, 2, 10, 11, 12, 13, 3}; +const char* expected11 = "0[1 2 10 11 12 13 3]"; +const uint32_t subset11[] = {0, 1, 6}; +const char* expectedSubset11 = "0[1 2] 6[3]"; + +// Almost run within a range with leading don't cares. +// Test rules: c 0 1 2 3 4 5 6 +const int16_t data12[] = {1, 10, 11, 2, 12, 13, 3}; +const char* expected12 = "0[1 10 11 2 12 13 3]"; +const uint32_t subset12[] = {0, 3, 6}; +const char* expectedSubset12 = "0[1 0 0 2 0 0 3]"; + +// Run within a range with leading don't cares. +// Test rules: c 0 1 2 3 4 5 6 7 +const int16_t data13[] = {1, 10, 11, 2, 2, 12, 13, 3}; +const char* expected13 = "0[1 10 11 2 2 12 13 3]"; +const uint32_t subset13[] = {0, 3, 4, 7}; +const char* expectedSubset13 = "0[1] 1 6 2 7[3]"; + +// Enough don't cares to breakup something. +// Test rules: a 0 1 2 3 4 5 +const int16_t data14[] = {1, 0, 0, 0, 0, 2}; +const char* expected14 = "0[1] 5[2]"; +const uint32_t subset14[] = {0, 5}; +const char* expectedSubset14 = "0[1] 5[2]"; + +} + +static SkString stringify_advance_data( + SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* data) { + SkString result; + bool leadingSpace = false; + while (data != NULL) { + if (leadingSpace) { + result.appendf(" "); + } else { + leadingSpace = true; + } + switch(data->fType) { + case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRun: + result.appendf("%d %d %d", data->fStartId, data->fEndId, + data->fAdvance[0]); + break; + case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRange: + result.appendf("%d[", data->fStartId); + for (int i = 0; i < data->fAdvance.count(); ++i) { + if (i > 0) { + result.appendf(" "); + } + result.appendf("%d", data->fAdvance[i]); + } + result.appendf("]"); + break; + case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kDefault: + result.appendf("<Default=%d>", data->fAdvance[0]); + break; + } + data = data->fNext.get(); + } + return result; +} + +class TestWData { + public: + TestWData(skiatest::Reporter* reporter, + const int16_t advances[], + int advanceLen, + const uint32_t subset[], + int subsetLen, + const char* expected) + : fAdvances(advances) + , fAdvancesLen(advanceLen) + , fSubset(subset) + , fSubsetLen(subsetLen) + , fExpected(expected) { + REPORTER_ASSERT(reporter, RunTest()); + } + + private: + const int16_t* fAdvances; + const int fAdvancesLen; + const uint32_t* fSubset; + const int fSubsetLen; + const char* fExpected; + + static bool getAdvance(TestWData* testCase, int gId, int16_t* advance) { + if (gId >= 0 && gId < testCase->fAdvancesLen) { + *advance = testCase->fAdvances[gId]; + return true; + } + return false; + } + + bool RunTest() { + SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t> > result; + result.reset(getAdvanceData(this, fAdvancesLen, fSubset, fSubsetLen, + getAdvance)); + + SkString stringResult = stringify_advance_data(result.get()); + if (!stringResult.equals(fExpected)) { + printf("Expected: %s\n Result: %s\n", fExpected, + stringResult.c_str()); + return false; + } + return true; + } +}; + +static void TestWArray(skiatest::Reporter* reporter) { + TestWData(reporter, data1, SK_ARRAY_COUNT(data1), NULL, 0, expected1); + TestWData(reporter, data2, SK_ARRAY_COUNT(data2), NULL, 0, expected2); + TestWData(reporter, data3, SK_ARRAY_COUNT(data3), NULL, 0, expected3); + TestWData(reporter, data4, SK_ARRAY_COUNT(data4), NULL, 0, expected4); + TestWData(reporter, data5, SK_ARRAY_COUNT(data5), NULL, 0, expected5); + TestWData(reporter, data6, SK_ARRAY_COUNT(data6), NULL, 0, expected6); + TestWData(reporter, data7, SK_ARRAY_COUNT(data7), NULL, 0, expected7); + TestWData(reporter, data7, SK_ARRAY_COUNT(data7), subset7, + SK_ARRAY_COUNT(subset7), expectedSubset7); + TestWData(reporter, data8, SK_ARRAY_COUNT(data8), NULL, 0, expected8); + TestWData(reporter, data8, SK_ARRAY_COUNT(data8), subset8, + SK_ARRAY_COUNT(subset8), expectedSubset8); + TestWData(reporter, data9, SK_ARRAY_COUNT(data9), NULL, 0, expected9); + TestWData(reporter, data9, SK_ARRAY_COUNT(data9), subset9, + SK_ARRAY_COUNT(subset9), expectedSubset9); + TestWData(reporter, data10, SK_ARRAY_COUNT(data10), NULL, 0, expected10); + TestWData(reporter, data10, SK_ARRAY_COUNT(data10), subset10, + SK_ARRAY_COUNT(subset10), expectedSubset10); + TestWData(reporter, data11, SK_ARRAY_COUNT(data11), NULL, 0, expected11); + TestWData(reporter, data11, SK_ARRAY_COUNT(data11), subset11, + SK_ARRAY_COUNT(subset11), expectedSubset11); + TestWData(reporter, data12, SK_ARRAY_COUNT(data12), NULL, 0, expected12); + TestWData(reporter, data12, SK_ARRAY_COUNT(data12), subset12, + SK_ARRAY_COUNT(subset12), expectedSubset12); + TestWData(reporter, data13, SK_ARRAY_COUNT(data13), NULL, 0, expected13); + TestWData(reporter, data13, SK_ARRAY_COUNT(data13), subset13, + SK_ARRAY_COUNT(subset13), expectedSubset13); + TestWData(reporter, data14, SK_ARRAY_COUNT(data14), NULL, 0, expected14); + TestWData(reporter, data14, SK_ARRAY_COUNT(data14), subset14, + SK_ARRAY_COUNT(subset14), expectedSubset14); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("WArray", WArrayTest, TestWArray) diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp new file mode 100644 index 0000000..0c5b7b9 --- /dev/null +++ b/tests/WritePixelsTest.cpp @@ -0,0 +1,419 @@ + +/* + * 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 "SkCanvas.h" +#include "SkRegion.h" +#include "SkGpuDevice.h" + +static const int DEV_W = 100, DEV_H = 100; +static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); +static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, + DEV_H * SK_Scalar1); +static const U8CPU DEV_PAD = 0xee; + +namespace { +SkPMColor getCanvasColor(int x, int y) { + SkASSERT(x >= 0 && x < DEV_W); + SkASSERT(y >= 0 && y < DEV_H); + + U8CPU r = x; + U8CPU g = y; + U8CPU b = 0xc; + + U8CPU a = 0x0; + switch ((x+y) % 5) { + case 0: + a = 0xff; + break; + case 1: + a = 0x80; + break; + case 2: + a = 0xCC; + break; + case 3: + a = 0x00; + break; + case 4: + a = 0x01; + break; + } + return SkPremultiplyARGBInline(a, r, g, b); +} + +bool config8888IsPremul(SkCanvas::Config8888 config8888) { + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + case SkCanvas::kBGRA_Premul_Config8888: + case SkCanvas::kRGBA_Premul_Config8888: + return true; + case SkCanvas::kNative_Unpremul_Config8888: + case SkCanvas::kBGRA_Unpremul_Config8888: + case SkCanvas::kRGBA_Unpremul_Config8888: + return false; + default: + SkASSERT(0); + return false; + } +} + +// assumes any premu/.unpremul has been applied +uint32_t packConfig8888(SkCanvas::Config8888 config8888, + U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + uint32_t r32; + uint8_t* result = reinterpret_cast<uint8_t*>(&r32); + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + case SkCanvas::kNative_Unpremul_Config8888: + r32 = SkPackARGB32NoCheck(a, r, g, b); + break; + case SkCanvas::kBGRA_Premul_Config8888: + case SkCanvas::kBGRA_Unpremul_Config8888: + result[0] = b; + result[1] = g; + result[2] = r; + result[3] = a; + break; + case SkCanvas::kRGBA_Premul_Config8888: + case SkCanvas::kRGBA_Unpremul_Config8888: + result[0] = r; + result[1] = g; + result[2] = b; + result[3] = a; + break; + default: + SkASSERT(0); + return 0; + } + return r32; +} + +uint32_t getBitmapColor(int x, int y, int w, int h, SkCanvas::Config8888 config8888) { + int n = y * w + x; + U8CPU b = n & 0xff; + U8CPU g = (n >> 8) & 0xff; + U8CPU r = (n >> 16) & 0xff; + U8CPU a = 0; + switch ((x+y) % 5) { + case 4: + a = 0xff; + break; + case 3: + a = 0x80; + break; + case 2: + a = 0xCC; + break; + case 1: + a = 0x01; + break; + case 0: + a = 0x00; + break; + } + if (config8888IsPremul(config8888)) { + r = SkMulDiv255Ceiling(r, a); + g = SkMulDiv255Ceiling(g, a); + b = SkMulDiv255Ceiling(b, a); + } + return packConfig8888(config8888, a, r, g , b); +} + +void fillCanvas(SkCanvas* canvas) { + static SkBitmap bmp; + if (bmp.isNull()) { + bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H); + bool alloc = bmp.allocPixels(); + SkASSERT(alloc); + SkAutoLockPixels alp(bmp); + intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels()); + for (int y = 0; y < DEV_H; ++y) { + for (int x = 0; x < DEV_W; ++x) { + SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel()); + *pixel = getCanvasColor(x, y); + } + } + } + canvas->save(); + canvas->setMatrix(SkMatrix::I()); + canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op); + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + canvas->drawBitmap(bmp, 0, 0, &paint); + canvas->restore(); +} + +SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888, + uint32_t color, + bool* premul) { + const uint8_t* c = reinterpret_cast<uint8_t*>(&color); + U8CPU a,r,g,b; + *premul = false; + switch (config8888) { + case SkCanvas::kNative_Premul_Config8888: + return color; + case SkCanvas::kNative_Unpremul_Config8888: + *premul = true; + a = SkGetPackedA32(color); + r = SkGetPackedR32(color); + g = SkGetPackedG32(color); + b = SkGetPackedB32(color); + break; + case SkCanvas::kBGRA_Unpremul_Config8888: + *premul = true; // fallthru + case SkCanvas::kBGRA_Premul_Config8888: + a = static_cast<U8CPU>(c[3]); + r = static_cast<U8CPU>(c[2]); + g = static_cast<U8CPU>(c[1]); + b = static_cast<U8CPU>(c[0]); + break; + case SkCanvas::kRGBA_Unpremul_Config8888: + *premul = true; // fallthru + case SkCanvas::kRGBA_Premul_Config8888: + a = static_cast<U8CPU>(c[3]); + r = static_cast<U8CPU>(c[0]); + g = static_cast<U8CPU>(c[1]); + b = static_cast<U8CPU>(c[2]); + break; + default: + GrCrash("Unexpected Config8888"); + } + if (*premul) { + r = SkMulDiv255Ceiling(r, a); + g = SkMulDiv255Ceiling(g, a); + b = SkMulDiv255Ceiling(b, a); + } + return SkPackARGB32(a, r, g, b); +} + +bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) { + if (!didPremulConversion) { + return a == b; + } + int32_t aA = static_cast<int32_t>(SkGetPackedA32(a)); + int32_t aR = static_cast<int32_t>(SkGetPackedR32(a)); + int32_t aG = static_cast<int32_t>(SkGetPackedG32(a)); + int32_t aB = SkGetPackedB32(a); + + int32_t bA = static_cast<int32_t>(SkGetPackedA32(b)); + int32_t bR = static_cast<int32_t>(SkGetPackedR32(b)); + int32_t bG = static_cast<int32_t>(SkGetPackedG32(b)); + int32_t bB = static_cast<int32_t>(SkGetPackedB32(b)); + + return aA == bA && + SkAbs32(aR - bR) <= 1 && + SkAbs32(aG - bG) <= 1 && + SkAbs32(aB - bB) <= 1; +} + +bool checkWrite(skiatest::Reporter* reporter, + SkCanvas* canvas, + const SkBitmap& bitmap, + int writeX, int writeY, + SkCanvas::Config8888 config8888) { + SkDevice* dev = canvas->getDevice(); + if (!dev) { + return false; + } + SkBitmap devBmp = dev->accessBitmap(false); + if (devBmp.width() != DEV_W || + devBmp.height() != DEV_H || + devBmp.config() != SkBitmap::kARGB_8888_Config || + devBmp.isNull()) { + return false; + } + SkAutoLockPixels alp(devBmp); + + intptr_t canvasPixels = reinterpret_cast<intptr_t>(devBmp.getPixels()); + size_t canvasRowBytes = devBmp.rowBytes(); + SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height()); + for (int cy = 0; cy < DEV_H; ++cy) { + const SkPMColor* canvasRow = reinterpret_cast<const SkPMColor*>(canvasPixels); + for (int cx = 0; cx < DEV_W; ++cx) { + SkPMColor canvasPixel = canvasRow[cx]; + if (writeRect.contains(cx, cy)) { + int bx = cx - writeX; + int by = cy - writeY; + uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(), bitmap.height(), config8888); + bool mul; + SkPMColor bmpPMColor = convertConfig8888ToPMColor(config8888, bmpColor8888, &mul); + bool check; + REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul)); + if (!check) { + return false; + } + } else { + bool check; + SkPMColor testColor = getCanvasColor(cx, cy); + REPORTER_ASSERT(reporter, check = (canvasPixel == testColor)); + if (!check) { + return false; + } + } + } + if (cy != DEV_H -1) { + const char* pad = reinterpret_cast<const char*>(canvasPixels + 4 * DEV_W); + for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) { + bool check; + REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD))); + if (!check) { + return false; + } + } + } + canvasPixels += canvasRowBytes; + } + + return true; +} + +enum DevType { + kRaster_DevType, + kGpu_DevType, +}; + +struct CanvasConfig { + DevType fDevType; + bool fTightRowBytes; +}; + +static const CanvasConfig gCanvasConfigs[] = { + {kRaster_DevType, true}, + {kRaster_DevType, false}, +#ifdef SK_SCALAR_IS_FLOAT + {kGpu_DevType, true}, // row bytes has no meaning on gpu devices +#endif +}; + +bool setupCanvas(SkCanvas* canvas, const CanvasConfig& c, GrContext* grCtx) { + switch (c.fDevType) { + case kRaster_DevType: { + SkBitmap bmp; + size_t rowBytes = c.fTightRowBytes ? 0 : 4 * DEV_W + 100; + bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H, rowBytes); + if (!bmp.allocPixels()) { + return false; + } + // if rowBytes isn't tight then set the padding to a known value + if (rowBytes) { + SkAutoLockPixels alp(bmp); + memset(bmp.getPixels(), DEV_PAD, bmp.getSafeSize()); + } + canvas->setDevice(new SkDevice(bmp))->unref(); + } break; + case kGpu_DevType: + canvas->setDevice(new SkGpuDevice(grCtx, + SkBitmap::kARGB_8888_Config, + DEV_W, DEV_H))->unref(); + break; + } + return true; +} + +bool setupBitmap(SkBitmap* bitmap, + SkCanvas::Config8888 config8888, + int w, int h, + bool tightRowBytes) { + size_t rowBytes = tightRowBytes ? 0 : 4 * w + 60; + bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes); + if (!bitmap->allocPixels()) { + return false; + } + SkAutoLockPixels alp(*bitmap); + intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels()); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + uint32_t* pixel = reinterpret_cast<uint32_t*>(pixels + y * bitmap->rowBytes() + x * 4); + *pixel = getBitmapColor(x, y, w, h, config8888); + } + } + return true; +} + +void WritePixelsTest(skiatest::Reporter* reporter, GrContext* context) { + SkCanvas canvas; + + const SkIRect testRects[] = { + // entire thing + DEV_RECT, + // larger on all sides + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10), + // fully contained + SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4), + // outside top left + SkIRect::MakeLTRB(-10, -10, -1, -1), + // touching top left corner + SkIRect::MakeLTRB(-10, -10, 0, 0), + // overlapping top left corner + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4), + // overlapping top left and top right corners + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H / 4), + // touching entire top edge + SkIRect::MakeLTRB(-10, -10, DEV_W + 10, 0), + // overlapping top right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H / 4), + // contained in x, overlapping top edge + SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W / 4, DEV_H / 4), + // outside top right corner + SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1), + // touching top right corner + SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0), + // overlapping top left and bottom left corners + SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10), + // overlapping bottom left corner + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10), + // contained in y, overlapping left edge + SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4), + // outside bottom left corner + SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10), + // touching bottom left corner + SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10), + // overlapping bottom left and bottom right corners + SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // touching entire left edge + SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10), + // overlapping bottom right corner + SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10), + // overlapping top right and bottom right corners + SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10), + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) { + REPORTER_ASSERT(reporter, setupCanvas(&canvas, gCanvasConfigs[i], context)); + + static const SkCanvas::Config8888 gReadConfigs[] = { + SkCanvas::kNative_Premul_Config8888, + SkCanvas::kNative_Unpremul_Config8888, + SkCanvas::kBGRA_Premul_Config8888, + SkCanvas::kBGRA_Unpremul_Config8888, + SkCanvas::kRGBA_Premul_Config8888, + SkCanvas::kRGBA_Unpremul_Config8888, + }; + for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) { + const SkIRect& rect = testRects[r]; + for (int tightBmp = 0; tightBmp < 2; ++tightBmp) { + for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) { + fillCanvas(&canvas); + SkCanvas::Config8888 config8888 = gReadConfigs[c]; + SkBitmap bmp; + REPORTER_ASSERT(reporter, setupBitmap(&bmp, config8888, rect.width(), rect.height(), SkToBool(tightBmp))); + canvas.writePixels(bmp, rect.fLeft, rect.fTop, config8888); + REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop, config8888)); + } + } + } + } +} +} + +#include "TestClassDef.h" +DEFINE_GPUTESTCLASS("WritePixels", WritePixelsTestClass, WritePixelsTest) + diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp index 63b1209..a9a07ad 100644 --- a/tests/Writer32Test.cpp +++ b/tests/Writer32Test.cpp @@ -1,20 +1,13 @@ -/* - Copyright 2011 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +/* + * 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 "SkReader32.h" #include "SkWriter32.h" #include "Test.h" diff --git a/tests/XfermodeTest.cpp b/tests/XfermodeTest.cpp index b552ce0..966da51 100644 --- a/tests/XfermodeTest.cpp +++ b/tests/XfermodeTest.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkColor.h" #include "SkXfermode.h" @@ -39,5 +46,27 @@ static void test_asMode(skiatest::Reporter* reporter) { bogusXfer->unref(); } +static void test_IsMode(skiatest::Reporter* reporter) { + REPORTER_ASSERT(reporter, SkXfermode::IsMode(NULL, + SkXfermode::kSrcOver_Mode)); + + for (int i = 0; i <= SkXfermode::kLastMode; ++i) { + SkXfermode::Mode mode = (SkXfermode::Mode)i; + + SkXfermode* xfer = SkXfermode::Create(mode); + REPORTER_ASSERT(reporter, SkXfermode::IsMode(xfer, mode)); + SkSafeUnref(xfer); + + if (SkXfermode::kSrcOver_Mode != mode) { + REPORTER_ASSERT(reporter, !SkXfermode::IsMode(NULL, mode)); + } + } +} + +static void test_xfermodes(skiatest::Reporter* reporter) { + test_asMode(reporter); + test_IsMode(reporter); +} + #include "TestClassDef.h" -DEFINE_TESTCLASS("Xfermode", XfermodeTestClass, test_asMode) +DEFINE_TESTCLASS("Xfermode", XfermodeTestClass, test_xfermodes) diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp index f931d62..b740590 100644 --- a/tests/skia_test.cpp +++ b/tests/skia_test.cpp @@ -1,3 +1,10 @@ + +/* + * 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 "SkGraphics.h" #include "Test.h" @@ -97,11 +104,40 @@ private: int main (int argc, char * const argv[]) { SkAutoGraphics ag; - + bool androidMode = false; - for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-android")) { + const char* matchStr = NULL; + + char* const* stop = argv + argc; + for (++argv; argv < stop; ++argv) { + if (strcmp(*argv, "-android") == 0) { androidMode = true; + + } else if (strcmp(*argv, "--match") == 0) { + ++argv; + if (argv < stop && **argv) { + matchStr = *argv; + } + } + } + + { + SkString header("Skia UnitTests:"); + if (matchStr) { + header.appendf(" --match %s", matchStr); + } +#ifdef SK_DEBUG + header.append(" SK_DEBUG"); +#else + header.append(" SK_RELEASE"); +#endif +#ifdef SK_SCALAR_IS_FIXED + header.append(" SK_SCALAR_IS_FIXED"); +#else + header.append(" SK_SCALAR_IS_FLOAT"); +#endif + if (!androidMode) { + SkDebugf("%s\n", header.c_str()); } } @@ -111,17 +147,24 @@ int main (int argc, char * const argv[]) { const int count = Iter::Count(); int index = 0; - int successCount = 0; + int failCount = 0; + int skipCount = 0; while ((test = iter.next()) != NULL) { reporter.setIndexOfTotal(index, count); - successCount += test->run(); + if (NULL != matchStr && !strstr(test->getName(), matchStr)) { + ++skipCount; + } else { + if (!test->run()) { + ++failCount; + } + } SkDELETE(test); index += 1; } if (!androidMode) { - SkDebugf("Finished %d tests, %d failures.\n", count, - count - successCount); + SkDebugf("Finished %d tests, %d failures, %d skipped.\n", + count, failCount, skipCount); } - return (count == successCount) ? 0 : 1; + return (failCount == 0) ? 0 : 1; } diff --git a/tests/tests_files.mk b/tests/tests_files.mk deleted file mode 100644 index 667c9b5..0000000 --- a/tests/tests_files.mk +++ /dev/null @@ -1,39 +0,0 @@ -SOURCE := \ - BitmapCopyTest.cpp \ - BitmapGetColorTest.cpp \ - BlitRowTest.cpp \ - ClampRangeTest.cpp \ - ClipCubicTest.cpp \ - ClipStackTest.cpp \ - ClipperTest.cpp \ - ColorFilterTest.cpp \ - ColorTest.cpp \ - DequeTest.cpp \ - DrawBitmapRectTest.cpp \ - FillPathTest.cpp \ - FlateTest.cpp \ - GeometryTest.cpp \ - InfRectTest.cpp \ - MathTest.cpp \ - MatrixTest.cpp \ - MetaDataTest.cpp \ - PackBitsTest.cpp \ - PaintTest.cpp \ - ParsePathTest.cpp \ - PathCoverageTest.cpp \ - PathMeasureTest.cpp \ - PathTest.cpp \ - Reader32Test.cpp \ - RefDictTest.cpp \ - RegionTest.cpp \ - Sk64Test.cpp \ - skia_test.cpp \ - SortTest.cpp \ - SrcOverTest.cpp \ - StreamTest.cpp \ - StringTest.cpp \ - Test.cpp \ - TestSize.cpp \ - UtilsTest.cpp \ - Writer32Test.cpp \ - XfermodeTest.cpp |