disable neon-opt for 32A->565 + alpha blend since it doesn't correctly treat
src-alpha == 0 as a no-op.

update unittests to measure this

Change-Id: If4d61ac5f7ff3d7fc27cbc3f242dbdf7ff4e76be
http://b/issue?id=2333376
diff --git a/Android.mk b/Android.mk
index d7b8444..a418776 100644
--- a/Android.mk
+++ b/Android.mk
@@ -280,3 +280,7 @@
 
 # golden-master (fidelity / regression test)
 include $(BASE_PATH)/gm/Android.mk
+
+# unit-tests
+include $(BASE_PATH)/tests/Android.mk
+
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
index 07c42ce..72c9d52 100644
--- a/src/core/SkBlitRow_D16.cpp
+++ b/src/core/SkBlitRow_D16.cpp
@@ -63,27 +63,16 @@
     SkASSERT(255 > alpha);
     
     if (count > 0) {
-        int src_scale = SkAlpha255To256(alpha);
         do {
             SkPMColor sc = *src++;
             SkPMColorAssert(sc);
-            if (sc)
-            {
+            if (sc) {
                 uint16_t dc = *dst;
-                unsigned sa = SkGetPackedA32(sc);
-                unsigned dr, dg, db;
-                
-                if (sa == 255) {
-                    dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale);
-                    dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale);
-                    db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale);
-                } else {
-                    unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale);
-                    dr = (SkPacked32ToR16(sc) * src_scale + SkGetPackedR16(dc) * dst_scale) >> 8;
-                    dg = (SkPacked32ToG16(sc) * src_scale + SkGetPackedG16(dc) * dst_scale) >> 8;
-                    db = (SkPacked32ToB16(sc) * src_scale + SkGetPackedB16(dc) * dst_scale) >> 8;
-                }
-                *dst = SkPackRGB16(dr, dg, db);
+                unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
+                unsigned dr = SkMulS16(SkPacked32ToR16(sc), alpha) + SkMulS16(SkGetPackedR16(dc), dst_scale);
+                unsigned dg = SkMulS16(SkPacked32ToG16(sc), alpha) + SkMulS16(SkGetPackedG16(dc), dst_scale);
+                unsigned db = SkMulS16(SkPacked32ToB16(sc), alpha) + SkMulS16(SkGetPackedB16(dc), dst_scale);
+                *dst = SkPackRGB16(SkDiv255Round(dr), SkDiv255Round(dg), SkDiv255Round(db));
             }
             dst += 1;
         } while (--count != 0);
diff --git a/src/opts/SkBlitRow_opts_arm.cpp b/src/opts/SkBlitRow_opts_arm.cpp
index 78fc907..5b6ce7e 100644
--- a/src/opts/SkBlitRow_opts_arm.cpp
+++ b/src/opts/SkBlitRow_opts_arm.cpp
@@ -281,23 +281,13 @@
     if (count > 0) {
         do {
             SkPMColor sc = *src++;
-            if (sc)
-            {
+            if (sc) {
                 uint16_t dc = *dst;
-                unsigned sa = SkGetPackedA32(sc);
-                unsigned dr, dg, db;
-                
-                if (sa == 255) {
-                    dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), alpha);
-                    dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), alpha);
-                    db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), alpha);
-                } else {
-                    unsigned dst_scale = 255 - SkAlphaMul(sa, alpha);
-                    dr = (SkPacked32ToR16(sc) * alpha + SkGetPackedR16(dc) * dst_scale) >> 8;
-                    dg = (SkPacked32ToG16(sc) * alpha + SkGetPackedG16(dc) * dst_scale) >> 8;
-                    db = (SkPacked32ToB16(sc) * alpha + SkGetPackedB16(dc) * dst_scale) >> 8;
-                }
-                *dst = SkPackRGB16(dr, dg, db);
+                unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
+                unsigned dr = SkMulS16(SkPacked32ToR16(sc), alpha) + SkMulS16(SkGetPackedR16(dc), dst_scale);
+                unsigned dg = SkMulS16(SkPacked32ToG16(sc), alpha) + SkMulS16(SkGetPackedG16(dc), dst_scale);
+                unsigned db = SkMulS16(SkPacked32ToB16(sc), alpha) + SkMulS16(SkGetPackedB16(dc), dst_scale);
+                *dst = SkPackRGB16(SkDiv255Round(dr), SkDiv255Round(dg), SkDiv255Round(db));
             }
             dst += 1;
         } while (--count != 0);
@@ -988,7 +978,8 @@
     S32_D565_Opaque_PROC,
     S32_D565_Blend_PROC,
     S32A_D565_Opaque_PROC,
-    S32A_D565_Blend_PROC,
+    // fails to treat src==0 as a no-op. see BlitRowTest.cpp
+    NULL,   // S32A_D565_Blend_PROC,
     
     // dither
     S32_D565_Opaque_Dither_PROC,
diff --git a/tests/Android.mk b/tests/Android.mk
index 9421f8b..efcceae 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -3,6 +3,7 @@
 
 
 LOCAL_SRC_FILES:= \
+        BlitRowTest.cpp \
         GeometryTest.cpp \
         MathTest.cpp \
         MatrixTest.cpp \
diff --git a/tests/BitmapCopyTest.cpp b/tests/BitmapCopyTest.cpp
new file mode 100644
index 0000000..be3850e
--- /dev/null
+++ b/tests/BitmapCopyTest.cpp
@@ -0,0 +1,184 @@
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkRect.h"
+
+static const char* boolStr(bool value) {
+    return value ? "true" : "false";
+}
+
+// these are in the same order as the SkBitmap::Config enum
+static const char* gConfigName[] = {
+    "None", "A1", "A8", "Index8", "565", "4444", "8888", "RLE_Index8"
+};
+
+static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src,
+                              const SkBitmap& dst) {
+    SkString str;
+    str.printf("src %s opaque:%d, dst %s opaque:%d",
+               gConfigName[src.config()], src.isOpaque(),
+               gConfigName[dst.config()], dst.isOpaque());
+    reporter->reportFailed(str);
+}
+
+static bool canHaveAlpha(SkBitmap::Config config) {
+    return config != SkBitmap::kRGB_565_Config;
+}
+
+// copyTo() should preserve isOpaque when it makes sense
+static void test_isOpaque(skiatest::Reporter* reporter, const SkBitmap& src,
+                          SkBitmap::Config dstConfig) {
+    SkBitmap bitmap(src);
+    SkBitmap dst;
+
+    // we need the lock so that we get a valid colorTable (when available)
+    SkAutoLockPixels alp(bitmap);
+    SkColorTable* ctable = bitmap.getColorTable();
+    unsigned ctableFlags = ctable ? ctable->getFlags() : 0;
+
+    if (canHaveAlpha(bitmap.config()) && canHaveAlpha(dstConfig)) {
+        bitmap.setIsOpaque(false);
+        if (ctable) {
+            ctable->setFlags(ctableFlags & ~SkColorTable::kColorsAreOpaque_Flag);
+        }
+        REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig));
+        REPORTER_ASSERT(reporter, dst.config() == dstConfig);
+        if (bitmap.isOpaque() != dst.isOpaque()) {
+            report_opaqueness(reporter, bitmap, dst);
+        }
+    }
+
+    bitmap.setIsOpaque(true);
+    if (ctable) {
+        ctable->setFlags(ctableFlags | SkColorTable::kColorsAreOpaque_Flag);
+    }
+    REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig));
+    REPORTER_ASSERT(reporter, dst.config() == dstConfig);
+    if (bitmap.isOpaque() != dst.isOpaque()) {
+        report_opaqueness(reporter, bitmap, dst);
+    }
+
+    if (ctable) {
+        ctable->setFlags(ctableFlags);
+    }
+}
+
+static void init_src(const SkBitmap& bitmap) {
+    SkAutoLockPixels lock(bitmap);
+    if (bitmap.getPixels()) {
+        memset(bitmap.getPixels(), 4, bitmap.getSize());
+    }
+}
+
+SkColorTable* init_ctable() {
+    static const SkColor colors[] = {
+        SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
+    };
+    return new SkColorTable(colors, SK_ARRAY_COUNT(colors));
+}
+
+struct Pair {
+    SkBitmap::Config    fConfig;
+    const char*         fValid;
+};
+
+static void TestBitmapCopy(skiatest::Reporter* reporter) {
+    static const Pair gPairs[] = {
+        { SkBitmap::kNo_Config,         "00000000"  },
+        { SkBitmap::kA1_Config,         "01000000"  },
+        { SkBitmap::kA8_Config,         "00101110"  },
+        { SkBitmap::kIndex8_Config,     "00111110"  },
+        { SkBitmap::kRGB_565_Config,    "00101110"  },
+        { SkBitmap::kARGB_4444_Config,  "00101110"  },
+        { SkBitmap::kARGB_8888_Config,  "00101110"  },
+// TODO: create valid RLE bitmap to test with
+ //       { SkBitmap::kRLE_Index8_Config, "00101111"  }
+    };
+
+    const int W = 20;
+    const int H = 33;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
+            SkBitmap src, dst;
+            SkColorTable* ct = NULL;
+
+            src.setConfig(gPairs[i].fConfig, W, H);
+            if (SkBitmap::kIndex8_Config == src.config() ||
+                    SkBitmap::kRLE_Index8_Config == src.config()) {
+                ct = init_ctable();
+            }
+            src.allocPixels(ct);
+            SkSafeUnref(ct);
+
+            init_src(src);
+            bool success = src.copyTo(&dst, gPairs[j].fConfig);
+            bool expected = gPairs[i].fValid[j] != '0';
+            if (success != expected) {
+                SkString str;
+                str.printf("SkBitmap::copyTo from %s to %s. expected %s returned %s",
+                           gConfigName[i], gConfigName[j], boolStr(expected),
+                           boolStr(success));
+                reporter->reportFailed(str);
+            }
+            
+            bool canSucceed = src.canCopyTo(gPairs[j].fConfig);
+            if (success != canSucceed) {
+                SkString str;
+                str.printf("SkBitmap::copyTo from %s to %s. returned %s canCopyTo %s",
+                           gConfigName[i], gConfigName[j], boolStr(success),
+                           boolStr(canSucceed));
+                reporter->reportFailed(str);
+            }
+
+            if (success) {
+                REPORTER_ASSERT(reporter, src.width() == dst.width());
+                REPORTER_ASSERT(reporter, src.height() == dst.height());
+                REPORTER_ASSERT(reporter, dst.config() == gPairs[j].fConfig);
+                test_isOpaque(reporter, src, dst.config());
+                if (src.config() == dst.config()) {
+                    SkAutoLockPixels srcLock(src);
+                    SkAutoLockPixels dstLock(dst);
+                    REPORTER_ASSERT(reporter, src.readyToDraw());
+                    REPORTER_ASSERT(reporter, dst.readyToDraw());
+                    const char* srcP = (const char*)src.getAddr(0, 0);
+                    const char* dstP = (const char*)dst.getAddr(0, 0);
+                    REPORTER_ASSERT(reporter, srcP != dstP);
+                    REPORTER_ASSERT(reporter, !memcmp(srcP, dstP,
+                                                      src.getSize()));
+                }
+                // test extractSubset
+                {
+                    SkBitmap subset;
+                    SkIRect r;
+                    r.set(1, 1, 2, 2);
+                    if (src.extractSubset(&subset, r)) {
+                        REPORTER_ASSERT(reporter, subset.width() == 1);
+                        REPORTER_ASSERT(reporter, subset.height() == 1);
+
+                        SkBitmap copy;
+                        REPORTER_ASSERT(reporter,
+                                        subset.copyTo(&copy, subset.config()));
+                        REPORTER_ASSERT(reporter, copy.width() == 1);
+                        REPORTER_ASSERT(reporter, copy.height() == 1);
+                        REPORTER_ASSERT(reporter, copy.rowBytes() <= 4);
+                        
+                        SkAutoLockPixels alp0(subset);
+                        SkAutoLockPixels alp1(copy);
+                        // they should both have, or both not-have, a colortable
+                        bool hasCT = subset.getColorTable() != NULL;
+                        REPORTER_ASSERT(reporter,
+                                    (copy.getColorTable() != NULL) == hasCT);
+                    }
+                }
+            } else {
+                // dst should be unchanged from its initial state
+                REPORTER_ASSERT(reporter, dst.config() == SkBitmap::kNo_Config);
+                REPORTER_ASSERT(reporter, dst.width() == 0);
+                REPORTER_ASSERT(reporter, dst.height() == 0);
+            }
+        }
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BitmapCopy", TestBitmapCopyClass, TestBitmapCopy)
diff --git a/tests/BlitRowTest.cpp b/tests/BlitRowTest.cpp
new file mode 100644
index 0000000..3442b6b
--- /dev/null
+++ b/tests/BlitRowTest.cpp
@@ -0,0 +1,158 @@
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkRect.h"
+
+static inline const char* boolStr(bool value) {
+    return value ? "true" : "false";
+}
+
+// these are in the same order as the SkBitmap::Config enum
+static const char* gConfigName[] = {
+    "None", "A1", "A8", "Index8", "565", "4444", "8888", "RLE_Index8"
+};
+
+/** Returns -1 on success, else the x coord of the first bad pixel, return its
+    value in bad
+ */
+typedef int (*Proc)(const void*, int width, uint32_t expected, uint32_t* bad);
+
+static int proc_32(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
+    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
+    for (int x = 0; x < w; x++) {
+        if (addr[x] != expected) {
+            *bad = addr[x];
+            return x;
+        }
+    }
+    return -1;
+}
+
+static int proc_16(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
+    const uint16_t* addr = static_cast<const uint16_t*>(ptr);
+    for (int x = 0; x < w; x++) {
+        if (addr[x] != expected) {
+            *bad = addr[x];
+            return x;
+        }
+    }
+    return -1;
+}
+
+static int proc_8(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
+    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
+    for (int x = 0; x < w; x++) {
+        if (SkGetPackedA32(addr[x]) != expected) {
+            *bad = SkGetPackedA32(addr[x]);
+            return x;
+        }
+    }
+    return -1;
+}
+
+static int proc_bad(const void* ptr, int, uint32_t, uint32_t* bad) {
+    *bad = 0;
+    return 0;
+}
+
+static Proc find_proc(const SkBitmap& bm, SkPMColor expect32, uint16_t expect16,
+                      uint8_t expect8, uint32_t* expect) {
+    switch (bm.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            *expect = expect32;
+            return proc_32;
+        case SkBitmap::kARGB_4444_Config:
+        case SkBitmap::kRGB_565_Config:
+            *expect = expect16;
+            return proc_16;
+        case SkBitmap::kA8_Config:
+            *expect = expect8;
+            return proc_8;
+        default:
+            *expect = 0;
+            return proc_bad;
+    }
+}
+
+static bool check_color(const SkBitmap& bm, SkPMColor expect32,
+                        uint16_t expect16, uint8_t expect8,
+                        skiatest::Reporter* reporter) {
+    uint32_t expect;
+    Proc proc = find_proc(bm, expect32, expect16, expect8, &expect);
+    for (int y = 0; y < bm.height(); y++) {
+        uint32_t bad;
+        int x = proc(bm.getAddr(0, y), bm.width(), expect, &bad);
+        if (x >= 0) {
+            SkString str;
+            str.printf("BlitRow config=%s [%d %d] expected %x got %x",
+                       gConfigName[bm.config()], x, y, expect, bad);
+            reporter->reportFailed(str);
+            return false;
+        }
+    }
+    return true;
+}
+
+static void TestBlitRow(skiatest::Reporter* reporter) {
+    static const int W = 256;
+
+    static const SkBitmap::Config gDstConfig[] = {
+        SkBitmap::kARGB_8888_Config,
+        SkBitmap::kRGB_565_Config,
+//        SkBitmap::kARGB_4444_Config,
+//        SkBitmap::kA8_Config,
+    };
+
+    static const struct {
+        SkColor     fSrc;
+        SkColor     fDst;
+        SkPMColor   fResult32;
+        uint16_t    fResult16;
+        uint8_t     fResult8;
+    } gSrcRec[] = {
+        { 0,            0,          0,                                    0,      0 },
+        { 0,            0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
+        { 0xFFFFFFFF,   0,          SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
+        { 0xFFFFFFFF,   0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
+    };
+
+    SkPaint paint;
+    paint.setDither(true);
+
+    SkBitmap srcBM;
+    srcBM.setConfig(SkBitmap::kARGB_8888_Config, W, 1);
+    srcBM.allocPixels();
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstConfig); i++) {
+        SkBitmap dstBM;
+        dstBM.setConfig(gDstConfig[i], W, 1);
+        dstBM.allocPixels();
+        
+        SkCanvas canvas(dstBM);
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gSrcRec); j++) {
+            srcBM.eraseColor(gSrcRec[j].fSrc);
+            dstBM.eraseColor(gSrcRec[j].fDst);
+
+            for (int k = 0; k < 4; k++) {
+                bool dither = (k & 1) != 0;
+                bool blend = (k & 2) != 0;
+                if (gSrcRec[j].fSrc != 0 && blend) {
+                    // can't make a numerical promise about blending anything
+                    // but 0
+                 //   continue;
+                }
+                paint.setDither(dither);
+                paint.setAlpha(blend ? 0x80 : 0xFF);
+                canvas.drawBitmap(srcBM, 0, 0, &paint);
+                if (!check_color(dstBM, gSrcRec[j].fResult32, gSrcRec[j].fResult16,
+                                 gSrcRec[j].fResult8, reporter)) {
+                    SkDebugf("--- src index %d dither %d blend %d\n", j, dither, blend);
+                }
+            }
+        }
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BlitRow", TestBlitRowClass, TestBlitRow)
diff --git a/tests/ClipperTest.cpp b/tests/ClipperTest.cpp
new file mode 100644
index 0000000..66301fc
--- /dev/null
+++ b/tests/ClipperTest.cpp
@@ -0,0 +1,89 @@
+#include "Test.h"
+#include "SkPath.h"
+#include "SkLineClipper.h"
+#include "SkEdgeClipper.h"
+
+static void test_intersectline(skiatest::Reporter* reporter) {
+    static const SkScalar L = 0;
+    static const SkScalar T = 0;
+    static const SkScalar R = SkIntToScalar(100);
+    static const SkScalar B = SkIntToScalar(100);
+    static const SkScalar CX = SkScalarHalf(L + R);
+    static const SkScalar CY = SkScalarHalf(T + B);
+    static const SkRect gR = { L, T, R, B };
+
+    size_t i;
+    SkPoint dst[2];
+
+    static const SkPoint gEmpty[] = {
+        // sides
+        { L, CY }, { L - 10, CY },
+        { R, CY }, { R + 10, CY },
+        { CX, T }, { CX, T - 10 },
+        { CX, B }, { CX, B + 10 },
+        // corners
+        { L, T }, { L - 10, T - 10 },
+        { L, B }, { L - 10, B + 10 },
+        { R, T }, { R + 10, T - 10 },
+        { R, B }, { R + 10, B + 10 },
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gEmpty); i += 2) {
+        bool valid = SkLineClipper::IntersectLine(&gEmpty[i], gR, dst);
+        if (valid) {
+            SkDebugf("----- [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+        }
+        REPORTER_ASSERT(reporter, !valid);
+    }
+    
+    static const SkPoint gFull[] = {
+        // diagonals, chords
+        { L, T }, { R, B },
+        { L, B }, { R, T },
+        { CX, T }, { CX, B },
+        { L, CY }, { R, CY },
+        { CX, T }, { R, CY },
+        { CX, T }, { L, CY },
+        { L, CY }, { CX, B },
+        { R, CY }, { CX, B },
+        // edges
+        { L, T }, { L, B },
+        { R, T }, { R, B },
+        { L, T }, { R, T },
+        { L, B }, { R, B },
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gFull); i += 2) {
+        bool valid = SkLineClipper::IntersectLine(&gFull[i], gR, dst);
+        if (!valid || memcmp(&gFull[i], dst, sizeof(dst))) {
+            SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+        }
+        REPORTER_ASSERT(reporter, valid && !memcmp(&gFull[i], dst, sizeof(dst)));
+    }
+    
+    static const SkPoint gPartial[] = {
+        { L - 10, CY }, { CX, CY }, { L, CY }, { CX, CY },
+        { CX, T - 10 }, { CX, CY }, { CX, T }, { CX, CY },
+        { R + 10, CY }, { CX, CY }, { R, CY }, { CX, CY },
+        { CX, B + 10 }, { CX, CY }, { CX, B }, { CX, CY },
+        // extended edges
+        { L, T - 10 }, { L, B + 10 }, { L, T }, { L, B },
+        { R, T - 10 }, { R, B + 10 }, { R, T }, { R, B },
+        { L - 10, T }, { R + 10, T }, { L, T }, { R, T },
+        { L - 10, B }, { R + 10, B }, { L, B }, { R, B },
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gPartial); i += 4) {
+        bool valid = SkLineClipper::IntersectLine(&gPartial[i], gR, dst);
+        if (!valid || memcmp(&gPartial[i+2], dst, sizeof(dst))) {
+            SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+        }
+        REPORTER_ASSERT(reporter, valid &&
+                                  !memcmp(&gPartial[i+2], dst, sizeof(dst)));
+    }
+    
+}
+
+void TestClipper(skiatest::Reporter* reporter) {
+    test_intersectline(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Clipper", TestClipperClass, TestClipper)
diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp
new file mode 100644
index 0000000..4e6c8b9
--- /dev/null
+++ b/tests/PaintTest.cpp
@@ -0,0 +1,43 @@
+#include "Test.h"
+#include "SkPath.h"
+#include "SkPaint.h"
+
+// found and fixed for webkit: mishandling when we hit recursion limit on
+// mostly degenerate cubic flatness test
+static void regression_cubic(skiatest::Reporter* reporter) {
+    SkPath path, stroke;
+    SkPaint paint;
+
+    path.moveTo(460.2881309415525, 303.250847066498);
+    path.cubicTo(463.36378422175284, 302.1169735073363,
+                 456.32239330810046, 304.720354932878,
+                 453.15255460013304, 305.788586869862);
+    
+    SkRect fillR, strokeR;
+    fillR = path.getBounds();
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(SkIntToScalar(2));
+    paint.getFillPath(path, &stroke);
+    strokeR = stroke.getBounds();
+
+    SkRect maxR = fillR;
+    SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter());
+    SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ?
+                            SkScalarMul(paint.getStrokeWidth(), miter) :
+                            paint.getStrokeWidth();
+    maxR.inset(-inset, -inset);
+
+    // test that our stroke didn't explode
+    REPORTER_ASSERT(reporter, maxR.contains(strokeR));
+}
+
+static void TestPaint(skiatest::Reporter* reporter) {
+    // TODO add general paint tests
+
+    // regression tests
+    regression_cubic(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Paint", TestPaintClass, TestPaint)
diff --git a/tests/ParsePathTest.cpp b/tests/ParsePathTest.cpp
new file mode 100644
index 0000000..917b0cb
--- /dev/null
+++ b/tests/ParsePathTest.cpp
@@ -0,0 +1,58 @@
+#include "Test.h"
+#include "SkParsePath.h"
+
+static void test_to_from(skiatest::Reporter* reporter, const SkPath& path) {
+    SkString str, str2;
+    SkParsePath::ToSVGString(path, &str);
+
+    SkPath path2;
+    bool success = SkParsePath::FromSVGString(str.c_str(), &path2);
+    REPORTER_ASSERT(reporter, success);
+
+    SkParsePath::ToSVGString(path2, &str2);
+    REPORTER_ASSERT(reporter, str == str2);
+#if 0 // closed paths are not equal, the iter explicitly gives the closing
+      // edge, even if it is not in the path.
+    REPORTER_ASSERT(reporter, path == path2);
+    if (path != path2) {
+        SkDebugf("str1=%s\nstr2=%s\n", str.c_str(), str2.c_str());
+    }
+#endif
+}
+
+static void TestParsePath(skiatest::Reporter* reporter) {
+    static const struct {
+        const char* fStr;
+        SkRect      fBounds;
+    } gRec[] = {
+        { "", { 0, 0, 0, 0 } },
+        { "M0,0L10,10", { 0, 0, SkIntToScalar(10), SkIntToScalar(10) } },
+        { "M-5.5,-0.5 Q 0 0 6,6.50",
+            { SkFloatToScalar(-5.5f), SkFloatToScalar(-0.5f),
+              SkFloatToScalar(6), SkFloatToScalar(6.5f) } }
+    };
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        SkPath  path;
+        bool success = SkParsePath::FromSVGString(gRec[i].fStr, &path);
+        REPORTER_ASSERT(reporter, success);
+        const SkRect& expectedBounds = gRec[i].fBounds;
+        const SkRect& pathBounds = path.getBounds();
+        REPORTER_ASSERT(reporter, expectedBounds == pathBounds);
+
+        test_to_from(reporter, path);
+    }
+    
+    SkRect r;
+    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5));
+    SkPath p;
+    p.addRect(r);
+    test_to_from(reporter, p);
+    p.addOval(r);
+    test_to_from(reporter, p);
+    p.addRoundRect(r, SkFloatToScalar(4), SkFloatToScalar(4.5));
+    test_to_from(reporter, p);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ParsePath", ParsePathClass, TestParsePath)
diff --git a/tests/TestSize.cpp b/tests/TestSize.cpp
new file mode 100644
index 0000000..6551509
--- /dev/null
+++ b/tests/TestSize.cpp
@@ -0,0 +1,60 @@
+#include "Test.h"
+#include "SkSize.h"
+
+static void TestISize(skiatest::Reporter* reporter) {
+    SkISize  a, b;
+    
+    a.set(0, 0);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.set(5, -5);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.clampNegToZero();
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    b.set(5, 0);
+    REPORTER_ASSERT(reporter, a == b);
+    
+    a.set(3, 5);
+    REPORTER_ASSERT(reporter, !a.isEmpty());
+    b = a;
+    REPORTER_ASSERT(reporter, !b.isEmpty());
+    REPORTER_ASSERT(reporter, a == b);
+    REPORTER_ASSERT(reporter, !(a != b));
+    REPORTER_ASSERT(reporter,
+                    a.fWidth == b.fWidth && a.fHeight == b.fHeight);
+}
+
+static void TestSize(skiatest::Reporter* reporter) {
+    TestISize(reporter);
+    
+    SkSize a, b;
+    int ix = 5;
+    int iy = 3;
+    SkScalar x = SkIntToScalar(ix);
+    SkScalar y = SkIntToScalar(iy);
+    
+    a.set(0, 0);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.set(x, -x);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.clampNegToZero();
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    b.set(x, 0);
+    REPORTER_ASSERT(reporter, a == b);
+    
+    a.set(y, x);
+    REPORTER_ASSERT(reporter, !a.isEmpty());
+    b = a;
+    REPORTER_ASSERT(reporter, !b.isEmpty());
+    REPORTER_ASSERT(reporter, a == b);
+    REPORTER_ASSERT(reporter, !(a != b));
+    REPORTER_ASSERT(reporter,
+                    a.fWidth == b.fWidth && a.fHeight == b.fHeight);
+    
+    SkISize ia;
+    ia.set(ix, iy);
+    a.set(x, y);
+    REPORTER_ASSERT(reporter, a.round() == ia);
+};
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Size", TestSizeClass, TestSize)
diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp
index 91b41ed..25248a3 100644
--- a/tests/skia_test.cpp
+++ b/tests/skia_test.cpp
@@ -95,19 +95,9 @@
     bool fAndroidMode;
 };
 
-class SkAutoGraphics {
-public:
-    SkAutoGraphics() {
-        SkGraphics::Init();
-    }
-    ~SkAutoGraphics() {
-        SkGraphics::Term();
-    }
-};
-
 int main (int argc, char * const argv[]) {
     SkAutoGraphics ag;
-
+    
     bool androidMode = false;
     for (int i = 1; i < argc; i++) {
         if (!strcmp(argv[i], "-android")) {