Snapshot from http://skia.googlecode.com/svn/trunk@7527

Change-Id: I83c7c2152f5d2c303b4655a5a604f513a54f350a
diff --git a/DEPS b/DEPS
index 5cd2370..408aab1 100644
--- a/DEPS
+++ b/DEPS
@@ -11,11 +11,10 @@
   "third_party/externals/angle" : "http://angleproject.googlecode.com/svn/trunk@1268",
   "third_party/externals/cityhash" : "http://cityhash.googlecode.com/svn/trunk@11",
   "third_party/externals/freetype" : "https://android.googlesource.com/platform/external/freetype.git",
-  "third_party/externals/gyp" : "http://gyp.googlecode.com/svn/trunk@1517",
+  "third_party/externals/gyp" : "http://gyp.googlecode.com/svn/trunk@1563",
   "third_party/externals/libjpeg" : "http://src.chromium.org/svn/trunk/src/third_party/libjpeg@125399",
-  "third_party/externals/jsoncpp" : "http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@125399",
-  "third_party/externals/jsoncpp/source/include" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/include@248",
-  "third_party/externals/jsoncpp/source/src/lib_json" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/src/lib_json@248",
+  "third_party/externals/jsoncpp" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp@248",
+  "third_party/externals/jsoncpp-chromium" : "http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@125399",
 }
 
 #hooks = [
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
new file mode 100644
index 0000000..ddac74a
--- /dev/null
+++ b/PRESUBMIT.py
@@ -0,0 +1,93 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""Top-level presubmit script for Skia.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into gcl.
+"""
+
+
+def _CheckChangeHasEol(input_api, output_api, source_file_filter=None):
+  """Checks that files end with atleast one \n (LF)."""
+  eof_files = []
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    # Check that the file ends in atleast one newline character.
+    if len(contents) > 1 and contents[-1:] != '\n':
+      eof_files.append(f.LocalPath())
+
+  if eof_files:
+    return [output_api.PresubmitPromptWarning(
+      'These files should end in a newline character:',
+      items=eof_files)]
+  return []
+
+
+def _CommonChecks(input_api, output_api):
+  """Presubmit checks common to upload and commit."""
+  results = []
+  sources = lambda x: (x.LocalPath().endswith('.h') or
+                       x.LocalPath().endswith('.gypi') or
+                       x.LocalPath().endswith('.gyp') or
+                       x.LocalPath().endswith('.py') or
+                       x.LocalPath().endswith('.sh') or
+                       x.LocalPath().endswith('.cpp'))
+  results.extend(
+      _CheckChangeHasEol(
+          input_api, output_api, source_file_filter=sources))
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  """Presubmit checks for the change on upload.
+
+  The following are the presubmit checks:
+  * Check change has one and only one EOL.
+  """
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
+
+
+def _CheckTreeStatus(input_api, output_api, json_url):
+  """Check whether to allow commit.
+
+  Args:
+    input_api: input related apis.
+    output_api: output related apis.
+    json_url: url to download json style status.
+  """
+  tree_status_results = input_api.canned_checks.CheckTreeIsOpen(
+      input_api, output_api, json_url=json_url)
+  if not tree_status_results:
+    # Check for caution state only if tree is not closed.
+    connection = input_api.urllib2.urlopen(json_url)
+    status = input_api.json.loads(connection.read())
+    connection.close()
+    if 'caution' in status['message'].lower():
+      short_text = 'Tree state is: ' + status['general_state']
+      long_text = status['message'] + '\n' + json_url
+      tree_status_results.append(
+          output_api.PresubmitPromptWarning(
+              message=short_text, long_text=long_text))
+  return tree_status_results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  """Presubmit checks for the change on commit.
+
+  The following are the presubmit checks:
+  * Check change has one and only one EOL.
+  * Ensures that the Skia tree is open in
+    http://skia-tree-status.appspot.com/. Shows a warning if it is in 'Caution'
+    state and an error if it is in 'Closed' state.
+  """
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  results.extend(
+      _CheckTreeStatus(input_api, output_api, json_url=(
+          'http://skia-tree-status.appspot.com/banner-status?format=json')))
+  return results
diff --git a/bench/BenchSysTimer_posix.h b/bench/BenchSysTimer_posix.h
index de793f3..8fd9706 100644
--- a/bench/BenchSysTimer_posix.h
+++ b/bench/BenchSysTimer_posix.h
@@ -23,4 +23,3 @@
 };
 
 #endif
-
diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp
index 5f06f88..0efdde3 100644
--- a/bench/BitmapBench.cpp
+++ b/bench/BitmapBench.cpp
@@ -282,4 +282,3 @@
 static BenchRegistry gReg14(Fact14);
 static BenchRegistry gReg15(Fact15);
 static BenchRegistry gReg16(Fact16);
-
diff --git a/bench/BitmapRectBench.cpp b/bench/BitmapRectBench.cpp
index 4391626..f9f46b8 100644
--- a/bench/BitmapRectBench.cpp
+++ b/bench/BitmapRectBench.cpp
@@ -41,14 +41,16 @@
 class BitmapRectBench : public SkBenchmark {
     SkBitmap    fBitmap;
     bool        fDoFilter;
+    bool        fSlightMatrix;
     uint8_t     fAlpha;
     SkString    fName;
     SkRect      fSrcR, fDstR;
     enum { N = SkBENCHLOOP(300) };
 public:
-    BitmapRectBench(void* param, U8CPU alpha, bool doFilter) : INHERITED(param) {
+    BitmapRectBench(void* param, U8CPU alpha, bool doFilter, bool slightMatrix) : INHERITED(param) {
         fAlpha = SkToU8(alpha);
         fDoFilter = doFilter;
+        fSlightMatrix = slightMatrix;
 
         const int w = 128;
         const int h = 128;
@@ -61,16 +63,26 @@
 
         fSrcR.iset(0, 0, w, h);
         fDstR.iset(0, 0, w, h);
+
+        if (slightMatrix) {
+            // want fractional translate
+            fDstR.offset(SK_Scalar1 / 3, SK_Scalar1 * 5 / 7);
+            // want enough to create a scale matrix, but not enough to scare
+            // off our sniffer which tries to see if the matrix is "effectively"
+            // translate-only.
+            fDstR.fRight += SK_Scalar1 / (w * 60);
+        }
     }
 
 protected:
     virtual const char* onGetName() {
-        fName.printf("bitmaprect_%02X_%sfilter", fAlpha, fDoFilter ? "" : "no");
+        fName.printf("bitmaprect_%02X_%sfilter_%s",
+                     fAlpha, fDoFilter ? "" : "no",
+                     fSlightMatrix ? "trans" : "identity");
         return fName.c_str();
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        SkIPoint dim = this->getSize();
         SkRandom rand;
 
         SkPaint paint;
@@ -87,8 +99,10 @@
     typedef SkBenchmark INHERITED;
 };
 
-DEF_BENCH(return new BitmapRectBench(p, 0xFF, false))
-DEF_BENCH(return new BitmapRectBench(p, 0x80, false))
-DEF_BENCH(return new BitmapRectBench(p, 0xFF, true))
-DEF_BENCH(return new BitmapRectBench(p, 0x80, true))
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, false, false))
+DEF_BENCH(return new BitmapRectBench(p, 0x80, false, false))
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, true, false))
+DEF_BENCH(return new BitmapRectBench(p, 0x80, true, false))
 
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, false, true))
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, true, true))
diff --git a/bench/BlurBench.cpp b/bench/BlurBench.cpp
index ab77d2b..5f2f425 100644
--- a/bench/BlurBench.cpp
+++ b/bench/BlurBench.cpp
@@ -103,4 +103,3 @@
 DEF_BENCH(return new BlurBench(p, REAL, SkBlurMaskFilter::kNormal_BlurStyle, SkBlurMaskFilter::kHighQuality_BlurFlag);)
 
 DEF_BENCH(return new BlurBench(p, 0, SkBlurMaskFilter::kNormal_BlurStyle);)
-
diff --git a/bench/BlurRectBench.cpp b/bench/BlurRectBench.cpp
new file mode 100644
index 0000000..b0b06e6
--- /dev/null
+++ b/bench/BlurRectBench.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkBlurMask.h"
+
+#define SMALL   SkIntToScalar(2)
+#define REAL    SkFloatToScalar(1.5f)
+#define BIG     SkIntToScalar(10)
+#define REALBIG SkFloatToScalar(30.5f)
+
+class BlurRectBench: public SkBenchmark {
+    SkScalar    fRadius;
+    SkString    fName;
+
+public:
+    BlurRectBench(void *param, SkScalar rad) : INHERITED(param) {
+        fRadius = rad;
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    SkScalar radius() const {
+        return fRadius;
+    }
+
+    void setName(const SkString& name) {
+        fName = name;
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        this->setupPaint(&paint);
+
+        paint.setAntiAlias(true);
+
+        SkScalar pad = fRadius*3/2 + SK_Scalar1;
+        SkRect r = SkRect::MakeWH(2 * pad + SK_Scalar1, 2 * pad + SK_Scalar1);
+
+        int loop_count;
+
+        if (fRadius > SkIntToScalar(25)) {
+          loop_count = 100;
+        } else if (fRadius > SkIntToScalar(5)) {
+          loop_count = 1000;
+        } else {
+          loop_count = 10000;
+        }
+
+        preBenchSetup(r);
+
+        for (int i = 0; i < SkBENCHLOOP(loop_count); i++) {
+            makeBlurryRect(r);
+        }
+    }
+
+    virtual void makeBlurryRect(const SkRect&) = 0;
+    virtual void preBenchSetup(const SkRect&) {}
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+
+class BlurRectDirectBench: public BlurRectBench {
+ public:
+    BlurRectDirectBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
+        SkString name;
+
+        if (SkScalarFraction(rad) != 0) {
+            name.printf("blurrect_direct_%.2f", SkScalarToFloat(rad));
+        } else {
+            name.printf("blurrect_direct_%d", SkScalarRound(rad));
+        }
+
+        setName(name);
+    }
+protected:
+    virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
+        SkMask mask;
+        SkBlurMask::BlurRect(&mask, r, radius(), SkBlurMask::kNormal_Style,
+                             SkBlurMask::kHigh_Quality);
+        SkMask::FreeImage(mask.fImage);
+    }
+};
+
+class BlurRectSeparableBench: public BlurRectBench {
+    SkMask fSrcMask;
+public:
+    BlurRectSeparableBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
+        SkString name;
+        if (SkScalarFraction(rad) != 0) {
+            name.printf("blurrect_separable_%.2f", SkScalarToFloat(rad));
+        } else {
+            name.printf("blurrect_separable_%d", SkScalarRound(rad));
+        }
+        setName(name);
+        fSrcMask.fImage = NULL;
+    }
+
+    ~BlurRectSeparableBench() {
+        SkMask::FreeImage(fSrcMask.fImage);
+    }
+
+protected:
+    virtual void preBenchSetup(const SkRect& r) SK_OVERRIDE {
+        SkMask::FreeImage(fSrcMask.fImage);
+
+        r.roundOut(&fSrcMask.fBounds);
+        fSrcMask.fFormat = SkMask::kA8_Format;
+        fSrcMask.fRowBytes = fSrcMask.fBounds.width();
+        fSrcMask.fImage = SkMask::AllocImage(fSrcMask.computeTotalImageSize());
+
+        memset(fSrcMask.fImage, 0xff, fSrcMask.computeTotalImageSize());
+    }
+
+    virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
+        SkMask mask;
+        SkBlurMask::BlurSeparable(&mask, fSrcMask, radius(),
+                                  SkBlurMask::kNormal_Style,
+                                  SkBlurMask::kHigh_Quality);
+        SkMask::FreeImage(mask.fImage);
+    }
+};
+
+DEF_BENCH(return new BlurRectSeparableBench(p, SMALL);)
+DEF_BENCH(return new BlurRectSeparableBench(p, BIG);)
+DEF_BENCH(return new BlurRectSeparableBench(p, REALBIG);)
+DEF_BENCH(return new BlurRectSeparableBench(p, REAL);)
+DEF_BENCH(return new BlurRectDirectBench(p, SMALL);)
+DEF_BENCH(return new BlurRectDirectBench(p, BIG);)
+DEF_BENCH(return new BlurRectDirectBench(p, REALBIG);)
+DEF_BENCH(return new BlurRectDirectBench(p, REAL);)
diff --git a/bench/ChecksumBench.cpp b/bench/ChecksumBench.cpp
index 818b9e3..4158d94 100644
--- a/bench/ChecksumBench.cpp
+++ b/bench/ChecksumBench.cpp
@@ -7,7 +7,20 @@
 #include "SkBenchmark.h"
 #include "SkCanvas.h"
 #include "SkChecksum.h"
+#include "SkCityHash.h"
+#include "SkMD5.h"
 #include "SkRandom.h"
+#include "SkSHA1.h"
+
+template<typename T> inline void sk_ignore_unused(const T&) { }
+
+enum ChecksumType {
+    kChecksum_ChecksumType,
+    kMD5_ChecksumType,
+    kSHA1_ChecksumType,
+    kCityHash32,
+    kCityHash64
+};
 
 class ComputeChecksumBench : public SkBenchmark {
     enum {
@@ -16,9 +29,10 @@
         N         = SkBENCHLOOP(100000),
     };
     uint32_t    fData[U32COUNT];
+    ChecksumType fType;
 
 public:
-    ComputeChecksumBench(void* param) : INHERITED(param) {
+    ComputeChecksumBench(void* param, ChecksumType type) : INHERITED(param), fType(type) {
         SkRandom rand;
         for (int i = 0; i < U32COUNT; ++i) {
             fData[i] = rand.nextU();
@@ -28,13 +42,56 @@
 
 protected:
     virtual const char* onGetName() {
-        return "compute_checksum";
+        switch (fType) {
+            case kChecksum_ChecksumType: return "compute_checksum";
+            case kMD5_ChecksumType: return "compute_md5";
+            case kSHA1_ChecksumType: return "compute_sha1";
+            case kCityHash32: return "compute_cityhash32";
+            case kCityHash64: return "compute_cityhash64";
+            default: SK_CRASH(); return "";
+        }
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        for (int i = 0; i < N; i++) {
-            volatile uint32_t result = SkChecksum::Compute(fData, sizeof(fData));
+        switch (fType) {
+            case kChecksum_ChecksumType: {
+                for (int i = 0; i < N; i++) {
+                    volatile uint32_t result = SkChecksum::Compute(fData, sizeof(fData));
+                    sk_ignore_unused(result);
+                }
+            } break;
+            case kMD5_ChecksumType: {
+                for (int i = 0; i < N; i++) {
+                    SkMD5 md5;
+                    md5.update(reinterpret_cast<uint8_t*>(fData), sizeof(fData));
+                    SkMD5::Digest digest;
+                    md5.finish(digest);
+                    sk_ignore_unused(digest);
+                }
+            } break;
+            case kSHA1_ChecksumType: {
+                for (int i = 0; i < N; i++) {
+                    SkSHA1 sha1;
+                    sha1.update(reinterpret_cast<uint8_t*>(fData), sizeof(fData));
+                    SkSHA1::Digest digest;
+                    sha1.finish(digest);
+                    sk_ignore_unused(digest);
+                }
+            } break;
+            case kCityHash32: {
+                for (int i = 0; i < N; i++) {
+                    volatile uint32_t result = SkCityHash::Compute32(reinterpret_cast<char*>(fData), sizeof(fData));
+                    sk_ignore_unused(result);
+                }
+            } break;
+            case kCityHash64: {
+                for (int i = 0; i < N; i++) {
+                    volatile uint64_t result = SkCityHash::Compute64(reinterpret_cast<char*>(fData), sizeof(fData));
+                    sk_ignore_unused(result);
+                }
+            } break;
         }
+
     }
 
 private:
@@ -43,6 +100,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static SkBenchmark* Fact0(void* p) { return new ComputeChecksumBench(p); }
+static SkBenchmark* Fact0(void* p) { return new ComputeChecksumBench(p, kChecksum_ChecksumType); }
+static SkBenchmark* Fact1(void* p) { return new ComputeChecksumBench(p, kMD5_ChecksumType); }
+static SkBenchmark* Fact2(void* p) { return new ComputeChecksumBench(p, kSHA1_ChecksumType); }
+static SkBenchmark* Fact3(void* p) { return new ComputeChecksumBench(p, kCityHash32); }
+static SkBenchmark* Fact4(void* p) { return new ComputeChecksumBench(p, kCityHash64); }
 
 static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
+static BenchRegistry gReg3(Fact3);
+static BenchRegistry gReg4(Fact4);
diff --git a/bench/ChromeBench.cpp b/bench/ChromeBench.cpp
index f045573..8021f92 100644
--- a/bench/ChromeBench.cpp
+++ b/bench/ChromeBench.cpp
@@ -491,11 +491,10 @@
     typedef SkBenchmark INHERITED;
 };
 
-static SkBenchmark* ScrollGmailFactory(void* p) {
+static inline SkBenchmark* ScrollGmailFactory(void* p) {
     return SkNEW_ARGS(ScrollGmailBench, (p));
 }
 
 // Disabled this benchmark: it takes 15x longer than any other benchmark
 // and is probably not giving us important information.
 //static BenchRegistry gScrollGmailReg(ScrollGmailFactory);
-
diff --git a/bench/DashBench.cpp b/bench/DashBench.cpp
index b324cf2..e62fb53 100644
--- a/bench/DashBench.cpp
+++ b/bench/DashBench.cpp
@@ -206,7 +206,7 @@
         for (int i = 0; i < N; ++i) {
             SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
 
-            fPE->filterPath(&dst, fPath, &rec);
+            fPE->filterPath(&dst, fPath, &rec, NULL);
             dst.rewind();
         }
     }
@@ -311,50 +311,114 @@
 private:
     typedef SkBenchmark INHERITED;
 };
+
+// Want to test how we handle dashing when 99% of the dash is clipped out
+class GiantDashBench : public SkBenchmark {
+    SkString fName;
+    SkScalar fStrokeWidth;
+    SkPoint  fPts[2];
+    SkAutoTUnref<SkPathEffect> fPathEffect;
+
+public:
+    enum LineType {
+        kHori_LineType,
+        kVert_LineType,
+        kDiag_LineType
+    };
+
+    static const char* LineTypeName(LineType lt) {
+        static const char* gNames[] = { "hori", "vert", "diag" };
+        SkASSERT((size_t)lt <= SK_ARRAY_COUNT(gNames));
+        return gNames[lt];
+    }
+
+    GiantDashBench(void* param, LineType lt, SkScalar width) : INHERITED(param) {
+        fName.printf("giantdashline_%s_%g", LineTypeName(lt), width);
+        fStrokeWidth = width;
+
+        // deliberately pick intervals that won't be caught by asPoints(), so
+        // we can test the filterPath code-path.
+        const SkScalar intervals[] = { 2, 1, 1, 1 };
+        fPathEffect.reset(new SkDashPathEffect(intervals,
+                                               SK_ARRAY_COUNT(intervals), 0));
+
+        SkScalar cx = 640 / 2;  // center X
+        SkScalar cy = 480 / 2;  // center Y
+        SkMatrix matrix;
+
+        switch (lt) {
+            case kHori_LineType:
+                matrix.setIdentity();
+                break;
+            case kVert_LineType:
+                matrix.setRotate(90, cx, cy);
+                break;
+            case kDiag_LineType:
+                matrix.setRotate(45, cx, cy);
+                break;
+        }
+
+        const SkScalar overshoot = 100*1000;
+        const SkPoint pts[2] = {
+            { -overshoot, cy }, { 640 + overshoot, cy }
+        };
+        matrix.mapPoints(fPts, pts, 2);
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint p;
+        this->setupPaint(&p);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(fStrokeWidth);
+        p.setPathEffect(fPathEffect);
+
+        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, p);
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 };
 
 #define PARAM(array)    array, SK_ARRAY_COUNT(array)
 
-static SkBenchmark* gF0(void* p) { return new DashBench(p, PARAM(gDots), 0); }
-static SkBenchmark* gF1(void* p) { return new DashBench(p, PARAM(gDots), 1); }
-static SkBenchmark* gF2(void* p) { return new DashBench(p, PARAM(gDots), 1, true); }
-static SkBenchmark* gF3(void* p) { return new DashBench(p, PARAM(gDots), 4); }
-static SkBenchmark* gF4(void* p) { return new MakeDashBench(p, make_poly, "poly"); }
-static SkBenchmark* gF5(void* p) { return new MakeDashBench(p, make_quad, "quad"); }
-static SkBenchmark* gF6(void* p) { return new MakeDashBench(p, make_cubic, "cubic"); }
-static SkBenchmark* gF700(void* p) { return new DashLineBench(p, 0, false); }
-static SkBenchmark* gF710(void* p) { return new DashLineBench(p, SK_Scalar1, false); }
-static SkBenchmark* gF720(void* p) { return new DashLineBench(p, 2 * SK_Scalar1, false); }
-static SkBenchmark* gF701(void* p) { return new DashLineBench(p, 0, true); }
-static SkBenchmark* gF711(void* p) { return new DashLineBench(p, SK_Scalar1, true); }
-static SkBenchmark* gF721(void* p) { return new DashLineBench(p, 2 * SK_Scalar1, true); }
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 0); )
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 1); )
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 1, true); )
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 4); )
+DEF_BENCH( return new MakeDashBench(p, make_poly, "poly"); )
+DEF_BENCH( return new MakeDashBench(p, make_quad, "quad"); )
+DEF_BENCH( return new MakeDashBench(p, make_cubic, "cubic"); )
+DEF_BENCH( return new DashLineBench(p, 0, false); )
+DEF_BENCH( return new DashLineBench(p, SK_Scalar1, false); )
+DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, false); )
+DEF_BENCH( return new DashLineBench(p, 0, true); )
+DEF_BENCH( return new DashLineBench(p, SK_Scalar1, true); )
+DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, true); )
 
-static SkBenchmark* gF8(void* p) { return new DrawPointsDashingBench(p, 1, 1, false); }
-static SkBenchmark* gF9(void* p) { return new DrawPointsDashingBench(p, 1, 1, true); }
-static SkBenchmark* gF10(void* p) { return new DrawPointsDashingBench(p, 3, 1, false); }
-static SkBenchmark* gF11(void* p) { return new DrawPointsDashingBench(p, 3, 1, true); }
-static SkBenchmark* gF12(void* p) { return new DrawPointsDashingBench(p, 5, 5, false); }
-static SkBenchmark* gF13(void* p) { return new DrawPointsDashingBench(p, 5, 5, true); }
+DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, false); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, true); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, false); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, true); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, false); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, true); )
 
-static BenchRegistry gR0(gF0);
-static BenchRegistry gR1(gF1);
-static BenchRegistry gR2(gF2);
-static BenchRegistry gR3(gF3);
-static BenchRegistry gR4(gF4);
-static BenchRegistry gR5(gF5);
-static BenchRegistry gR6(gF6);
-static BenchRegistry gR700(gF700);
-static BenchRegistry gR710(gF710);
-static BenchRegistry gR720(gF720);
-static BenchRegistry gR701(gF701);
-static BenchRegistry gR711(gF711);
-static BenchRegistry gR721(gF721);
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 0); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 0); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 0); )
 
-static BenchRegistry gR8(gF8);
-static BenchRegistry gR9(gF9);
-static BenchRegistry gR10(gF10);
-static BenchRegistry gR11(gF11);
-static BenchRegistry gR12(gF12);
-static BenchRegistry gR13(gF13);
+// pass 2 to explicitly avoid any 1-is-the-same-as-hairline special casing
+
+// hori_2 is just too slow to enable at the moment
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 2); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 2); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 2); )
diff --git a/bench/GradientBench.cpp b/bench/GradientBench.cpp
index d7ce74d..2f1b825 100644
--- a/bench/GradientBench.cpp
+++ b/bench/GradientBench.cpp
@@ -287,4 +287,3 @@
 static BenchRegistry gReg5(Fact5);
 
 static BenchRegistry gReg4(Fact4);
-
diff --git a/bench/InterpBench.cpp b/bench/InterpBench.cpp
index b689c72..086cc3b 100644
--- a/bench/InterpBench.cpp
+++ b/bench/InterpBench.cpp
@@ -164,4 +164,3 @@
 static BenchRegistry gReg2(M2);
 static BenchRegistry gReg3(M3);
 static BenchRegistry gReg4(M4);
-
diff --git a/bench/Matrix44Bench.cpp b/bench/Matrix44Bench.cpp
index 31525d2..0d9d572 100644
--- a/bench/Matrix44Bench.cpp
+++ b/bench/Matrix44Bench.cpp
@@ -48,9 +48,9 @@
 protected:
     virtual void performTest() {
         for (int i = 0; i < 10; ++i) {
-            fM0 == fM1;
-            fM1 == fM2;
-            fM2 == fM0;
+            (void) (fM0 == fM1);
+            (void) (fM1 == fM2);
+            (void) (fM2 == fM0);
         }
     }
 private:
@@ -169,4 +169,3 @@
 DEF_BENCH( return new InvertMatrix44Bench(p); )
 DEF_BENCH( return new SetConcatMatrix44Bench(p); )
 DEF_BENCH( return new GetTypeMatrix44Bench(p); )
-
diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp
index b4715ba..f739715 100644
--- a/bench/MatrixBench.cpp
+++ b/bench/MatrixBench.cpp
@@ -264,7 +264,6 @@
     typedef MatrixBench INHERITED;
 };
 
-#ifdef SK_SCALAR_IS_FLOAT
 class ScaleTransMixedMatrixBench : public MatrixBench {
  public:
     ScaleTransMixedMatrixBench(void* p) : INHERITED(p, "scaletrans_mixed"), fCount (16) {
@@ -341,7 +340,6 @@
     SkRandom fRandom;
     typedef MatrixBench INHERITED;
 };
-#endif
 
 class InvertMapRectMatrixBench : public MatrixBench {
 public:
@@ -398,69 +396,47 @@
     typedef MatrixBench INHERITED;
 };
 
+///////////////////////////////////////////////////////////////////////////////
 
+DEF_BENCH( return new EqualsMatrixBench(p); )
+DEF_BENCH( return new ScaleMatrixBench(p); )
+DEF_BENCH( return new FloatConcatMatrixBench(p); )
+DEF_BENCH( return new FloatDoubleConcatMatrixBench(p); )
+DEF_BENCH( return new DoubleConcatMatrixBench(p); )
+DEF_BENCH( return new GetTypeMatrixBench(p); )
+DEF_BENCH( return new InvertMapRectMatrixBench(p, "invert_maprect_identity", 0); )
 
+DEF_BENCH(return new InvertMapRectMatrixBench(p,
+                                  "invert_maprect_rectstaysrect",
+                                  InvertMapRectMatrixBench::kScale_Flag |
+                                  InvertMapRectMatrixBench::kTranslate_Flag); )
 
-static SkBenchmark* M0(void* p) { return new EqualsMatrixBench(p); }
-static SkBenchmark* M1(void* p) { return new ScaleMatrixBench(p); }
-static SkBenchmark* M2(void* p) { return new FloatConcatMatrixBench(p); }
-static SkBenchmark* M3(void* p) { return new FloatDoubleConcatMatrixBench(p); }
-static SkBenchmark* M4(void* p) { return new DoubleConcatMatrixBench(p); }
-static SkBenchmark* M5(void* p) { return new GetTypeMatrixBench(p); }
-static SkBenchmark* M6(void* p) {
-    return new InvertMapRectMatrixBench(p,
-        "invert_maprect_identity", 0);
-}
-static SkBenchmark* M7(void* p) {
-    return new InvertMapRectMatrixBench(p,
-        "invert_maprect_rectstaysrect",
-        InvertMapRectMatrixBench::kScale_Flag |
-        InvertMapRectMatrixBench::kTranslate_Flag);
-}
-static SkBenchmark* M8(void* p) {
-    return new InvertMapRectMatrixBench(p,
-        "invert_maprect_nonpersp",
-        InvertMapRectMatrixBench::kScale_Flag |
-        InvertMapRectMatrixBench::kRotate_Flag |
-        InvertMapRectMatrixBench::kTranslate_Flag);
-}
-static SkBenchmark* M9(void* p) {
-    return new InvertMapRectMatrixBench(p,
-        "invert_maprect_persp",
-        InvertMapRectMatrixBench::kPerspective_Flag);
-}
-static SkBenchmark* M10(void* p) {
-    return new InvertMapRectMatrixBench(p,
-        "invert_maprect_typemask_rectstaysrect",
-        InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
-        InvertMapRectMatrixBench::kScale_Flag |
-        InvertMapRectMatrixBench::kTranslate_Flag);
-}
-static SkBenchmark* M11(void* p) {
-    return new InvertMapRectMatrixBench(p,
-        "invert_maprect_typemask_nonpersp",
-        InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
-        InvertMapRectMatrixBench::kScale_Flag |
-        InvertMapRectMatrixBench::kRotate_Flag |
-        InvertMapRectMatrixBench::kTranslate_Flag);
-}
+DEF_BENCH(return new InvertMapRectMatrixBench(p,
+                                  "invert_maprect_translate",
+                                  InvertMapRectMatrixBench::kTranslate_Flag); )
 
-static BenchRegistry gReg0(M0);
-static BenchRegistry gReg1(M1);
-static BenchRegistry gReg2(M2);
-static BenchRegistry gReg3(M3);
-static BenchRegistry gReg4(M4);
-static BenchRegistry gReg5(M5);
-static BenchRegistry gReg6(M6);
-static BenchRegistry gReg7(M7);
-static BenchRegistry gReg8(M8);
-static BenchRegistry gReg9(M9);
-static BenchRegistry gReg10(M10);
-static BenchRegistry gReg11(M11);
+DEF_BENCH(return new InvertMapRectMatrixBench(p,
+                                  "invert_maprect_nonpersp",
+                                  InvertMapRectMatrixBench::kScale_Flag |
+                                  InvertMapRectMatrixBench::kRotate_Flag |
+                                  InvertMapRectMatrixBench::kTranslate_Flag); )
 
-#ifdef SK_SCALAR_IS_FLOAT
-static SkBenchmark* FlM0(void* p) { return new ScaleTransMixedMatrixBench(p); }
-static SkBenchmark* FlM1(void* p) { return new ScaleTransDoubleMatrixBench(p); }
-static BenchRegistry gFlReg5(FlM0);
-static BenchRegistry gFlReg6(FlM1);
-#endif
+DEF_BENCH( return new InvertMapRectMatrixBench(p,
+                               "invert_maprect_persp",
+                               InvertMapRectMatrixBench::kPerspective_Flag); )
+
+DEF_BENCH( return new InvertMapRectMatrixBench(p,
+                           "invert_maprect_typemask_rectstaysrect",
+                           InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
+                           InvertMapRectMatrixBench::kScale_Flag |
+                           InvertMapRectMatrixBench::kTranslate_Flag); )
+
+DEF_BENCH( return new InvertMapRectMatrixBench(p,
+                           "invert_maprect_typemask_nonpersp",
+                           InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
+                           InvertMapRectMatrixBench::kScale_Flag |
+                           InvertMapRectMatrixBench::kRotate_Flag |
+                           InvertMapRectMatrixBench::kTranslate_Flag); )
+
+DEF_BENCH( return new ScaleTransMixedMatrixBench(p); )
+DEF_BENCH( return new ScaleTransDoubleMatrixBench(p); )
diff --git a/bench/MemoryBench.cpp b/bench/MemoryBench.cpp
index 30b9ea3..ab9c07d 100644
--- a/bench/MemoryBench.cpp
+++ b/bench/MemoryBench.cpp
@@ -59,4 +59,3 @@
 
 static BenchRegistry gR0(F0);
 static BenchRegistry gR1(F1);
-
diff --git a/bench/MorphologyBench.cpp b/bench/MorphologyBench.cpp
index b6e9c92..f9657ba 100644
--- a/bench/MorphologyBench.cpp
+++ b/bench/MorphologyBench.cpp
@@ -114,4 +114,3 @@
 static BenchRegistry gRegNone(FactNone);
 
 #endif
-
diff --git a/bench/MutexBench.cpp b/bench/MutexBench.cpp
index dfdb792..6c152a0 100644
--- a/bench/MutexBench.cpp
+++ b/bench/MutexBench.cpp
@@ -40,4 +40,3 @@
 static SkBenchmark* Fact(void* p) { return new MutexBench(p); }
 
 static BenchRegistry gReg01(Fact);
-
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index 390c532..41a6922 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -122,7 +122,7 @@
         name->append("oval");
     }
     virtual void makePath(SkPath* path) SK_OVERRIDE {
-        SkRect r = { 10, 10, 20, 20 };
+        SkRect r = { 10, 10, 23, 20 };
         path->addOval(r);
     }
 private:
@@ -624,13 +624,14 @@
 class CirclesBench : public SkBenchmark {
 protected:
     SkString            fName;
+    Flags               fFlags;
 
     enum {
         N = SkBENCHLOOP(100)
     };
 public:
-    CirclesBench(void* param) : INHERITED(param) {
-        fName.printf("circles");
+    CirclesBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) {
+        fName.printf("circles_%s", fFlags & kStroke_Flag ? "stroke" : "fill");
     }
 
 protected:
@@ -643,6 +644,9 @@
 
         paint.setColor(SK_ColorBLACK);
         paint.setAntiAlias(true);
+        if (fFlags & kStroke_Flag) {
+            paint.setStyle(SkPaint::kStroke_Style);
+        }
 
         SkRandom rand;
 
@@ -655,6 +659,10 @@
             r.fRight =  r.fLeft + 2 * radius;
             r.fBottom = r.fTop + 2 * radius;
 
+            if (fFlags & kStroke_Flag) {
+                paint.setStrokeWidth(rand.nextUScalar1() * 5.0f);
+            }
+
             SkPath temp;
 
             // mimic how Chrome does circles
@@ -671,6 +679,7 @@
     typedef SkBenchmark INHERITED;
 };
 
+
 // Chrome creates its own round rects with each corner possibly being different.
 // In its "zero radius" incarnation it creates degenerate round rects.
 // Note: PathTest::test_arb_round_rect_is_convex and
@@ -736,7 +745,7 @@
         add_corner_arc(path, r, xCorner, yCorner, 180);
         path->close();
 
-#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
         SkASSERT(path->isConvex());
 #endif
     }
@@ -959,9 +968,12 @@
 static BenchRegistry gRegReverseAdd(FactReverseAdd);
 static BenchRegistry gRegReverseTo(FactReverseTo);
 
-static SkBenchmark* CirclesTest(void* p) { return new CirclesBench(p); }
+static SkBenchmark* CirclesTest(void* p) { return new CirclesBench(p, FLAGS00); }
 static BenchRegistry gRegCirclesTest(CirclesTest);
 
+static SkBenchmark* CirclesStrokeTest(void* p) { return new CirclesBench(p, FLAGS01); }
+static BenchRegistry gRegCirclesStrokeTest(CirclesStrokeTest);
+
 static SkBenchmark* ArbRoundRectTest(void* p) { return new ArbRoundRectBench(p, false); }
 static BenchRegistry gRegArbRoundRectTest(ArbRoundRectTest);
 
diff --git a/bench/PicturePlaybackBench.cpp b/bench/PicturePlaybackBench.cpp
index 7cdbccf..bd84baf 100644
--- a/bench/PicturePlaybackBench.cpp
+++ b/bench/PicturePlaybackBench.cpp
@@ -144,4 +144,3 @@
 static BenchRegistry gReg0(Fact0);
 static BenchRegistry gReg1(Fact1);
 static BenchRegistry gReg2(Fact2);
-
diff --git a/bench/RTreeBench.cpp b/bench/RTreeBench.cpp
index e1885c3..53dbe28 100644
--- a/bench/RTreeBench.cpp
+++ b/bench/RTreeBench.cpp
@@ -145,22 +145,22 @@
     typedef SkBenchmark INHERITED;
 };
 
-static SkIRect make_simple_rect(SkRandom&, int index, int numRects) {
+static inline SkIRect make_simple_rect(SkRandom&, int index, int numRects) {
     SkIRect out = {0, 0, GENERATE_EXTENTS, GENERATE_EXTENTS};
     return out;
 }
 
-static SkIRect make_concentric_rects_increasing(SkRandom&, int index, int numRects) {
+static inline SkIRect make_concentric_rects_increasing(SkRandom&, int index, int numRects) {
     SkIRect out = {0, 0, index + 1, index + 1};
     return out;
 }
 
-static SkIRect make_concentric_rects_decreasing(SkRandom&, int index, int numRects) {
+static inline SkIRect make_concentric_rects_decreasing(SkRandom&, int index, int numRects) {
     SkIRect out = {0, 0, numRects - index, numRects - index};
     return out;
 }
 
-static SkIRect make_point_rects(SkRandom& rand, int index, int numRects) {
+static inline SkIRect make_point_rects(SkRandom& rand, int index, int numRects) {
     SkIRect out;
     out.fLeft   = rand.nextU() % GENERATE_EXTENTS;
     out.fTop    = rand.nextU() % GENERATE_EXTENTS;
@@ -169,7 +169,7 @@
     return out;
 }
 
-static SkIRect make_random_rects(SkRandom& rand, int index, int numRects) {
+static inline SkIRect make_random_rects(SkRandom& rand, int index, int numRects) {
     SkIRect out;
     out.fLeft   = rand.nextS() % GENERATE_EXTENTS;
     out.fTop    = rand.nextS() % GENERATE_EXTENTS;
@@ -178,7 +178,7 @@
     return out;
 }
 
-static SkIRect make_large_rects(SkRandom& rand, int index, int numRects) {
+static inline SkIRect make_large_rects(SkRandom& rand, int index, int numRects) {
     SkIRect out;
     out.fLeft   = rand.nextU() % GENERATE_EXTENTS;
     out.fTop    = rand.nextU() % GENERATE_EXTENTS;
@@ -189,23 +189,23 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static SkBenchmark* Fact0(void* p) {
+static inline SkBenchmark* Fact0(void* p) {
     return SkNEW_ARGS(BBoxBuildBench, (p, "random", &make_random_rects, true,
                       SkRTree::Create(5, 16)));
 }
-static SkBenchmark* Fact1(void* p) {
+static inline SkBenchmark* Fact1(void* p) {
     return SkNEW_ARGS(BBoxBuildBench, (p, "random", &make_random_rects, false,
                       SkRTree::Create(5, 16)));
 }
-static SkBenchmark* Fact2(void* p) {
+static inline SkBenchmark* Fact2(void* p) {
     return SkNEW_ARGS(BBoxBuildBench, (p, "concentric",
                       &make_concentric_rects_increasing, true, SkRTree::Create(5, 16)));
 }
-static SkBenchmark* Fact3(void* p) {
+static inline SkBenchmark* Fact3(void* p) {
     return SkNEW_ARGS(BBoxQueryBench, (p, "random", &make_random_rects, true,
                       BBoxQueryBench::kRandom_QueryType, SkRTree::Create(5, 16)));
 }
-static SkBenchmark* Fact4(void* p) {
+static inline SkBenchmark* Fact4(void* p) {
     return SkNEW_ARGS(BBoxQueryBench, (p, "random", &make_random_rects, false,
                       BBoxQueryBench::kRandom_QueryType, SkRTree::Create(5, 16)));
 }
@@ -215,4 +215,3 @@
 static BenchRegistry gReg2(Fact2);
 static BenchRegistry gReg3(Fact3);
 static BenchRegistry gReg4(Fact4);
-
diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp
index 4a2d3c5..645f8a8 100644
--- a/bench/RectBench.cpp
+++ b/bench/RectBench.cpp
@@ -308,4 +308,3 @@
                      (p, SkCanvas::kPoints_PointMode,
                      BlitMaskBench::KMaskShader, "maskshader")
                      ); )
-
diff --git a/bench/RegionContainBench.cpp b/bench/RegionContainBench.cpp
new file mode 100644
index 0000000..1aae264
--- /dev/null
+++ b/bench/RegionContainBench.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkString.h"
+
+static bool sect_proc(SkRegion& a, SkRegion& b) {
+    SkRegion result;
+    return result.op(a, b, SkRegion::kIntersect_Op);
+}
+
+class RegionContainBench : public SkBenchmark {
+public:
+    typedef bool (*Proc)(SkRegion& a, SkRegion& b);
+    SkRegion fA, fB;
+    Proc     fProc;
+    SkString fName;
+
+    enum {
+        W = 200,
+        H = 200,
+        COUNT = 10,
+        N = SkBENCHLOOP(20000)
+    };
+
+    SkIRect randrect(SkRandom& rand, int i) {
+        int w = rand.nextU() % W;
+        return SkIRect::MakeXYWH(0, i*H/COUNT, w, H/COUNT);
+    }
+
+    RegionContainBench(void* param, Proc proc, const char name[]) : INHERITED(param) {
+        fProc = proc;
+        fName.printf("region_contains_%s", name);
+
+        SkRandom rand;
+        for (int i = 0; i < COUNT; i++) {
+            fA.op(randrect(rand, i), SkRegion::kXOR_Op);
+        }
+
+        fB.setRect(0, 0, H, W);
+
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() { return fName.c_str(); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        Proc proc = fProc;
+
+        for (int i = 0; i < N; ++i) {
+           proc(fA, fB);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* gF0(void* p) { return SkNEW_ARGS(RegionContainBench, (p, sect_proc, "sect")); }
+
+static BenchRegistry gR0(gF0);
diff --git a/bench/RepeatTileBench.cpp b/bench/RepeatTileBench.cpp
index fe39da4..5235696 100644
--- a/bench/RepeatTileBench.cpp
+++ b/bench/RepeatTileBench.cpp
@@ -140,4 +140,3 @@
 DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kRGB_565_Config))
 DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kARGB_4444_Config))
 DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kIndex8_Config))
-
diff --git a/bench/ShaderMaskBench.cpp b/bench/ShaderMaskBench.cpp
index ad27fcb..0e8e4bb 100644
--- a/bench/ShaderMaskBench.cpp
+++ b/bench/ShaderMaskBench.cpp
@@ -105,4 +105,3 @@
 static BenchRegistry gReg11(Fact11);
 static BenchRegistry gReg20(Fact20);
 static BenchRegistry gReg21(Fact21);
-
diff --git a/bench/SortBench.cpp b/bench/SortBench.cpp
new file mode 100644
index 0000000..8d6a716
--- /dev/null
+++ b/bench/SortBench.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkRandom.h"
+#include "SkTSort.h"
+#include "SkString.h"
+
+#ifdef SK_DEBUG
+// Windows-debug builds (at least) don't implement tail-recursion, and we have
+// a bench that triggers a worst-case behavior in SkTQSort (w/ repeated keys)
+// which can overflow the stack if N is too big. So we reduce it for debug
+// builds (for which we don't care about sorting performance anyways).
+static const int N = 100;
+#else
+static const int N = 1000;
+#endif
+
+static void rand_proc(int array[], int count) {
+    SkRandom rand;
+    for (int i = 0; i < count; ++i) {
+        array[i] = rand.nextS();
+    }
+}
+
+static void randN_proc(int array[], int count) {
+    SkRandom rand;
+    int mod = N / 10;
+    for (int i = 0; i < count; ++i) {
+        array[i] = rand.nextU() % mod;
+    }
+}
+
+static void forward_proc(int array[], int count) {
+    for (int i = 0; i < count; ++i) {
+        array[i] = i;
+    }
+}
+
+static void backward_proc(int array[], int count) {
+    for (int i = 0; i < count; ++i) {
+        array[i] = -i;
+    }
+}
+
+static void same_proc(int array[], int count) {
+    for (int i = 0; i < count; ++i) {
+        array[i] = count;
+    }
+}
+
+typedef void (*SortProc)(int array[], int count);
+
+enum Type {
+    kRand, kRandN, kFore, kBack, kSame
+};
+
+static const struct {
+    const char* fName;
+    SortProc    fProc;
+} gRec[] = {
+    { "rand", rand_proc },
+    { "rand10", randN_proc },
+    { "forward", forward_proc },
+    { "backward", backward_proc },
+    { "repeated", same_proc },
+};
+
+static void skqsort_sort(int array[], int count) {
+    SkTQSort<int>(array, array + count);
+}
+
+static void skheap_sort(int array[], int count) {
+    SkTHeapSort<int>(array, count);
+}
+
+extern "C" {
+    static int int_compare(const void* a, const void* b) {
+        const int ai = *(const int*)a;
+        const int bi = *(const int*)b;
+        return ai < bi ? -1 : (ai > bi);
+    }
+}
+
+static void qsort_sort(int array[], int count) {
+    qsort(array, count, sizeof(int), int_compare);
+}
+
+enum SortType {
+    kSKQSort, kSKHeap, kQSort
+};
+
+static const struct {
+    const char* fName;
+    SortProc    fProc;
+} gSorts[] = {
+    { "skqsort", skqsort_sort },
+    { "skheap", skheap_sort },
+    { "qsort", qsort_sort },
+};
+
+class SortBench : public SkBenchmark {
+    SkString    fName;
+    enum { MAX = 100000 };
+    int         fUnsorted[MAX];
+    int         fSorted[MAX];
+    int         fCount;
+    SortProc    fSortProc;
+
+public:
+    SortBench(void* param, Type t, int n, SortType s) : INHERITED(param) {
+        if (n > MAX) {
+            n = MAX;
+        }
+        fName.printf("sort_%s_%s", gSorts[s].fName, gRec[t].fName);
+        fCount = n;
+        gRec[t].fProc(fUnsorted, n);
+        fSortProc = gSorts[s].fProc;
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        int n = SkBENCHLOOP(200);
+        for (int i = 0; i < n; i++) {
+            memcpy(fSorted, fUnsorted, fCount * sizeof(int));
+            fSortProc(fSorted, fCount);
+#ifdef SK_DEBUG
+            for (int j = 1; j < fCount; ++j) {
+                SkASSERT(fSorted[j - 1] <= fSorted[j]);
+            }
+#endif
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* NewSkQSort(void* param, Type t) {
+    return new SortBench(param, t, N, kSKQSort);
+}
+static SkBenchmark* NewSkHeap(void* param, Type t) {
+    return new SortBench(param, t, N, kSKHeap);
+}
+static SkBenchmark* NewQSort(void* param, Type t) {
+    return new SortBench(param, t, N, kQSort);
+}
+
+DEF_BENCH( return NewSkQSort(p, kRand); )
+DEF_BENCH( return NewSkHeap(p, kRand); )
+DEF_BENCH( return NewQSort(p, kRand); )
+
+DEF_BENCH( return NewSkQSort(p, kRandN); )
+DEF_BENCH( return NewSkHeap(p, kRandN); )
+DEF_BENCH( return NewQSort(p, kRandN); )
+
+DEF_BENCH( return NewSkQSort(p, kFore); )
+DEF_BENCH( return NewSkHeap(p, kFore); )
+DEF_BENCH( return NewQSort(p, kFore); )
+
+DEF_BENCH( return NewSkQSort(p, kBack); )
+DEF_BENCH( return NewSkHeap(p, kBack); )
+DEF_BENCH( return NewQSort(p, kBack); )
+
+DEF_BENCH( return NewSkQSort(p, kSame); )
+DEF_BENCH( return NewSkHeap(p, kSame); )
+DEF_BENCH( return NewQSort(p, kSame); )
diff --git a/bench/TileBench.cpp b/bench/TileBench.cpp
new file mode 100644
index 0000000..7f2e7a8
--- /dev/null
+++ b/bench/TileBench.cpp
@@ -0,0 +1,136 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkString.h"
+
+static void create_gradient(SkBitmap* bm) {
+    SkASSERT(1 == bm->width());
+    const int height = bm->height();
+
+    float deltaB = 255.0f / height;
+    float blue = 255.0f;
+
+    SkAutoLockPixels lock(*bm);
+    for (int y = 0; y < height; y++) {
+        *bm->getAddr32(0, y) = SkColorSetRGB(0, 0, (U8CPU) blue);
+        blue -= deltaB;
+    }
+}
+
+// Test out the special case of a tiled 1xN texture. Test out opacity,
+// filtering and the different tiling modes
+class ConstXTileBench : public SkBenchmark {
+    SkPaint             fPaint;
+    SkString            fName;
+    bool                fDoFilter;
+    bool                fDoTrans;
+    bool                fDoScale;
+    enum { N = SkBENCHLOOP(20) };
+    static const int kWidth = 1;
+    static const int kHeight = 300;
+
+public:
+    ConstXTileBench(void* param,
+                    SkShader::TileMode xTile,
+                    SkShader::TileMode yTile,
+                    bool doFilter,
+                    bool doTrans,
+                    bool doScale)
+        : INHERITED(param)
+        , fDoFilter(doFilter)
+        , fDoTrans(doTrans)
+        , fDoScale(doScale) {
+        SkBitmap bm;
+
+        bm.setConfig(SkBitmap::kARGB_8888_Config, kWidth, kHeight);
+
+        bm.allocPixels();
+        bm.eraseColor(SK_ColorWHITE);
+        bm.setIsOpaque(true);
+
+        create_gradient(&bm);
+
+        SkShader* s = SkShader::CreateBitmapShader(bm, xTile, yTile);
+        fPaint.setShader(s)->unref();
+
+        fName.printf("constXTile_");
+
+        static const char* gTileModeStr[SkShader::kTileModeCount] = { "C", "R", "M" };
+        fName.append(gTileModeStr[xTile]);
+        fName.append(gTileModeStr[yTile]);
+
+        if (doFilter) {
+            fName.append("_filter");
+        }
+
+        if (doTrans) {
+            fName.append("_trans");
+        }
+
+        if (doScale) {
+            fName.append("_scale");
+        }
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint(fPaint);
+        this->setupPaint(&paint);
+        paint.setFilterBitmap(fDoFilter);
+        if (fDoTrans) {
+            paint.setColor(SkColorSetARGBMacro(0x80, 0xFF, 0xFF, 0xFF));
+        }
+
+        SkRect r;
+
+        if (fDoScale) {
+            r = SkRect::MakeWH(SkIntToScalar(2 * 640), SkIntToScalar(2 * 480));
+            canvas->scale(SK_ScalarHalf, SK_ScalarHalf);
+        } else {
+            r = SkRect::MakeWH(SkIntToScalar(640), SkIntToScalar(480));
+        }
+
+        SkPaint bgPaint;
+        bgPaint.setColor(SK_ColorWHITE);
+
+        for (int i = 0; i < N; i++) {
+            if (fDoTrans) {
+                canvas->drawRect(r, bgPaint);
+            }
+
+            canvas->drawRect(r, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, false, false, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, false, false, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, false, false, true))
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, true, false, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, true, false, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, true, false, false))
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, false, true, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, false, true, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, false, true, true))
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, true, true, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, true, true, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, true, true, false))
diff --git a/bench/bench_compare.py b/bench/bench_compare.py
index d853358..f4f7734 100644
--- a/bench/bench_compare.py
+++ b/bench/bench_compare.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 '''
 Created on May 16, 2011
 
@@ -9,7 +10,7 @@
 
 def usage():
     """Prints simple usage information."""
-    
+
     print '-o <file> the old bench output file.'
     print '-n <file> the new bench output file.'
     print '-h causes headers to be output.'
@@ -28,10 +29,12 @@
     print '  n: new time'
     print '  d: diff'
     print '  p: percent diff'
-    
+    print '-t use tab delimited format for output.'
+    print '--match <bench> only matches benches which begin with <bench>.'
+
 class BenchDiff:
     """A compare between data points produced by bench.
-    
+
     (BenchDataPoint, BenchDataPoint)"""
     def __init__(self, old, new):
         self.old = old
@@ -41,42 +44,23 @@
         if old.time != 0:
             diffp = self.diff / old.time
         self.diffp = diffp
-    
+
     def __repr__(self):
         return "BenchDiff(%s, %s)" % (
                    str(self.new),
                    str(self.old),
                )
-        
+
 def main():
     """Parses command line and writes output."""
-    
+
     try:
-        opts, _ = getopt.getopt(sys.argv[1:], "f:o:n:s:h")
+        opts, _ = getopt.getopt(sys.argv[1:], "f:o:n:s:ht", ['match='])
     except getopt.GetoptError, err:
         print str(err) 
         usage()
         sys.exit(2)
-    
-    column_formats = {
-        'b' : '{bench: >28} ',
-        'c' : '{config: <4} ',
-        't' : '{time_type: <4} ',
-        'o' : '{old_time: >10.2f} ',
-        'n' : '{new_time: >10.2f} ',
-        'd' : '{diff: >+10.2f} ',
-        'p' : '{diffp: >+8.1%} ',
-    }
-    header_formats = {
-        'b' : '{bench: >28} ',
-        'c' : '{config: <4} ',
-        't' : '{time_type: <4} ',
-        'o' : '{old_time: >10} ',
-        'n' : '{new_time: >10} ',
-        'd' : '{diff: >10} ',
-        'p' : '{diffp: >8} ',
-    }
-    
+
     old = None
     new = None
     column_format = ""
@@ -84,7 +68,9 @@
     columns = 'bctondp'
     header = False
     stat_type = "avg"
-    
+    use_tabs = False
+    match_bench = None;
+
     for option, value in opts:
         if option == "-o":
             old = value
@@ -96,14 +82,78 @@
             columns = value
         elif option == "-s":
             stat_type = value
+        elif option == "-t":
+            use_tabs = True
+        elif option == "--match":
+            match_bench = value
         else:
             usage()
             assert False, "unhandled option"
-    
+
     if old is None or new is None:
         usage()
         sys.exit(2)
-    
+
+    old_benches = bench_util.parse({}, open(old, 'r'), stat_type)
+    new_benches = bench_util.parse({}, open(new, 'r'), stat_type)
+
+    bench_diffs = []
+    for old_bench in old_benches:
+        #filter benches by the match criteria
+        if match_bench and not old_bench.bench.startswith(match_bench):
+            continue
+
+        #filter new_benches for benches that match old_bench
+        new_bench_match = [bench for bench in new_benches
+            if old_bench.bench == bench.bench and
+               old_bench.config == bench.config and
+               old_bench.time_type == bench.time_type
+        ]
+        if (len(new_bench_match) < 1):
+            continue
+        bench_diffs.append(BenchDiff(old_bench, new_bench_match[0]))
+
+    if use_tabs:
+        column_formats = {
+            'b' : '{bench}\t',
+            'c' : '{config}\t',
+            't' : '{time_type}\t',
+            'o' : '{old_time: 0.2f}\t',
+            'n' : '{new_time: 0.2f}\t',
+            'd' : '{diff: 0.2f}\t',
+            'p' : '{diffp: 0.1%}\t',
+        }
+        header_formats = {
+            'b' : '{bench}\t',
+            'c' : '{config}\t',
+            't' : '{time_type}\t',
+            'o' : '{old_time}\t',
+            'n' : '{new_time}\t',
+            'd' : '{diff}\t',
+            'p' : '{diffp}\t',
+        }
+    else:
+        bench_max_len = max(map(lambda b: len(b.old.bench), bench_diffs))
+        config_max_len = max(map(lambda b: len(b.old.config), bench_diffs))
+        column_formats = {
+            'b' : '{bench: >%d} ' % (bench_max_len),
+            'c' : '{config: <%d} ' % (config_max_len),
+            't' : '{time_type: <4} ',
+            'o' : '{old_time: >10.2f} ',
+            'n' : '{new_time: >10.2f} ',
+            'd' : '{diff: >+10.2f} ',
+            'p' : '{diffp: >+8.1%} ',
+        }
+        header_formats = {
+            'b' : '{bench: >%d} ' % (bench_max_len),
+            'c' : '{config: <%d} ' % (config_max_len),
+            't' : '{time_type: <4} ',
+            'o' : '{old_time: >10} ',
+            'n' : '{new_time: >10} ',
+            'd' : '{diff: >10} ',
+            'p' : '{diffp: >8} ',
+        }
+
     for column_char in columns:
         if column_formats[column_char]:
             column_format += column_formats[column_char]
@@ -111,7 +161,7 @@
         else:
             usage()
             sys.exit(2)
-    
+
     if header:
         print header_format.format(
             bench='bench'
@@ -122,22 +172,7 @@
             , diff='diff'
             , diffp='diffP'
         )
-    
-    old_benches = bench_util.parse({}, open(old, 'r'), stat_type)
-    new_benches = bench_util.parse({}, open(new, 'r'), stat_type)
-    
-    bench_diffs = []
-    for old_bench in old_benches:
-        #filter new_benches for benches that match old_bench
-        new_bench_match = [bench for bench in new_benches
-            if old_bench.bench == bench.bench and
-               old_bench.config == bench.config and
-               old_bench.time_type == bench.time_type
-        ]
-        if (len(new_bench_match) < 1):
-            continue
-        bench_diffs.append(BenchDiff(old_bench, new_bench_match[0]))
-    
+
     bench_diffs.sort(key=lambda d : [d.diffp,
                                      d.old.bench,
                                      d.old.config,
@@ -153,6 +188,6 @@
             , diff=bench_diff.diff
             , diffp=bench_diff.diffp
         )
-    
+
 if __name__ == "__main__":
     main()
diff --git a/bench/bench_util.py b/bench/bench_util.py
index 39efda4..6d6839d 100644
--- a/bench/bench_util.py
+++ b/bench/bench_util.py
@@ -118,9 +118,11 @@
     time_re = '(?:(\w*)msecs = )?\s*((?:\d+\.\d+)(?:,\d+\.\d+)*)'
     # non-per-tile benches have configs that don't end with ']'
     config_re = '(\S+[^\]]): ((?:' + time_re + '\s+)+)'
-    # per-tile bench lines are in the following format
-    tile_re = ('  tile_(\S+): tile \[\d+,\d+\] out of \[\d+,\d+\]: ((?:' +
-               time_re + '\s+)+)')
+    # per-tile bench lines are in the following format. Note that there are
+    # non-averaged bench numbers in separate lines, which we ignore now due to
+    # their inaccuracy.
+    tile_re = ('  tile_(\S+): tile \[\d+,\d+\] out of \[\d+,\d+\] <averaged>:'
+               ' ((?:' + time_re + '\s+)+)')
 
     for line in lines:
 
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 6f062e4..370530d 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -398,7 +398,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
     gPrintInstCount = true;
 #endif
     SkAutoGraphics ag;
@@ -775,7 +775,8 @@
                     canvas = new SkDeferredCanvas(device);
                     break;
                 case kRecord_benchModes:
-                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY);
+                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
                     canvas->ref();
                     break;
                 case kPictureRecord_benchModes: {
@@ -784,10 +785,12 @@
                     // pictureRecordFrom. As the benchmark, we will time how
                     // long it takes to playback pictureRecordFrom into
                     // pictureRecordTo.
-                    SkCanvas* tempCanvas = pictureRecordFrom.beginRecording(dim.fX, dim.fY);
+                    SkCanvas* tempCanvas = pictureRecordFrom.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
                     bench->draw(tempCanvas);
                     pictureRecordFrom.endRecording();
-                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY);
+                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
                     canvas->ref();
                     break;
                 }
@@ -845,7 +848,8 @@
                      || benchMode == kPictureRecord_benchModes)) {
                     // This will clear the recorded commands so that they do not
                     // acculmulate.
-                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY);
+                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
                 }
 
                 timer.start();
@@ -887,6 +891,7 @@
             if (outDir.size() > 0) {
                 saveFile(bench->getName(), configName, outDir.c_str(),
                          device->accessBitmap(false));
+                canvas->clear(SK_ColorWHITE);
             }
         }
         logger.logProgress(SkString("\n"));
@@ -913,4 +918,3 @@
     return tool_main(argc, (char**) argv);
 }
 #endif
-
diff --git a/debugger/QT/SkCanvasWidget.cpp b/debugger/QT/SkCanvasWidget.cpp
index 2f714d7..63a0e05 100644
--- a/debugger/QT/SkCanvasWidget.cpp
+++ b/debugger/QT/SkCanvasWidget.cpp
@@ -29,8 +29,7 @@
     fHorizontalLayout.addWidget(&fGLWidget);
 
     fPreviousPoint.set(0,0);
-    fUserOffset.set(0,0);
-    fUserScaleFactor = 1.0;
+    fUserMatrix.reset();
 
     setWidgetVisibility(kGPU_WidgetType, true);
     connect(&fRasterWidget, SIGNAL(drawComplete()),
@@ -48,9 +47,10 @@
 
 void SkCanvasWidget::mouseMoveEvent(QMouseEvent* event) {
     SkIPoint eventPoint = SkIPoint::Make(event->globalX(), event->globalY());
-    fUserOffset += eventPoint - fPreviousPoint;
+    SkIPoint eventOffset = eventPoint - fPreviousPoint;
     fPreviousPoint = eventPoint;
-    fDebugger->setUserOffset(fUserOffset);
+    fUserMatrix.postTranslate(eventOffset.fX, eventOffset.fY);
+    fDebugger->setUserMatrix(fUserMatrix);
     drawTo(fDebugger->index());
 }
 
@@ -61,15 +61,50 @@
 }
 
 void SkCanvasWidget::mouseDoubleClickEvent(QMouseEvent* event) {
-    resetWidgetTransform();
+    Qt::KeyboardModifiers modifiers = event->modifiers();
+    if (modifiers.testFlag(Qt::ControlModifier)) {
+        snapWidgetTransform();
+    } else {
+        resetWidgetTransform();
+    }
+}
+
+#define ZOOM_FACTOR (1.25f)
+
+void SkCanvasWidget::wheelEvent(QWheelEvent* event) {
+    Qt::KeyboardModifiers modifiers = event->modifiers();
+    if (modifiers.testFlag(Qt::ControlModifier)) {
+        zoom(event->delta() > 0 ? ZOOM_FACTOR : (1.0f / ZOOM_FACTOR), event->x(), event->y());
+    } else {
+        if (Qt::Horizontal == event->orientation()) {
+            fUserMatrix.postTranslate(event->delta(), 0.0f);
+        } else {
+            fUserMatrix.postTranslate(0.0f, event->delta());
+        }
+        fDebugger->setUserMatrix(fUserMatrix);
+        drawTo(fDebugger->index());
+    }
+}
+
+void SkCanvasWidget::zoom(int zoomCommand) {
+    zoom(kIn_ZoomCommand == zoomCommand ? ZOOM_FACTOR : (1.0f / ZOOM_FACTOR),
+         this->size().width() / 2, this->size().height() / 2);
+}
+
+void SkCanvasWidget::snapWidgetTransform() {
+    double x, y;
+    modf(fUserMatrix.getTranslateX(), &x);
+    modf(fUserMatrix.getTranslateY(), &y);
+    fUserMatrix[SkMatrix::kMTransX] = x;
+    fUserMatrix[SkMatrix::kMTransY] = y;
+    fDebugger->setUserMatrix(fUserMatrix);
+    drawTo(fDebugger->index());
 }
 
 void SkCanvasWidget::resetWidgetTransform() {
-    fUserOffset.set(0,0);
-    fUserScaleFactor = 1.0;
-    fDebugger->setUserOffset(fUserOffset);
-    fDebugger->setUserScale(fUserScaleFactor);
-    emit scaleFactorChanged(fUserScaleFactor);
+    fUserMatrix.reset();
+    fDebugger->setUserMatrix(fUserMatrix);
+    emit scaleFactorChanged(fUserMatrix.getScaleX());
     drawTo(fDebugger->index());
 }
 
@@ -81,17 +116,9 @@
     }
 }
 
-void SkCanvasWidget::zoom(float zoomIncrement) {
-    fUserScaleFactor += zoomIncrement;
-
-    /* The range of the fUserScaleFactor crosses over the range -1,0,1 frequently.
-    * Based on the code below, -1 and 1 both scale the image to it's original
-    * size we do the following to never have a registered wheel scroll
-    * not effect the fUserScaleFactor. */
-    if (fUserScaleFactor == 0) {
-        fUserScaleFactor = 2 * zoomIncrement;
-    }
-    emit scaleFactorChanged(fUserScaleFactor);
-    fDebugger->setUserScale(fUserScaleFactor);
+void SkCanvasWidget::zoom(float scale, int px, int py) {
+    fUserMatrix.postScale(scale, scale, px, py);
+    emit scaleFactorChanged(fUserMatrix.getScaleX());
+    fDebugger->setUserMatrix(fUserMatrix);
     drawTo(fDebugger->index());
 }
diff --git a/debugger/QT/SkCanvasWidget.h b/debugger/QT/SkCanvasWidget.h
index ab634f8..f6bc618 100644
--- a/debugger/QT/SkCanvasWidget.h
+++ b/debugger/QT/SkCanvasWidget.h
@@ -34,28 +34,34 @@
 
     void setWidgetVisibility(WidgetType type, bool isHidden);
 
-    void zoom(float zoomIncrement);
+    /** Zooms the canvas by scale with the transformation centered at the widget point (px, py). */
+    void zoom(float scale, int px, int py);
 
     void resetWidgetTransform();
 
+    enum ZoomCommandTypes {
+        kIn_ZoomCommand,
+        kOut_ZoomCommand,
+    };
+public slots:
+    /**
+     *  Zooms in or out (see ZoomCommandTypes) by the standard zoom factor
+     *  with the transformation centered in the middle of the widget.
+     */
+    void zoom(int zoomCommand);
+
 signals:
     void scaleFactorChanged(float newScaleFactor);
     void commandChanged(int newCommand);
     void hitChanged(int hit);
 
-private slots:
-    void keyZoom(int zoomIncrement) {
-        zoom(zoomIncrement);
-    }
-
 private:
     QHBoxLayout fHorizontalLayout;
     SkRasterWidget fRasterWidget;
     SkGLWidget fGLWidget;
     SkDebugger* fDebugger;
     SkIPoint fPreviousPoint;
-    SkIPoint fUserOffset;
-    float fUserScaleFactor;
+    SkMatrix fUserMatrix;
 
     void mouseMoveEvent(QMouseEvent* event);
 
@@ -63,9 +69,9 @@
 
     void mouseDoubleClickEvent(QMouseEvent* event);
 
-    void wheelEvent(QWheelEvent* event) {
-        zoom(event->delta()/120);
-    }
+    void wheelEvent(QWheelEvent* event);
+
+    void snapWidgetTransform();
 };
 
 
diff --git a/debugger/QT/SkDebuggerGUI.cpp b/debugger/QT/SkDebuggerGUI.cpp
index e445171..a64ec9b 100644
--- a/debugger/QT/SkDebuggerGUI.cpp
+++ b/debugger/QT/SkDebuggerGUI.cpp
@@ -66,6 +66,7 @@
     , fLoading(false)
 {
     setupUi(this);
+    fListWidget.setSelectionMode(QAbstractItemView::ExtendedSelection);
     connect(&fListWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(registerListClick(QListWidgetItem *)));
     connect(&fActionOpen, SIGNAL(triggered()), this, SLOT(openFile()));
     connect(&fActionDirectory, SIGNAL(triggered()), this, SLOT(toggleDirectory()));
@@ -98,12 +99,12 @@
     connect(&fActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs()));
     connect(&fActionSave, SIGNAL(triggered()), this, SLOT(actionSave()));
 
-    fMapper.setMapping(&fActionZoomIn, 1);
-    fMapper.setMapping(&fActionZoomOut, -1);
+    fMapper.setMapping(&fActionZoomIn, SkCanvasWidget::kIn_ZoomCommand);
+    fMapper.setMapping(&fActionZoomOut, SkCanvasWidget::kOut_ZoomCommand);
 
     connect(&fActionZoomIn, SIGNAL(triggered()), &fMapper, SLOT(map()));
     connect(&fActionZoomOut, SIGNAL(triggered()), &fMapper, SLOT(map()));
-    connect(&fMapper, SIGNAL(mapped(int)), &fCanvasWidget, SLOT(keyZoom(int)));
+    connect(&fMapper, SIGNAL(mapped(int)), &fCanvasWidget, SLOT(zoom(int)));
 
     fInspectorWidget.setDisabled(true);
     fMenuEdit.setDisabled(true);
@@ -144,10 +145,10 @@
                            const SkTDArray<size_t>& offsets,
                            const SkTDArray<bool>& deletedCommands)
         : INHERITED(stream, info, isValid, decoder)
-        , fTot(0.0)
-        , fCurCommand(0)
         , fOffsets(offsets)
-        , fSkipCommands(deletedCommands) {
+        , fSkipCommands(deletedCommands)
+        , fTot(0.0)
+        , fCurCommand(0) {
         fTimes.setCount(fOffsets.count());
         fTypeTimes.setCount(LAST_DRAWTYPE_ENUM+1);
         this->resetTimes();
@@ -326,7 +327,7 @@
 
     renderer->setup();
     renderer->render(NULL);
-    renderer->resetState();
+    renderer->resetState(true);
 
     // We throw this away the first batch of times to remove first time effects (such as paging in this program)
     pict->resetTimes();
@@ -334,7 +335,7 @@
     for (int i = 0; i < repeats; ++i) {
         renderer->setup();
         renderer->render(NULL);
-        renderer->resetState();
+        renderer->resetState(true);
     }
 
     renderer->end();
@@ -385,6 +386,13 @@
     sk_tools::SimplePictureRenderer* renderer = NULL;
 
     renderer = SkNEW(sk_tools::SimplePictureRenderer);
+
+#if SK_SUPPORT_GPU
+    if (Qt::Checked == fSettingsWidget.getGLCheckBox()->checkState()) {
+        renderer->setDeviceType(sk_tools::PictureRenderer::kGPU_DeviceType);
+    }
+#endif
+
 #endif
 
     static const int kNumRepeats = 10;
@@ -448,19 +456,27 @@
 }
 
 void SkDebuggerGUI::actionDelete() {
-    int currentRow = fListWidget.currentRow();
-    QListWidgetItem* item = fListWidget.currentItem();
 
-    if (fDebugger.isCommandVisible(currentRow)) {
-        item->setData(Qt::UserRole + 2, QPixmap(":/delete.png"));
-        fDebugger.setCommandVisible(currentRow, false);
-        fSkipCommands[currentRow] = true;
-    } else {
-        item->setData(Qt::UserRole + 2, QPixmap(":/blank.png"));
-        fDebugger.setCommandVisible(currentRow, true);
-        fSkipCommands[currentRow] = false;
+    for (int row = 0; row < fListWidget.count(); ++row) {
+        QListWidgetItem* item = fListWidget.item(row);
+
+        if (!item->isSelected()) {
+            continue;
+        }
+
+        if (fDebugger.isCommandVisible(row)) {
+            item->setData(Qt::UserRole + 2, QPixmap(":/delete.png"));
+            fDebugger.setCommandVisible(row, false);
+            fSkipCommands[row] = true;
+        } else {
+            item->setData(Qt::UserRole + 2, QPixmap(":/blank.png"));
+            fDebugger.setCommandVisible(row, true);
+            fSkipCommands[row] = false;
+        }
     }
 
+    int currentRow = fListWidget.currentRow();
+
     if (fPause) {
         fCanvasWidget.drawTo(fPausedRow);
         fImageWidget.draw();
@@ -505,9 +521,9 @@
 }
 
 void SkDebuggerGUI::actionSave() {
-    fFileName = fPath.toAscii();
+    fFileName = fPath.toAscii().data();
     fFileName.append("/");
-    fFileName.append(fDirectoryWidget.currentItem()->text().toAscii());
+    fFileName.append(fDirectoryWidget.currentItem()->text().toAscii().data());
     saveToFile(fFileName);
 }
 
@@ -555,14 +571,16 @@
 
 void SkDebuggerGUI::saveToFile(const SkString& filename) {
     SkFILEWStream file(filename.c_str());
-    fDebugger.makePicture()->serialize(&file);
+    SkAutoTUnref<SkPicture> copy(fDebugger.copyPicture());
+
+    copy->serialize(&file);
 }
 
 void SkDebuggerGUI::loadFile(QListWidgetItem *item) {
     if (fDirectoryWidgetActive) {
-        fFileName = fPath.toAscii();
+        fFileName = fPath.toAscii().data();
         fFileName.append("/");
-        fFileName.append(item->text().toAscii());
+        fFileName.append(item->text().toAscii().data());
         loadPicture(fFileName);
     }
 }
@@ -570,12 +588,15 @@
 void SkDebuggerGUI::openFile() {
     QString temp = QFileDialog::getOpenFileName(this, tr("Open File"), "",
             tr("Files (*.*)"));
+    openFile(temp);
+}
+
+void SkDebuggerGUI::openFile(const QString &filename) {
     fDirectoryWidgetActive = false;
-    if (!temp.isEmpty()) {
-        QFileInfo pathInfo(temp);
-        fPath = pathInfo.path();
-        loadPicture(SkString(temp.toAscii().data()));
-        setupDirectoryWidget();
+    if (!filename.isEmpty()) {
+        QFileInfo pathInfo(filename);
+        loadPicture(SkString(filename.toAscii().data()));
+        setupDirectoryWidget(pathInfo.path());
     }
     fDirectoryWidgetActive = true;
 }
@@ -827,9 +848,8 @@
 
     // TODO(chudy): Remove static call.
     fDirectoryWidgetActive = false;
-    fPath = "";
     fFileName = "";
-    setupDirectoryWidget();
+    setupDirectoryWidget("");
     fDirectoryWidgetActive = true;
 
     // Menu Bar
@@ -878,8 +898,9 @@
     QMetaObject::connectSlotsByName(SkDebuggerGUI);
 }
 
-void SkDebuggerGUI::setupDirectoryWidget() {
-    QDir dir(fPath);
+void SkDebuggerGUI::setupDirectoryWidget(const QString& path) {
+    fPath = path;
+    QDir dir(path);
     QRegExp r(".skp");
     fDirectoryWidget.clear();
     const QStringList files = dir.entryList();
diff --git a/debugger/QT/SkDebuggerGUI.h b/debugger/QT/SkDebuggerGUI.h
index 75d3f28..6d57a8a 100644
--- a/debugger/QT/SkDebuggerGUI.h
+++ b/debugger/QT/SkDebuggerGUI.h
@@ -57,6 +57,17 @@
 
     ~SkDebuggerGUI();
 
+    /**
+        Updates the directory widget with the latest directory path stored in
+        the global class variable fPath.
+     */
+    void setupDirectoryWidget(const QString& path);
+
+    /**
+        Loads the specified file.
+    */
+    void openFile(const QString& filename);
+
 signals:
     void commandChanged(int command);
 
@@ -169,7 +180,7 @@
 
     /**
         Toggles a dialog with a file browser for navigating to a skpicture. Loads
-        the seleced file.
+        the selected file.
      */
     void openFile();
 
@@ -308,12 +319,6 @@
     void setupOverviewText(const SkTDArray<double>* typeTimes, double totTime);
 
     /**
-        Updates the directory widget with the latest directory path stored in
-        the global class variable fPath.
-     */
-    void setupDirectoryWidget();
-
-    /**
         Render the supplied picture several times tracking the time consumed
         by each command.
      */
diff --git a/debugger/QT/SkSettingsWidget.cpp b/debugger/QT/SkSettingsWidget.cpp
index d9268be..c74be40 100644
--- a/debugger/QT/SkSettingsWidget.cpp
+++ b/debugger/QT/SkSettingsWidget.cpp
@@ -151,12 +151,6 @@
     return &fVisibleOn;
 }
 
-void SkSettingsWidget::setZoomText(int scaleFactor) {
-    if(scaleFactor == 1 || scaleFactor == -1) {
-        fZoomBox.setText("100%");
-    } else if (scaleFactor > 1) {
-        fZoomBox.setText(QString::number(scaleFactor*100).append("%"));
-    } else if (scaleFactor < -1) {
-        fZoomBox.setText(QString::number(100 / pow(2.0f, (-scaleFactor - 1))).append("%"));
-    }
+void SkSettingsWidget::setZoomText(float scale) {
+    fZoomBox.setText(QString::number(scale*100, 'f', 0).append("%"));
 }
diff --git a/debugger/QT/SkSettingsWidget.h b/debugger/QT/SkSettingsWidget.h
index fdb99d1..1f75527 100644
--- a/debugger/QT/SkSettingsWidget.h
+++ b/debugger/QT/SkSettingsWidget.h
@@ -34,7 +34,8 @@
      */
     SkSettingsWidget();
 
-    void setZoomText(int scaleFactor);
+    /** Sets the displayed user zoom level. A scale of 1.0 represents no zoom. */
+    void setZoomText(float scale);
 
     QRadioButton* getVisibilityButton();
 
diff --git a/debugger/QT/qrc_SkIcons.cpp b/debugger/QT/qrc_SkIcons.cpp
index 0f33e52..bf43dbe 100644
--- a/debugger/QT/qrc_SkIcons.cpp
+++ b/debugger/QT/qrc_SkIcons.cpp
@@ -1148,4 +1148,3 @@
 }
 
 Q_DESTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(qCleanupResources))
-
diff --git a/debugger/SkDebugCanvas.cpp b/debugger/SkDebugCanvas.cpp
index 03b57c3..27b58da 100644
--- a/debugger/SkDebugCanvas.cpp
+++ b/debugger/SkDebugCanvas.cpp
@@ -7,11 +7,20 @@
  */
 
 
-#include <iostream>
 #include "SkDebugCanvas.h"
 #include "SkDrawCommand.h"
 #include "SkDevice.h"
-#include "SkImageWidget.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    // iostream includes xlocale which generates warning 4530 because we're compiling without
+    // exceptions
+    #pragma warning(push)
+    #pragma warning(disable : 4530)
+#endif
+#include <iostream>
+#ifdef SK_BUILD_FOR_WIN
+    #pragma warning(pop)
+#endif
 
 static SkBitmap make_noconfig_bm(int width, int height) {
     SkBitmap bm;
@@ -29,37 +38,30 @@
     fBm.setConfig(SkBitmap::kNo_Config, fWidth, fHeight);
     fFilter = false;
     fIndex = 0;
-    fUserOffset.set(0,0);
-    fUserScale = 1.0;
+    fUserMatrix.reset();
 }
 
 SkDebugCanvas::~SkDebugCanvas() {
-    commandVector.deleteAll();
+    fCommandVector.deleteAll();
 }
 
 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
-    commandVector.push(command);
+    fCommandVector.push(command);
 }
 
 void SkDebugCanvas::draw(SkCanvas* canvas) {
-    if(!commandVector.isEmpty()) {
-        for (int i = 0; i < commandVector.count(); i++) {
-            if (commandVector[i]->isVisible()) {
-                commandVector[i]->execute(canvas);
+    if(!fCommandVector.isEmpty()) {
+        for (int i = 0; i < fCommandVector.count(); i++) {
+            if (fCommandVector[i]->isVisible()) {
+                fCommandVector[i]->execute(canvas);
             }
         }
     }
-    fIndex = commandVector.count() - 1;
+    fIndex = fCommandVector.count() - 1;
 }
 
 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
-    canvas->translate(SkIntToScalar(fUserOffset.fX),
-                      SkIntToScalar(fUserOffset.fY));
-    if (fUserScale < 0) {
-        canvas->scale((1.0f / -fUserScale), (1.0f / -fUserScale));
-    } else if (fUserScale > 0) {
-        canvas->scale(fUserScale, fUserScale);
-    }
+    canvas->concat(fUserMatrix);
 }
 
 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
@@ -74,8 +76,8 @@
     int layer = 0;
     SkColor prev = bitmap.getColor(0,0);
     for (int i = 0; i < index; i++) {
-        if (commandVector[i]->isVisible()) {
-            commandVector[i]->execute(&canvas);
+        if (fCommandVector[i]->isVisible()) {
+            fCommandVector[i]->execute(&canvas);
         }
         if (prev != bitmap.getColor(0,0)) {
             layer = i;
@@ -86,9 +88,8 @@
 }
 
 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
-    int counter = 0;
-    SkASSERT(!commandVector.isEmpty());
-    SkASSERT(index < commandVector.count());
+    SkASSERT(!fCommandVector.isEmpty());
+    SkASSERT(index < fCommandVector.count());
     int i;
 
     // This only works assuming the canvas and device are the same ones that
@@ -125,9 +126,9 @@
             canvas->restore();
         }
 
-        if (commandVector[i]->isVisible()) {
-            commandVector[i]->execute(canvas);
-            commandVector[i]->trackSaveState(&fOutstandingSaveCount);
+        if (fCommandVector[i]->isVisible()) {
+            fCommandVector[i]->execute(canvas);
+            fCommandVector[i]->trackSaveState(&fOutstandingSaveCount);
         }
     }
     fMatrix = canvas->getTotalMatrix();
@@ -136,30 +137,30 @@
 }
 
 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
-    SkASSERT(index < commandVector.count());
-    return commandVector[index];
+    SkASSERT(index < fCommandVector.count());
+    return fCommandVector[index];
 }
 
 SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) {
-    SkASSERT(index < commandVector.count());
-    return commandVector[index]->Info();
+    SkASSERT(index < fCommandVector.count());
+    return fCommandVector[index]->Info();
 }
 
 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
-    SkASSERT(index < commandVector.count());
-    return commandVector[index]->isVisible();
+    SkASSERT(index < fCommandVector.count());
+    return fCommandVector[index]->isVisible();
 }
 
 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
-    return commandVector;
+    return fCommandVector;
 }
 
 // TODO(chudy): Free command string memory.
 SkTArray<SkString>* SkDebugCanvas::getDrawCommandsAsStrings() const {
-    SkTArray<SkString>* commandString = new SkTArray<SkString>(commandVector.count());
-    if (!commandVector.isEmpty()) {
-        for (int i = 0; i < commandVector.count(); i ++) {
-            commandString->push_back() = commandVector[i]->toString();
+    SkTArray<SkString>* commandString = new SkTArray<SkString>(fCommandVector.count());
+    if (!fCommandVector.isEmpty()) {
+        for (int i = 0; i < fCommandVector.count(); i ++) {
+            commandString->push_back() = fCommandVector[i]->toString();
         }
     }
     return commandString;
@@ -176,8 +177,8 @@
 static SkBitmap createBitmap(const SkPath& path) {
     SkBitmap bitmap;
     bitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                     SkImageWidget::kImageWidgetWidth,
-                     SkImageWidget::kImageWidgetHeight);
+                     SkDebugCanvas::kVizImageWidth,
+                     SkDebugCanvas::kVizImageHeight);
     bitmap.allocPixels();
     bitmap.eraseColor(SK_ColorWHITE);
     SkDevice* device = new SkDevice(bitmap);
@@ -188,11 +189,11 @@
     const SkRect& bounds = path.getBounds();
 
     if (bounds.width() > bounds.height()) {
-        canvas.scale(SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetWidth)/bounds.width()),
-                     SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetHeight)/bounds.width()));
+        canvas.scale(SkDoubleToScalar((0.9*SkDebugCanvas::kVizImageWidth)/bounds.width()),
+                     SkDoubleToScalar((0.9*SkDebugCanvas::kVizImageHeight)/bounds.width()));
     } else {
-        canvas.scale(SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetWidth)/bounds.height()),
-                     SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetHeight)/bounds.height()));
+        canvas.scale(SkDoubleToScalar((0.9*SkDebugCanvas::kVizImageWidth)/bounds.height()),
+                     SkDoubleToScalar((0.9*SkDebugCanvas::kVizImageHeight)/bounds.height()));
     }
     canvas.translate(-bounds.fLeft+2, -bounds.fTop+2);
 
@@ -208,8 +209,8 @@
 static SkBitmap createBitmap(const SkBitmap& input, const SkRect* srcRect) {
     SkBitmap bitmap;
     bitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                     SkImageWidget::kImageWidgetWidth,
-                     SkImageWidget::kImageWidgetHeight);
+                     SkDebugCanvas::kVizImageWidth,
+                     SkDebugCanvas::kVizImageHeight);
     bitmap.allocPixels();
     bitmap.eraseColor(SK_ColorLTGRAY);
     SkDevice* device = new SkDevice(bitmap);
@@ -217,8 +218,8 @@
     SkCanvas canvas(device);
     device->unref();
 
-    SkScalar xScale = (SkImageWidget::kImageWidgetWidth-2.0) / input.width();
-    SkScalar yScale = (SkImageWidget::kImageWidgetHeight-2.0) / input.height();
+    SkScalar xScale = SkIntToScalar(SkDebugCanvas::kVizImageWidth-2) / input.width();
+    SkScalar yScale = SkIntToScalar(SkDebugCanvas::kVizImageHeight-2) / input.height();
 
     if (input.width() > input.height()) {
         yScale *= input.height() / (float) input.width();
@@ -258,6 +259,11 @@
     return true;
 }
 
+bool SkDebugCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    addDrawCommand(new ClipRRect(rrect, op, doAA));
+    return true;
+}
+
 bool SkDebugCanvas::clipRegion(const SkRegion& region, SkRegion::Op op) {
     addDrawCommand(new ClipRegion(region, op));
     return true;
@@ -296,6 +302,10 @@
     addDrawCommand(new DrawData(data, length));
 }
 
+void SkDebugCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
+    addDrawCommand(new DrawOval(oval, paint));
+}
+
 void SkDebugCanvas::drawPaint(const SkPaint& paint) {
     addDrawCommand(new DrawPaint(paint));
 }
@@ -329,6 +339,10 @@
     addDrawCommand(new DrawRectC(rect, paint));
 }
 
+void SkDebugCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    addDrawCommand(new DrawRRect(rrect, paint));
+}
+
 void SkDebugCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
         const SkPaint* paint = NULL) {
     SkBitmap resizedBitmap = createBitmap(bitmap, NULL);
@@ -393,6 +407,6 @@
 }
 
 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
-    SkASSERT(index < commandVector.count());
-    commandVector[index]->setVisible(toggle);
+    SkASSERT(index < fCommandVector.count());
+    fCommandVector[index]->setVisible(toggle);
 }
diff --git a/debugger/SkDebugCanvas.h b/debugger/SkDebugCanvas.h
index 2512702..238c5c0 100644
--- a/debugger/SkDebugCanvas.h
+++ b/debugger/SkDebugCanvas.h
@@ -96,7 +96,7 @@
         Returns length of draw command vector.
      */
     int getSize() {
-        return commandVector.count();
+        return fCommandVector.count();
     }
 
     /**
@@ -110,12 +110,8 @@
         fHeight = height;
     }
 
-    void setUserOffset(SkIPoint offset) {
-        fUserOffset = offset;
-    }
-
-    void setUserScale(float scale) {
-        fUserScale = scale;
+    void setUserMatrix(SkMatrix matrix) {
+        fUserMatrix = matrix;
     }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -128,6 +124,10 @@
 
     virtual bool clipRect(const SkRect&, SkRegion::Op, bool) SK_OVERRIDE;
 
+    virtual bool clipRRect(const SkRRect& rrect,
+                           SkRegion::Op op = SkRegion::kIntersect_Op,
+                           bool doAntiAlias = false) SK_OVERRIDE;
+
     virtual bool clipRegion(const SkRegion& region, SkRegion::Op op) SK_OVERRIDE;
 
     virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
@@ -146,6 +146,8 @@
 
     virtual void drawData(const void*, size_t) SK_OVERRIDE;
 
+    virtual void drawOval(const SkRect& oval, const SkPaint&) SK_OVERRIDE;
+
     virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
 
     virtual void drawPath(const SkPath& path, const SkPaint&) SK_OVERRIDE;
@@ -159,10 +161,13 @@
                              const SkPoint pos[], const SkPaint&) SK_OVERRIDE;
 
     virtual void drawPosTextH(const void* text, size_t byteLength,
-                      const SkScalar xpos[], SkScalar constY, const SkPaint&) SK_OVERRIDE;
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint&) SK_OVERRIDE;
 
     virtual void drawRect(const SkRect& rect, const SkPaint&) SK_OVERRIDE;
 
+    virtual void drawRRect(const SkRRect& rrect, const SkPaint& paint) SK_OVERRIDE;
+
     virtual void drawSprite(const SkBitmap&, int left, int top,
                             const SkPaint*) SK_OVERRIDE;
 
@@ -170,13 +175,13 @@
                           SkScalar y, const SkPaint&) SK_OVERRIDE;
 
     virtual void drawTextOnPath(const void* text, size_t byteLength,
-                            const SkPath& path, const SkMatrix* matrix,
+                                const SkPath& path, const SkMatrix* matrix,
                                 const SkPaint&) SK_OVERRIDE;
 
     virtual void drawVertices(VertexMode, int vertexCount,
-                          const SkPoint vertices[], const SkPoint texs[],
-                          const SkColor colors[], SkXfermode*,
-                          const uint16_t indices[], int indexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode*,
+                              const uint16_t indices[], int indexCount,
                               const SkPaint&) SK_OVERRIDE;
 
     virtual void restore() SK_OVERRIDE;
@@ -195,16 +200,17 @@
 
     virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE;
 
+    static const int kVizImageHeight = 256;
+    static const int kVizImageWidth = 256;
+
 private:
-    typedef SkCanvas INHERITED;
-    SkTDArray<SkDrawCommand*> commandVector;
+    SkTDArray<SkDrawCommand*> fCommandVector;
     int fHeight;
     int fWidth;
     SkBitmap fBm;
     bool fFilter;
     int fIndex;
-    SkIPoint fUserOffset;
-    float fUserScale;
+    SkMatrix fUserMatrix;
     SkMatrix fMatrix;
     SkIRect fClip;
 
@@ -227,6 +233,8 @@
         drawing anything else into the canvas.
      */
     void applyUserTransform(SkCanvas* canvas);
+
+    typedef SkCanvas INHERITED;
 };
 
 #endif
diff --git a/debugger/SkDebugger.cpp b/debugger/SkDebugger.cpp
index 7934c73..caa361e 100644
--- a/debugger/SkDebugger.cpp
+++ b/debugger/SkDebugger.cpp
@@ -34,11 +34,12 @@
     SkRefCnt_SafeAssign(fPicture, picture);
 }
 
-SkPicture* SkDebugger::makePicture() {
-    SkSafeUnref(fPicture);
-    fPicture = new SkPicture();
-    SkCanvas* canvas = fPicture->beginRecording(fPictureWidth, fPictureHeight);
+SkPicture* SkDebugger::copyPicture() {
+    // We can't just call clone here since we want to removed the "deleted"
+    // commands. Playing back will strip those out.
+    SkPicture* newPicture = new SkPicture;
+    SkCanvas* canvas = newPicture->beginRecording(fPictureWidth, fPictureHeight);
     fDebugCanvas->draw(canvas);
-    fPicture->endRecording();
-    return fPicture;
+    newPicture->endRecording();
+    return newPicture;
 }
diff --git a/debugger/SkDebugger.h b/debugger/SkDebugger.h
index a0a1682..6b85de3 100644
--- a/debugger/SkDebugger.h
+++ b/debugger/SkDebugger.h
@@ -60,19 +60,15 @@
 
     void loadPicture(SkPicture* picture);
 
-    SkPicture* makePicture();
+    SkPicture* copyPicture();
 
     int getSize() {
         return fDebugCanvas->getSize();
     }
 
-    void setUserOffset(SkIPoint userOffset) {
+    void setUserMatrix(SkMatrix userMatrix) {
         // Should this live in debugger instead?
-        fDebugCanvas->setUserOffset(userOffset);
-    }
-
-    void setUserScale(float userScale) {
-        fDebugCanvas->setUserScale(userScale);
+        fDebugCanvas->setUserMatrix(userMatrix);
     }
 
     int getCommandAtPoint(int x, int y, int index) {
diff --git a/debugger/SkDrawCommand.cpp b/debugger/SkDrawCommand.cpp
index 322be7b..b185595 100644
--- a/debugger/SkDrawCommand.cpp
+++ b/debugger/SkDrawCommand.cpp
@@ -27,12 +27,14 @@
         case CLIP_PATH: return "Clip Path";
         case CLIP_REGION: return "Clip Region";
         case CLIP_RECT: return "Clip Rect";
+        case CLIP_RRECT: return "Clip RRect";
         case CONCAT: return "Concat";
         case DRAW_BITMAP: return "Draw Bitmap";
         case DRAW_BITMAP_MATRIX: return "Draw Bitmap Matrix";
         case DRAW_BITMAP_NINE: return "Draw Bitmap Nine";
         case DRAW_BITMAP_RECT_TO_RECT: return "Draw Bitmap Rect";
         case DRAW_DATA: return "Draw Data";
+        case DRAW_OVAL: return "Draw Oval";
         case DRAW_PAINT: return "Draw Paint";
         case DRAW_PATH: return "Draw Path";
         case DRAW_PICTURE: return "Draw Picture";
@@ -40,6 +42,7 @@
         case DRAW_POS_TEXT: return "Draw Pos Text";
         case DRAW_POS_TEXT_H: return "Draw Pos Text H";
         case DRAW_RECT: return "Draw Rect";
+        case DRAW_RRECT: return "Draw RRect";
         case DRAW_SPRITE: return "Draw Sprite";
         case DRAW_TEXT: return "Draw Text";
         case DRAW_TEXT_ON_PATH: return "Draw Text On Path";
@@ -123,6 +126,21 @@
     canvas->clipRect(*this->fRect, this->fOp, this->fDoAA);
 }
 
+ClipRRect::ClipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    this->fRRect = rrect;
+    this->fOp = op;
+    this->fDoAA = doAA;
+    this->fDrawType = CLIP_RRECT;
+
+    this->fInfo.push(SkObjectParser::RRectToString(rrect));
+    this->fInfo.push(SkObjectParser::RegionOpToString(op));
+    this->fInfo.push(SkObjectParser::BoolToString(doAA));
+}
+
+void ClipRRect::execute(SkCanvas* canvas) {
+    canvas->clipRRect(this->fRRect, this->fOp, this->fDoAA);
+}
+
 Concat::Concat(const SkMatrix& matrix) {
     this->fMatrix = &matrix;
     this->fDrawType = CONCAT;
@@ -146,6 +164,9 @@
     this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
     this->fInfo.push(SkObjectParser::ScalarToString(left, "SkScalar left: "));
     this->fInfo.push(SkObjectParser::ScalarToString(top, "SkScalar top: "));
+    if (NULL != paint) {
+        this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    }
 }
 
 void DrawBitmap::execute(SkCanvas* canvas) {
@@ -166,7 +187,9 @@
 
     this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
     this->fInfo.push(SkObjectParser::MatrixToString(matrix));
-    if (paint) this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    if (NULL != paint) {
+        this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    }
 }
 
 void DrawBitmapMatrix::execute(SkCanvas* canvas) {
@@ -189,7 +212,9 @@
     this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
     this->fInfo.push(SkObjectParser::IRectToString(center));
     this->fInfo.push(SkObjectParser::RectToString(dst, "Dst: "));
-    if (paint) this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    if (NULL != paint) {
+        this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    }
 }
 
 void DrawBitmapNine::execute(SkCanvas* canvas) {
@@ -205,18 +230,27 @@
     this->fBitmap = &bitmap;
     this->fSrc = src;
     this->fDst = &dst;
-    this->fPaint = paint;
+    if (NULL != paint) {
+        this->fPaint = *paint;
+        this->fPaintPtr = &this->fPaint;
+    } else {
+        this->fPaintPtr = NULL;
+    }
     this->fDrawType = DRAW_BITMAP_RECT_TO_RECT;
     this->fResizedBitmap = resizedBitmap;
 
     this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
-    if (src) this->fInfo.push(SkObjectParser::RectToString(*src, "Src: "));
+    if (NULL != src) {
+        this->fInfo.push(SkObjectParser::RectToString(*src, "Src: "));
+    }
     this->fInfo.push(SkObjectParser::RectToString(dst, "Dst: "));
-    if (paint) this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    if (NULL != paint) {
+        this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    }
 }
 
 void DrawBitmapRect::execute(SkCanvas* canvas) {
-    canvas->drawBitmapRectToRect(*this->fBitmap, this->fSrc, *this->fDst, this->fPaint);
+    canvas->drawBitmapRectToRect(*this->fBitmap, this->fSrc, *this->fDst, this->fPaintPtr);
 }
 
 const SkBitmap* DrawBitmapRect::getBitmap() const {
@@ -234,6 +268,19 @@
     canvas->drawData(this->fData, this->fLength);
 }
 
+DrawOval::DrawOval(const SkRect& oval, const SkPaint& paint) {
+    this->fOval = &oval;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_OVAL;
+
+    this->fInfo.push(SkObjectParser::RectToString(oval));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawOval::execute(SkCanvas* canvas) {
+    canvas->drawOval(*this->fOval, *this->fPaint);
+}
+
 DrawPaint::DrawPaint(const SkPaint& paint) {
     this->fPaint = &paint;
     this->fDrawType = DRAW_PAINT;
@@ -282,9 +329,10 @@
     this->fDrawType = DRAW_POINTS;
 
     this->fInfo.push(SkObjectParser::PointsToString(pts, count));
-    this->fInfo.push(SkObjectParser::ScalarToString(SkIntToScalar(count),
+    this->fInfo.push(SkObjectParser::ScalarToString(SkIntToScalar((unsigned int)count),
                                                     "Points: "));
     this->fInfo.push(SkObjectParser::PointModeToString(mode));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
 }
 
 void DrawPoints::execute(SkCanvas* canvas) {
@@ -343,6 +391,19 @@
     canvas->drawRect(*this->fRect, *this->fPaint);
 }
 
+DrawRRect::DrawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    this->fRRect = rrect;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_RRECT;
+
+    this->fInfo.push(SkObjectParser::RRectToString(rrect));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawRRect::execute(SkCanvas* canvas) {
+    canvas->drawRRect(this->fRRect, *this->fPaint);
+}
+
 DrawSprite::DrawSprite(const SkBitmap& bitmap, int left, int top,
         const SkPaint* paint, SkBitmap& resizedBitmap) {
     this->fBitmap = &bitmap;
diff --git a/debugger/SkDrawCommand.h b/debugger/SkDrawCommand.h
index c6811f6..fba5a01 100644
--- a/debugger/SkDrawCommand.h
+++ b/debugger/SkDrawCommand.h
@@ -98,6 +98,16 @@
     bool fDoAA;
 };
 
+class ClipRRect : public SkDrawCommand {
+public:
+    ClipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkRRect fRRect;
+    SkRegion::Op fOp;
+    bool fDoAA;
+};
+
 class Concat : public SkDrawCommand {
 public:
     Concat(const SkMatrix& matrix);
@@ -153,9 +163,20 @@
             const SkRect& dst, const SkPaint* paint, SkBitmap& resizedBitmap);
     virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
     virtual const SkBitmap* getBitmap() const SK_OVERRIDE;
+
+    // The non-const 'paint' method allows modification of this object's
+    // SkPaint. For this reason the ctor and setPaint method make a local copy.
+    // The 'fPaintPtr' member acts a signal that the local SkPaint is valid
+    // (since only an SkPaint* is passed into the ctor).
+    const SkPaint* paint() const { return fPaintPtr; }
+    SkPaint* paint() { return fPaintPtr; }
+
+    void setPaint(const SkPaint& paint) { fPaint = paint; fPaintPtr = &fPaint; }
+
 private:
     const SkRect* fSrc;
-    const SkPaint* fPaint;
+    SkPaint fPaint;
+    SkPaint* fPaintPtr;
     const SkBitmap* fBitmap;
     const SkRect* fDst;
     SkBitmap fResizedBitmap;
@@ -170,6 +191,15 @@
     size_t fLength;
 };
 
+class DrawOval : public SkDrawCommand {
+public:
+    DrawOval(const SkRect& oval, const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkRect* fOval;
+    const SkPaint* fPaint;
+};
+
 class DrawPaint : public SkDrawCommand {
 public:
     DrawPaint(const SkPaint& paint);
@@ -273,6 +303,15 @@
     const SkPaint* fPaint;
 };
 
+class DrawRRect : public SkDrawCommand {
+public:
+    DrawRRect(const SkRRect& rrect, const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkRRect fRRect;
+    const SkPaint* fPaint;
+};
+
 class DrawSprite : public SkDrawCommand {
 public:
     DrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint,
@@ -329,6 +368,9 @@
             SkCanvas::SaveFlags flags);
     virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
     virtual void trackSaveState(int* state) SK_OVERRIDE;
+
+    const SkPaint* paint() const { return fPaint; }
+
 private:
     const SkRect* fBounds;
     const SkPaint* fPaint;
diff --git a/debugger/SkObjectParser.cpp b/debugger/SkObjectParser.cpp
index 315e81c..c53d3b5 100644
--- a/debugger/SkObjectParser.cpp
+++ b/debugger/SkObjectParser.cpp
@@ -7,6 +7,13 @@
  */
 
 #include "SkObjectParser.h"
+#include "SkData.h"
+#include "SkFontDescriptor.h"
+#include "SkRRect.h"
+#include "SkShader.h"
+#include "SkStream.h"
+#include "SkStringUtils.h"
+#include "SkTypeface.h"
 
 /* TODO(chudy): Replace all std::strings with char */
 
@@ -96,10 +103,165 @@
 }
 
 SkString* SkObjectParser::PaintToString(const SkPaint& paint) {
-    SkColor color = paint.getColor();
-    SkString* mPaint = new SkString("SkPaint: Color: 0x");
-    mPaint->appendHex(color);
+    SkString* mPaint = new SkString("<dl><dt>SkPaint:</dt><dd><dl>");
 
+    SkTypeface* typeface = paint.getTypeface();
+    if (NULL != typeface) {
+        SkDynamicMemoryWStream ostream;
+        typeface->serialize(&ostream);
+        SkAutoTUnref<SkData> data(ostream.copyToData());
+
+        SkMemoryStream stream(data);
+        SkFontDescriptor descriptor(&stream);
+
+        mPaint->append("<dt>Font Family Name:</dt><dd>");
+        mPaint->append(descriptor.getFamilyName());
+        mPaint->append("</dd><dt>Font Full Name:</dt><dd>");
+        mPaint->append(descriptor.getFullName());
+        mPaint->append("</dd><dt>Font PS Name:</dt><dd>");
+        mPaint->append(descriptor.getPostscriptName());
+        mPaint->append("</dd><dt>Font File Name:</dt><dd>");
+        mPaint->append(descriptor.getFontFileName());
+        mPaint->append("</dd>");
+    }
+
+    mPaint->append("<dt>TextSize:</dt><dd>");
+    mPaint->appendScalar(paint.getTextSize());
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>TextScaleX:</dt><dd>");
+    mPaint->appendScalar(paint.getTextScaleX());
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>TextSkewX:</dt><dd>");
+    mPaint->appendScalar(paint.getTextSkewX());
+    mPaint->append("</dd>");
+
+    SkPathEffect* pathEffect = paint.getPathEffect();
+    if (NULL != pathEffect) {
+        mPaint->append("<dt>PathEffect:</dt><dd>");
+        mPaint->append("</dd>");
+    }
+
+    SkShader* shader = paint.getShader();
+    if (NULL != shader) {
+        mPaint->append("<dt>Shader:</dt><dd>");
+        SkDEVCODE(shader->toString(mPaint);)
+        mPaint->append("</dd>");
+    }
+
+    SkXfermode* xfer = paint.getXfermode();
+    if (NULL != xfer) {
+        mPaint->append("<dt>Xfermode:</dt><dd>");
+        SkDEVCODE(xfer->toString(mPaint);)
+        mPaint->append("</dd>");
+    }
+
+    SkMaskFilter* maskFilter = paint.getMaskFilter();
+    if (NULL != maskFilter) {
+        mPaint->append("<dt>MaskFilter:</dt><dd>");
+        mPaint->append("</dd>");
+    }
+
+    SkColorFilter* colorFilter = paint.getColorFilter();
+    if (NULL != colorFilter) {
+        mPaint->append("<dt>ColorFilter:</dt><dd>");
+        mPaint->append("</dd>");
+    }
+
+    SkRasterizer* rasterizer = paint.getRasterizer();
+    if (NULL != rasterizer) {
+        mPaint->append("<dt>Rasterizer:</dt><dd>");
+        mPaint->append("</dd>");
+    }
+
+    SkDrawLooper* looper = paint.getLooper();
+    if (NULL != looper) {
+        mPaint->append("<dt>DrawLooper:</dt><dd>");
+        SkDEVCODE(looper->toString(mPaint);)
+        mPaint->append("</dd>");
+    }
+
+    SkImageFilter* imageFilter = paint.getImageFilter();
+    if (NULL != imageFilter) {
+        mPaint->append("<dt>ImageFilter:</dt><dd>");
+        mPaint->append("</dd>");
+    }
+
+    SkAnnotation* annotation = paint.getAnnotation();
+    if (NULL != annotation) {
+        mPaint->append("<dt>Annotation:</dt><dd>");
+        mPaint->append("</dd>");
+    }
+
+    mPaint->append("<dt>Color:</dt><dd>0x");
+    SkColor color = paint.getColor();
+    mPaint->appendHex(color);
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>Stroke Width:</dt><dd>");
+    mPaint->appendScalar(paint.getStrokeWidth());
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>Stroke Miter:</dt><dd>");
+    mPaint->appendScalar(paint.getStrokeMiter());
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>Flags:</dt><dd>(");
+    if (paint.getFlags()) {
+        bool needSeparator = false;
+        SkAddFlagToString(mPaint, paint.isAntiAlias(), "AntiAlias", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isFilterBitmap(), "FilterBitmap", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isDither(), "Dither", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isUnderlineText(), "UnderlineText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isStrikeThruText(), "StrikeThruText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isFakeBoldText(), "FakeBoldText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isLinearText(), "LinearText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isSubpixelText(), "SubpixelText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isDevKernText(), "DevKernText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isLCDRenderText(), "LCDRenderText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isEmbeddedBitmapText(),
+                          "EmbeddedBitmapText", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isAutohinted(), "Autohinted", &needSeparator);
+        SkAddFlagToString(mPaint, paint.isVerticalText(), "VerticalText", &needSeparator);
+        SkAddFlagToString(mPaint, SkToBool(paint.getFlags() & SkPaint::kGenA8FromLCD_Flag),
+                          "GenA8FromLCD", &needSeparator);
+    } else {
+        mPaint->append("None");
+    }
+    mPaint->append(")</dd>");
+
+    mPaint->append("<dt>TextAlign:</dt><dd>");
+    static const char* gTextAlignStrings[SkPaint::kAlignCount] = { "Left", "Center", "Right" };
+    mPaint->append(gTextAlignStrings[paint.getTextAlign()]);
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>CapType:</dt><dd>");
+    static const char* gStrokeCapStrings[SkPaint::kCapCount] = { "Butt", "Round", "Square" };
+    mPaint->append(gStrokeCapStrings[paint.getStrokeCap()]);
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>JoinType:</dt><dd>");
+    static const char* gJoinStrings[SkPaint::kJoinCount] = { "Miter", "Round", "Bevel" };
+    mPaint->append(gJoinStrings[paint.getStrokeJoin()]);
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>Style:</dt><dd>");
+    static const char* gStyleStrings[SkPaint::kStyleCount] = { "Fill", "Stroke", "StrokeAndFill" };
+    mPaint->append(gStyleStrings[paint.getStyle()]);
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>TextEncoding:</dt><dd>");
+    static const char* gTextEncodingStrings[] = { "UTF8", "UTF16", "UTF32", "GlyphID" };
+    mPaint->append(gTextEncodingStrings[paint.getTextEncoding()]);
+    mPaint->append("</dd>");
+
+    mPaint->append("<dt>Hinting:</dt><dd>");
+    static const char* gHintingStrings[] = { "None", "Slight", "Normal", "Full" };
+    mPaint->append(gHintingStrings[paint.getHinting()]);
+    mPaint->append("</dd>");
+
+    mPaint->append("</dd></dl></dl>");
     return mPaint;
 }
 
@@ -214,6 +376,50 @@
     return mRect;
 }
 
+SkString* SkObjectParser::RRectToString(const SkRRect& rrect, const char* title) {
+
+    SkString* mRRect = new SkString;
+
+    if (NULL == title) {
+        mRRect->append("SkRRect (");
+        if (rrect.isEmpty()) {
+            mRRect->append("empty");
+        } else if (rrect.isRect()) {
+            mRRect->append("rect");
+        } else if (rrect.isOval()) {
+            mRRect->append("oval");
+        } else if (rrect.isSimple()) {
+            mRRect->append("simple");
+        } else {
+            SkASSERT(rrect.isComplex());
+            mRRect->append("complex");
+        }
+        mRRect->append("): ");
+    } else {
+        mRRect->append(title);
+    }
+    mRRect->append("(");
+    mRRect->appendScalar(rrect.rect().left());
+    mRRect->append(", ");
+    mRRect->appendScalar(rrect.rect().top());
+    mRRect->append(", ");
+    mRRect->appendScalar(rrect.rect().right());
+    mRRect->append(", ");
+    mRRect->appendScalar(rrect.rect().bottom());
+    mRRect->append(") radii: (");
+    for (int i = 0; i < 4; ++i) {
+        const SkVector& radii = rrect.radii((SkRRect::Corner) i);
+        mRRect->appendScalar(radii.fX);
+        mRRect->append(", ");
+        mRRect->appendScalar(radii.fY);
+        if (i < 3) {
+            mRRect->append(", ");
+        }
+    }
+    mRRect->append(")");
+    return mRRect;
+}
+
 SkString* SkObjectParser::RegionOpToString(SkRegion::Op op) {
     SkString* mOp = new SkString("SkRegion::Op: ");
     if (op == SkRegion::kDifference_Op) {
diff --git a/debugger/SkObjectParser.h b/debugger/SkObjectParser.h
index e30bd13..ff22069 100644
--- a/debugger/SkObjectParser.h
+++ b/debugger/SkObjectParser.h
@@ -86,6 +86,12 @@
     static SkString* RectToString(const SkRect& rect, const char* title = NULL);
 
     /**
+        Returns a string representation of an SkRRect.
+        @param rrect  SkRRect
+     */
+    static SkString* RRectToString(const SkRRect& rrect, const char* title = NULL);
+
+    /**
         Returns a string representation of the SkRegion enum.
         @param op  SkRegion::op enum
      */
diff --git a/debugger/debuggermain.cpp b/debugger/debuggermain.cpp
index 86a4574..f749911 100644
--- a/debugger/debuggermain.cpp
+++ b/debugger/debuggermain.cpp
@@ -9,9 +9,51 @@
 #include "SkDebuggerGUI.h"
 #include <QApplication>
 
+static void usage(const char * argv0) {
+    SkDebugf("%s <input> \n", argv0);
+    SkDebugf("    [--help|-h]: show this help message\n");
+    SkDebugf("\n\n");
+    SkDebugf("     input:     Either a directory or a single .skp file.\n");
+}
+
 int main(int argc, char *argv[]) {
     QApplication a(argc, argv);
+
+    QStringList argList = a.arguments();
+
+    if (argList.count() <= 0) {
+        return -1;  // should at least have command name
+    }
+
+    SkString input;
+
+    QStringList::const_iterator iter = argList.begin();
+
+    SkString commandName(iter->toAscii().data());
+    ++iter; // skip the command name
+
+    for ( ; iter != argList.end(); ++iter) {
+        if (0 == iter->compare("--help") || 0 == iter->compare("-h")) {
+            usage(commandName.c_str());
+            return -1;
+        } else if (input.isEmpty()) {
+            input = SkString(iter->toAscii().data());
+        } else {
+            usage(commandName.c_str());
+            return -1;
+        }
+    }
+
     SkDebuggerGUI w;
+
+    if (!input.isEmpty()) {
+        if (SkStrEndsWith(input.c_str(), ".skp")) {
+            w.openFile(input.c_str());
+        } else {
+            w.setupDirectoryWidget(input.c_str());
+        }
+    }
+
     w.show();
     return a.exec();
 }
diff --git a/experimental/AndroidPathRenderer/AndroidPathRenderer.cpp b/experimental/AndroidPathRenderer/AndroidPathRenderer.cpp
index 5d927e1..6c32248 100644
--- a/experimental/AndroidPathRenderer/AndroidPathRenderer.cpp
+++ b/experimental/AndroidPathRenderer/AndroidPathRenderer.cpp
@@ -12,7 +12,7 @@
 #define VERTEX_DEBUG 0
 
 #include <SkPath.h>
-#include <SkPaint.h>
+#include <SkStrokeRec.h>
 
 #include <stdlib.h>
 #include <stdint.h>
@@ -46,14 +46,14 @@
     return bounds;
 }
 
-void computeInverseScales(const SkMatrix* transform, float &inverseScaleX, float& inverseScaleY) {
+inline void computeInverseScales(const SkMatrix* transform, float &inverseScaleX, float& inverseScaleY) {
     if (transform && transform->getType() & (SkMatrix::kScale_Mask|SkMatrix::kAffine_Mask|SkMatrix::kPerspective_Mask)) {
         float m00 = transform->getScaleX();
         float m01 = transform->getSkewY();
         float m10 = transform->getSkewX();
         float m11 = transform->getScaleY();
-        float scaleX = sqrt(m00 * m00 + m01 * m01);
-        float scaleY = sqrt(m10 * m10 + m11 * m11);
+        float scaleX = sk_float_sqrt(m00 * m00 + m01 * m01);
+        float scaleY = sk_float_sqrt(m10 * m10 + m11 * m11);
         inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
         inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
     } else {
@@ -82,7 +82,7 @@
  */
 inline SkVector totalOffsetFromNormals(const SkVector& normalA, const SkVector& normalB) {
     SkVector pseudoNormal = normalA + normalB;
-    pseudoNormal.scale(1.0f / (1.0f + fabs(normalA.dot(normalB))));
+    pseudoNormal.scale(1.0f / (1.0f + sk_float_abs(normalA.dot(normalB))));
     return pseudoNormal;
 }
 
@@ -97,7 +97,7 @@
     }
 }
 
-void getFillVerticesFromPerimeter(const SkTArray<Vertex, true>& perimeter, VertexBuffer* vertexBuffer) {
+static void getFillVerticesFromPerimeter(const SkTArray<Vertex, true>& perimeter, VertexBuffer* vertexBuffer) {
     Vertex* buffer = vertexBuffer->alloc<Vertex>(perimeter.count());
 
     int currentIndex = 0;
@@ -114,7 +114,7 @@
     }
 }
 
-void getStrokeVerticesFromPerimeter(const SkTArray<Vertex, true>& perimeter, float halfStrokeWidth,
+static void getStrokeVerticesFromPerimeter(const SkTArray<Vertex, true>& perimeter, float halfStrokeWidth,
         VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
     Vertex* buffer = vertexBuffer->alloc<Vertex>(perimeter.count() * 2 + 2);
 
@@ -153,7 +153,7 @@
     copyVertex(&buffer[currentIndex++], &buffer[1]);
 }
 
-void getStrokeVerticesFromUnclosedVertices(const SkTArray<Vertex, true>& vertices, float halfStrokeWidth,
+static void getStrokeVerticesFromUnclosedVertices(const SkTArray<Vertex, true>& vertices, float halfStrokeWidth,
         VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
     Vertex* buffer = vertexBuffer->alloc<Vertex>(vertices.count() * 2);
 
@@ -203,7 +203,7 @@
 #endif
 }
 
-void getFillVerticesFromPerimeterAA(const SkTArray<Vertex, true>& perimeter, VertexBuffer* vertexBuffer,
+static void getFillVerticesFromPerimeterAA(const SkTArray<Vertex, true>& perimeter, VertexBuffer* vertexBuffer,
          float inverseScaleX, float inverseScaleY) {
     AlphaVertex* buffer = vertexBuffer->alloc<AlphaVertex>(perimeter.count() * 3 + 2);
 
@@ -268,7 +268,7 @@
 }
 
 
-void getStrokeVerticesFromUnclosedVerticesAA(const SkTArray<Vertex, true>& vertices, float halfStrokeWidth,
+static void getStrokeVerticesFromUnclosedVerticesAA(const SkTArray<Vertex, true>& vertices, float halfStrokeWidth,
         VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
     AlphaVertex* buffer = vertexBuffer->alloc<AlphaVertex>(6 * vertices.count() + 2);
 
@@ -427,7 +427,7 @@
 }
 
 
-void getStrokeVerticesFromPerimeterAA(const SkTArray<Vertex, true>& perimeter, float halfStrokeWidth,
+static void getStrokeVerticesFromPerimeterAA(const SkTArray<Vertex, true>& perimeter, float halfStrokeWidth,
         VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
     AlphaVertex* buffer = vertexBuffer->alloc<AlphaVertex>(6 * perimeter.count() + 8);
 
@@ -520,12 +520,11 @@
 #endif
 }
 
-void PathRenderer::ConvexPathVertices(const SkPath &path, const SkPaint* paint,
+void PathRenderer::ConvexPathVertices(const SkPath &path, const SkStrokeRec& stroke, bool isAA,
         const SkMatrix* transform, VertexBuffer* vertexBuffer) {
     SK_TRACE_EVENT0("PathRenderer::convexPathVertices");
 
-    SkPaint::Style style = paint->getStyle();
-    bool isAA = paint->isAntiAlias();
+    SkStrokeRec::Style style = stroke.getStyle();
 
     float inverseScaleX, inverseScaleY;
     computeInverseScales(transform, inverseScaleX, inverseScaleY);
@@ -533,18 +532,18 @@
     SkTArray<Vertex, true> tempVertices;
     float threshInvScaleX = inverseScaleX;
     float threshInvScaleY = inverseScaleY;
-    if (style == SkPaint::kStroke_Style) {
+    if (style == SkStrokeRec::kStroke_Style) {
         // alter the bezier recursion threshold values we calculate in order to compensate for
         // expansion done after the path vertices are found
         SkRect bounds = path.getBounds();
         if (!bounds.isEmpty()) {
-            threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
-            threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
+            threshInvScaleX *= bounds.width() / (bounds.width() + stroke.getWidth());
+            threshInvScaleY *= bounds.height() / (bounds.height() + stroke.getWidth());
         }
     }
 
     // force close if we're filling the path, since fill path expects closed perimeter.
-    bool forceClose = style != SkPaint::kStroke_Style;
+    bool forceClose = style != SkStrokeRec::kStroke_Style;
     bool wasClosed = ConvexPathPerimeterVertices(path, forceClose, threshInvScaleX * threshInvScaleX,
             threshInvScaleY * threshInvScaleY, &tempVertices);
 
@@ -559,8 +558,8 @@
     }
 #endif
 
-    if (style == SkPaint::kStroke_Style) {
-        float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
+    if (style == SkStrokeRec::kStroke_Style) {
+        float halfStrokeWidth = stroke.getWidth() * 0.5f;
         if (!isAA) {
             if (wasClosed) {
                 getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer,
@@ -590,7 +589,7 @@
 }
 
 
-void pushToVector(SkTArray<Vertex, true>* vertices, float x, float y) {
+static void pushToVector(SkTArray<Vertex, true>* vertices, float x, float y) {
     // TODO: make this not yuck
     vertices->push_back();
     Vertex* newVertex = &((*vertices)[vertices->count() - 1]);
@@ -607,7 +606,7 @@
     SkPath::Iter iter(path, forceClose);
     SkPoint pts[4];
     SkPath::Verb v;
-    Vertex* newVertex = 0;
+
     while (SkPath::kDone_Verb != (v = iter.next(pts))) {
             switch (v) {
                 case SkPath::kMove_Verb:
@@ -661,8 +660,8 @@
         float sqrInvScaleX, float sqrInvScaleY, SkTArray<Vertex, true>* outputVertices) {
     float dx = p2x - p1x;
     float dy = p2y - p1y;
-    float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
-    float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
+    float d1 = sk_float_abs((c1x - p2x) * dy - (c1y - p2y) * dx);
+    float d2 = sk_float_abs((c2x - p2x) * dy - (c2y - p2y) * dx);
     float d = d1 + d2;
 
     // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
diff --git a/experimental/AndroidPathRenderer/AndroidPathRenderer.h b/experimental/AndroidPathRenderer/AndroidPathRenderer.h
index 64aebfa..0e87aed 100644
--- a/experimental/AndroidPathRenderer/AndroidPathRenderer.h
+++ b/experimental/AndroidPathRenderer/AndroidPathRenderer.h
@@ -57,7 +57,7 @@
 public:
     static SkRect ComputePathBounds(const SkPath& path, const SkPaint* paint);
 
-    static void ConvexPathVertices(const SkPath& path, const SkPaint* paint,
+    static void ConvexPathVertices(const SkPath& path, const SkStrokeRec& stroke, bool isAA,
             const SkMatrix* transform, VertexBuffer* vertexBuffer);
 
 private:
diff --git a/experimental/AndroidPathRenderer/GrAndroidPathRenderer.cpp b/experimental/AndroidPathRenderer/GrAndroidPathRenderer.cpp
new file mode 100644
index 0000000..2050f09
--- /dev/null
+++ b/experimental/AndroidPathRenderer/GrAndroidPathRenderer.cpp
@@ -0,0 +1,75 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAndroidPathRenderer.h"
+#include "AndroidPathRenderer.h"
+#include "Vertex.h"
+
+GrAndroidPathRenderer::GrAndroidPathRenderer() {
+}
+
+bool GrAndroidPathRenderer::canDrawPath(const SkPath& path,
+                                        const SkStrokeRec& stroke,
+                                        const GrDrawTarget* target,
+                                        bool antiAlias) const {
+    return ((stroke.isFillStyle() || stroke.getStyle() == SkStrokeRec::kStroke_Style)
+             && !path.isInverseFillType() && path.isConvex());
+}
+
+struct ColorVertex {
+    SkPoint pos;
+    GrColor color;
+};
+
+bool GrAndroidPathRenderer::onDrawPath(const SkPath& origPath,
+                                       const SkStrokeRec& stroke,
+                                       GrDrawTarget* target,
+                                       bool antiAlias) {
+
+    // generate verts using Android algorithm
+    android::uirenderer::VertexBuffer vertices;
+    android::uirenderer::PathRenderer::ConvexPathVertices(origPath, stroke, antiAlias, NULL,
+                                                          &vertices);
+
+    // set vertex layout depending on anti-alias
+    GrVertexLayout layout = antiAlias ? GrDrawState::kCoverage_VertexLayoutBit : 0;
+
+    // allocate our vert buffer
+    int vertCount = vertices.getSize();
+    GrDrawTarget::AutoReleaseGeometry geo(target, layout, vertCount, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
+        return false;
+    }
+
+    // copy android verts to our vertex buffer
+    if (antiAlias) {
+        ColorVertex* outVert = reinterpret_cast<ColorVertex*>(geo.vertices());
+        android::uirenderer::AlphaVertex* inVert =
+            reinterpret_cast<android::uirenderer::AlphaVertex*>(vertices.getBuffer());
+
+        for (int i = 0; i < vertCount; ++i) {
+            // copy vertex position
+            outVert->pos.set(inVert->position[0], inVert->position[1]);
+            // copy alpha
+            int coverage = static_cast<int>(inVert->alpha * 0xff);
+            outVert->color = GrColorPackRGBA(coverage, coverage, coverage, coverage);
+            ++outVert;
+            ++inVert;
+        }
+    } else {
+       size_t vsize = GrDrawState::VertexSize(layout);
+       size_t copySize = vsize*vertCount;
+       memcpy(geo.vertices(), vertices.getBuffer(), copySize);
+    }
+
+    // render it
+    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, vertCount);
+
+    return true;
+}
diff --git a/experimental/AndroidPathRenderer/GrAndroidPathRenderer.h b/experimental/AndroidPathRenderer/GrAndroidPathRenderer.h
new file mode 100644
index 0000000..38d2030
--- /dev/null
+++ b/experimental/AndroidPathRenderer/GrAndroidPathRenderer.h
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrPathRenderer.h"
+
+
+class GrAndroidPathRenderer : public GrPathRenderer {
+public:
+    GrAndroidPathRenderer();
+
+    virtual bool canDrawPath(const SkPath& path,
+                             const SkStrokeRec& stroke,
+                             const GrDrawTarget* target,
+                             bool antiAlias) const SK_OVERRIDE;
+
+protected:
+    virtual bool onDrawPath(const SkPath& path,
+                            const SkStrokeRec& stroke,
+                            GrDrawTarget* target,
+                            bool antiAlias) SK_OVERRIDE;
+
+private:
+    typedef GrPathRenderer INHERITED;
+};
diff --git a/experimental/Debugger/DebuggerCommandsView.cpp b/experimental/Debugger/DebuggerCommandsView.cpp
deleted file mode 100644
index 735c808..0000000
--- a/experimental/Debugger/DebuggerCommandsView.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-
-/*
- * 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 "DebuggerViews.h"
-
-DebuggerCommandsView::DebuggerCommandsView() {
-    fBGColor = 0xFFBBBBBB;
-    fTopIndex = 0;
-    fHighlight = 0;
-    fResizing = false;
-
-    SkPaint p;
-    p.setTextSize(SkIntToScalar(SKDEBUGGER_TEXTSIZE));
-    fSpacing = p.getFontSpacing();
-    fCentered = false;
-    fRange = (int)(this->height()/fSpacing) - 1;
-}
-
-DebuggerCommandsView::~DebuggerCommandsView() {
-    fList.deleteAll();
-}
-
-bool DebuggerCommandsView::onEvent(const SkEvent& evt) {
-    if (evt.isType(SKDEBUGGER_COMMANDTYPE)) {
-        *fList.append() = new SkString(evt.findString(SKDEBUGGER_ATOM));
-        this->inval(NULL);
-        return true;
-    }
-    return this->INHERITED::onEvent(evt);
-}
-
-void DebuggerCommandsView::onSizeChange() {
-    fRange = (int)(this->height()/fSpacing);
-    this->INHERITED::onSizeChange();
-}
-
-void DebuggerCommandsView::alignCenter() {
-    if (!fCentered || fHighlight < fRange/2 || fHighlight > (fList.count() - fRange/2)) {
-        return;
-    } else {
-        if (fHighlight > (fTopIndex + fRange/2))
-            fTopIndex += fHighlight - (fTopIndex + fRange/2);
-        if (fHighlight < (fTopIndex + fRange/2))
-            fTopIndex -= (fTopIndex + fRange/2) - fHighlight;
-    }
-}
-
-int DebuggerCommandsView::nextItem() {
-    if (fHighlight < fList.count() - 1)
-        ++fHighlight;
-    if (fHighlight < fTopIndex || fHighlight > (fTopIndex + fRange))
-        fTopIndex = fHighlight;
-    if (fHighlight == (fTopIndex + fRange))
-        ++fTopIndex;
-    this->alignCenter();
-    this->inval(NULL);
-    return fHighlight;
-}
-
-int DebuggerCommandsView::prevItem() {
-    if (fHighlight > 0)
-        --fHighlight;
-    if (fHighlight < fTopIndex || fHighlight > (fTopIndex + fRange))
-        fTopIndex = fHighlight;
-    this->alignCenter();
-    this->inval(NULL);
-    return fHighlight;
-}
-
-int DebuggerCommandsView::scrollUp() {
-    if (fTopIndex > 0)
-        --fTopIndex;
-    this->inval(NULL);
-    return fHighlight;
-}
-
-int DebuggerCommandsView::scrollDown() {
-    if (fTopIndex < (fList.count() - 1))
-        ++fTopIndex;
-    this->inval(NULL);
-    return fHighlight;
-}
-
-void DebuggerCommandsView::highlight(int index) {
-    SkASSERT(index >= 0 && index < fList.count());
-    if (fHighlight != index) {
-        fHighlight = index;
-        this->alignCenter();
-        this->inval(NULL);
-    }
-}
-
-int DebuggerCommandsView::selectHighlight(int ypos) {
-    int i = (int)(ypos/fSpacing) + fTopIndex;
-    if (i >= fList.count()) {
-        i = fList.count() - 1;
-    }
-    if (fHighlight != i) {
-        fHighlight = i;
-        this->alignCenter();
-        this->inval(NULL);
-    }
-    return fHighlight;
-}
-
-void DebuggerCommandsView::toggleCentered() {
-    fCentered = !fCentered;
-    this->alignCenter();
-    this->inval(NULL);
-}
-
-void DebuggerCommandsView::onDraw(SkCanvas* canvas) {
-    canvas->drawColor(fBGColor);
-
-    SkPaint p;
-    p.setTextSize(SkIntToScalar(SKDEBUGGER_TEXTSIZE));
-    p.setAntiAlias(true);
-
-    //draw highlight
-    int selected = fHighlight - fTopIndex;
-    SkRect r = {0, fSpacing * selected, this->width(), fSpacing * (selected+1)};
-    p.setColor(SKDEBUGGER_HIGHLIGHTCOLOR);
-    canvas->drawRect(r, p);
-
-    int endIndex = fTopIndex + fRange;
-    if (endIndex > fList.count())
-        endIndex = fList.count();
-
-    p.setColor(SKDEBUGGER_TEXTCOLOR);
-    int pos;
-    for (int i = fTopIndex; i < endIndex; ++i) {
-        pos = i - fTopIndex;
-        canvas->drawText(fList[i]->c_str(), fList[i]->size(),
-                         0, fSpacing - 2 + fSpacing * pos, p);
-    }
-    p.setColor(SKDEBUGGER_RESIZEBARCOLOR);
-    r = SkRect::MakeXYWH(this->width() - SKDEBUGGER_RESIZEBARSIZE, 0,
-                         SKDEBUGGER_RESIZEBARSIZE, this->height());
-    canvas->drawRect(r, p);
-}
-
diff --git a/experimental/Debugger/DebuggerContentView.cpp b/experimental/Debugger/DebuggerContentView.cpp
deleted file mode 100644
index f079508..0000000
--- a/experimental/Debugger/DebuggerContentView.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-#include "SampleCode.h"
-#include "SkOSMenu.h"
-
-#include "DebuggerViews.h"
-static const char gIsDebuggerQuery[] = "is-debugger";
-class DebuggerView : public SampleView {
-public:
-        DebuggerView(const char* data, size_t size) {
-        fData.append(size, data);
-        fCommandsVisible = true;
-        fCommandsResizing = false;
-        fStateVisible = true;
-        fStateResizing = false;
-
-        fCommands = new DebuggerCommandsView;
-        fCommands->setVisibleP(fCommandsVisible);
-        this->attachChildToFront(fCommands)->unref();
-
-
-        fState = new DebuggerStateView;
-        fState->setVisibleP(fStateVisible);
-        this->attachChildToFront(fState)->unref();
-
-        fAtomsToRead = 0;
-        fDisplayClip = false;
-
-        fDumper = new SkDebugDumper(this->getSinkID(), fCommands->getSinkID(),
-                                    fState->getSinkID());
-
-        fDumper->unload();
-        fAtomBounds.reset();
-        fFrameBounds.reset();
-
-        SkDumpCanvas* dumpCanvas = new SkDumpCanvas(fDumper);
-        SkGPipeReader* dumpReader = new SkGPipeReader(dumpCanvas);
-
-
-        if (size > 0) {
-            int offset = 0;
-            int frameBound = 0;
-            size_t bytesRead;
-            while (static_cast<unsigned>(offset) < size) {
-                SkGPipeReader::Status s =
-                    dumpReader->playback(data + offset, size - offset,
-                                         SkGPipeReader::kReadAtom_PlaybackFlag, &bytesRead);
-                SkASSERT(SkGPipeReader::kError_Status != s);
-                offset += bytesRead;
-
-                if (SkGPipeReader::kDone_Status == s) {
-                    fDumper->dump(dumpCanvas, SkDumpCanvas::kNULL_Verb,
-                                 "End of Frame", NULL);
-                    delete dumpReader;
-                    delete dumpCanvas;
-                    dumpCanvas = new SkDumpCanvas(fDumper);
-                    dumpReader = new SkGPipeReader(dumpCanvas);
-                    frameBound = offset;
-                }
-                fAtomBounds.append(1, &offset);
-                fFrameBounds.append(1, &frameBound);
-            }
-        }
-
-        delete dumpReader;
-        delete dumpCanvas;
-
-        fDumper->load();
-    }
-
-    ~DebuggerView() {
-        fAtomBounds.reset();
-        fFrameBounds.reset();
-        delete fDumper;
-    }
-
-    virtual void requestMenu(SkOSMenu* menu) {
-        menu->setTitle("Debugger");
-        menu->appendSwitch("Show Commands", "Commands", this->getSinkID(), fCommandsVisible);
-        menu->appendSwitch("Show State", "State", this->getSinkID(), fStateVisible);
-        menu->appendSwitch("Display Clip", "Clip", this->getSinkID(), fDisplayClip);
-    }
-
-
-    void goToAtom(int atom) {
-        if (atom != fAtomsToRead) {
-            fAtomsToRead = atom;
-            this->inval(NULL);
-        }
-    }
-
-protected:
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Debugger");
-            return true;
-        }
-        if (evt->isType(gIsDebuggerQuery)) {
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual bool onEvent(const SkEvent& evt) {
-        if (SkOSMenu::FindSwitchState(evt, "Commands", &fCommandsVisible) ||
-            SkOSMenu::FindSwitchState(evt, "State", &fStateVisible)) {
-            fCommands->setVisibleP(fCommandsVisible);
-            fState->setVisibleP(fStateVisible);
-            fStateOffset = (fCommandsVisible) ? fCommands->width() : 0;
-            fState->setSize(this->width() - fStateOffset, fState->height());
-            fState->setLoc(fStateOffset, this->height() - fState->height());
-            this->inval(NULL);
-            return true;
-        }
-        if (SkOSMenu::FindSwitchState(evt, "Clip", &fDisplayClip)) {
-            this->inval(NULL);
-            return true;
-        }
-        return this->INHERITED::onEvent(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        if (fData.count() <= 0)
-            return;
-        SkAutoCanvasRestore acr(canvas, true);
-        canvas->translate(fStateOffset, 0);
-
-        int lastFrameBound = fFrameBounds[fAtomsToRead];
-        int toBeRead = fAtomBounds[fAtomsToRead] - lastFrameBound;
-        int firstChunk = (fAtomsToRead > 0) ? fAtomBounds[fAtomsToRead - 1] - lastFrameBound: 0;
-        if (toBeRead > 0) {
-            SkDumpCanvas* dumpCanvas = new SkDumpCanvas(fDumper);
-            SkGPipeReader* dumpReader = new SkGPipeReader(dumpCanvas);
-            SkGPipeReader* reader = new SkGPipeReader(canvas);
-            fDumper->disable();
-
-            int offset = 0;
-            size_t bytesRead;
-            SkGPipeReader::Status s;
-            //Read the first chunk
-            if (offset < firstChunk && firstChunk < toBeRead) {
-                s = dumpReader->playback(fData.begin() + offset, firstChunk - offset);
-                SkASSERT(SkGPipeReader::kError_Status != s);
-                s = reader->playback(fData.begin() + offset, firstChunk - offset, 0, &bytesRead);
-                SkASSERT(SkGPipeReader::kError_Status != s);
-                if (SkGPipeReader::kDone_Status == s){
-                    delete dumpReader;
-                    delete dumpCanvas;
-                    dumpCanvas = new SkDumpCanvas(fDumper);
-                    dumpReader = new SkGPipeReader(dumpCanvas);
-                    delete reader;
-                    reader = new SkGPipeReader(canvas);
-                }
-                offset += bytesRead;
-            }
-            SkASSERT(offset == firstChunk);
-            //Then read the current atom
-            fDumper->enable();
-            s = dumpReader->playback(fData.begin() + offset, toBeRead - offset,
-                                     SkGPipeReader::kReadAtom_PlaybackFlag);
-            SkASSERT(SkGPipeReader::kError_Status != s);
-            s = reader->playback(fData.begin() + offset, toBeRead - offset,
-                                 SkGPipeReader::kReadAtom_PlaybackFlag, &bytesRead);
-            SkASSERT(SkGPipeReader::kError_Status != s);
-
-            delete reader;
-            delete dumpReader;
-            delete dumpCanvas;
-
-            if (fDisplayClip) {
-                SkPaint p;
-                p.setColor(0x440000AA);
-                SkPath path;
-                canvas->getTotalClip().getBoundaryPath(&path);
-                canvas->drawPath(path, p);
-            }
-        }
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        return new Click(this);
-    }
-
-    virtual bool onClick(SkView::Click* click) {
-        SkPoint prev = click->fPrev;
-        SkPoint curr = click->fCurr;
-        bool handled = true;
-        switch (click->fState) {
-            case SkView::Click::kDown_State:
-                if (SkScalarAbs(curr.fX - fCommands->width()) <= SKDEBUGGER_RESIZEBARSIZE) {
-                    fCommandsResizing = true;
-                }
-                else if (SkScalarAbs(curr.fY - (this->height() - fState->height())) <= SKDEBUGGER_RESIZEBARSIZE &&
-                         curr.fX > fCommands->width()) {
-                    fStateResizing = true;
-                }
-                else if (curr.fX < fCommands->width()) {
-                    fAtomsToRead = fCommands->selectHighlight(
-                                                  SkScalarFloorToInt(curr.fY));
-                }
-                else
-                    handled = false;
-                break;
-            case SkView::Click::kMoved_State:
-                if (fCommandsResizing)
-                    fCommands->setSize(curr.fX, this->height());
-                else if (fStateResizing)
-                    fState->setSize(this->width(), this->height() - curr.fY);
-                else if (curr.fX < fCommands->width()) {
-                    if (curr.fY - prev.fY < 0) {
-                        fCommands->scrollDown();
-                    }
-                    if (curr.fY - prev.fY > 0) {
-                        fCommands->scrollUp();
-                    }
-                }
-                else
-                    handled = false;
-                break;
-            case SkView::Click::kUp_State:
-                fStateResizing = fCommandsResizing = false;
-                break;
-            default:
-                break;
-        }
-
-        fStateOffset = fCommands->width();
-        fState->setSize(this->width() - fStateOffset, fState->height());
-        fState->setLoc(fStateOffset, this->height() - fState->height());
-        if (handled)
-            this->inval(NULL);
-        return handled;
-    }
-
-    virtual void onSizeChange() {
-        this->INHERITED::onSizeChange();
-        fCommands->setSize(CMD_WIDTH, this->height());
-        fCommands->setLoc(0, 0);
-        fState->setSize(this->width() - CMD_WIDTH, SkFloatToScalar(INFO_HEIGHT));
-        fState->setLoc(CMD_WIDTH, this->height() - SkFloatToScalar(INFO_HEIGHT));
-    }
-
-private:
-    DebuggerCommandsView*   fCommands;
-    DebuggerStateView*      fState;
-    bool                    fCommandsResizing;
-    bool                    fCommandsVisible;
-    bool                    fStateResizing;
-    bool                    fStateVisible;
-    float                   fStateOffset;
-    bool                    fDisplayClip;
-    int                     fAtomsToRead;
-    SkTDArray<int>          fAtomBounds;
-    SkTDArray<int>          fFrameBounds;
-    SkTDArray<char>         fData;
-    SkDebugDumper*          fDumper;
-
-    typedef SampleView INHERITED;
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkView* create_debugger(const char* data, size_t size);
-
-SkView* create_debugger(const char* data, size_t size) {
-    return SkNEW_ARGS(DebuggerView, (data, size));
-};
-
-bool is_debugger(SkView* view);
-
-bool is_debugger(SkView* view) {
-    SkEvent isDebugger(gIsDebuggerQuery);
-    return view->doQuery(&isDebugger);
-}
diff --git a/experimental/Debugger/DebuggerStateView.cpp b/experimental/Debugger/DebuggerStateView.cpp
deleted file mode 100644
index 27befcb..0000000
--- a/experimental/Debugger/DebuggerStateView.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-
-/*
- * 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 "DebuggerViews.h"
-#include "SkRect.h"
-
-DebuggerStateView::DebuggerStateView() {
-    fBGColor = 0xFF999999;
-    fPaint.setColor(fBGColor);
-    fResizing = false;
-}
-
-bool DebuggerStateView::onEvent(const SkEvent& evt) {
-    if (evt.isType(SKDEBUGGER_STATETYPE)) {
-        fMatrix = evt.findString(SKDEBUGGER_MATRIX);
-        fClip = evt.findString(SKDEBUGGER_CLIP);
-
-        SkPaint* ptr;
-        if (evt.getMetaData().findPtr(SKDEBUGGER_PAINT, (void**)&ptr)) {
-            fPaint = *ptr;
-            fPaintInfo = evt.findString(SKDEBUGGER_PAINTINFO);
-        }
-        this->inval(NULL);
-        return true;
-    }
-    return this->INHERITED::onEvent(evt);
-}
-
-void DebuggerStateView::onDraw(SkCanvas* canvas) {
-    canvas->drawColor(fBGColor);
-
-    //Display Current Paint
-    SkRect r = {10, 20, 40, 50};
-    canvas->drawRect(r, fPaint);
-    //Display Information
-    SkPaint p;
-    p.setTextSize(SKDEBUGGER_TEXTSIZE);
-    p.setAntiAlias(true);
-    SkScalar x = 50 * SK_Scalar1;
-    canvas->drawText(fPaintInfo.c_str(), fPaintInfo.size(), x, 30, p);
-    canvas->drawText(fMatrix.c_str(), fMatrix.size(), x, 60, p);
-    canvas->drawText(fClip.c_str(), fClip.size(), x, 90, p);
-
-    p.setColor(SKDEBUGGER_RESIZEBARCOLOR);
-    r = SkRect::MakeXYWH(0, 0, this->width(), SKDEBUGGER_RESIZEBARSIZE);
-    canvas->drawRect(r, p);
-}
-
diff --git a/experimental/Debugger/DebuggerViews.h b/experimental/Debugger/DebuggerViews.h
deleted file mode 100644
index c63283f..0000000
--- a/experimental/Debugger/DebuggerViews.h
+++ /dev/null
@@ -1,100 +0,0 @@
-
-/*
- * 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 "SkView.h"
-#include "SkColor.h"
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkGPipe.h"
-#include "SkPaint.h"
-
-#include "SkDebugDumper.h"
-
-#define SKDEBUGGER_COMMANDTYPE  "SKDEBUGGER_COMMAND"
-#define SKDEBUGGER_STATETYPE    "SKDEBUGGER_STATE"
-
-#define SKDEBUGGER_ATOM         "SKDEBUGGER_ATOM"
-#define SKDEBUGGER_MATRIX       "SKDEBUGGER_MATRIX"
-#define SKDEBUGGER_CLIP         "SKDEBUGGER_CLIP"
-#define SKDEBUGGER_PAINTINFO    "SKDEBUGGER_PAINTINFO"
-#define SKDEBUGGER_PAINT        "SKDEBUGGER_PAINT"
-
-#define SKDEBUGGER_TEXTSIZE         14
-#define CMD_WIDTH                   200
-#define INFO_HEIGHT                 150.0f
-#define SKDEBUGGER_HIGHLIGHTCOLOR   0xFF113399
-#define SKDEBUGGER_TEXTCOLOR        0xFF000000
-#define SKDEBUGGER_RESIZEBARCOLOR   0xFF333333
-#define SKDEBUGGER_RESIZEBARSIZE    5
-
-/*
- * Debugger - Info Panel
- */
-class DebuggerStateView : public SkView {
-public:
-    DebuggerStateView();
-
-protected:
-    virtual bool onEvent(const SkEvent& evt);
-    virtual void onDraw(SkCanvas* canvas);
-private:
-    SkColor     fBGColor;
-    SkPaint     fPaint;
-    SkString    fMatrix;
-    SkString    fPaintInfo;
-    SkString    fClip;
-    bool        fResizing;
-    typedef SkView INHERITED;
-};
-
-/*
- * Debugger - Commands List
- */
-class DebuggerCommandsView : public SkView {
-public:
-    DebuggerCommandsView();
-    ~DebuggerCommandsView();
-    int nextItem();
-    int prevItem();
-    int scrollUp();
-    int scrollDown();
-    void highlight(int index);
-    int  selectHighlight(int ypos);
-    void toggleCentered();
-
-protected:
-    virtual bool onEvent(const SkEvent& evt);
-    virtual void onSizeChange();
-    virtual void onDraw(SkCanvas* canvas);
-private:
-    void        init();
-    void        alignCenter();
-    SkColor     fBGColor;
-    int         fTopIndex;
-    int         fHighlight;
-    SkScalar    fSpacing;
-    int         fRange;
-    bool        fResizing;
-    bool        fCentered;
-    SkTDArray<SkString*> fList;
-    typedef SkView INHERITED;
-};
-
-
-static void* PaintProc(void* ptr, bool doRef) {
-    SkPaint* p = (SkPaint*) ptr;
-
-    if (doRef) {
-        return new SkPaint(*p);
-    }
-    else {
-        delete p;
-        return NULL;
-    }
-
-}
-
diff --git a/experimental/Debugger/SkDebugDumper.cpp b/experimental/Debugger/SkDebugDumper.cpp
deleted file mode 100644
index cfda184..0000000
--- a/experimental/Debugger/SkDebugDumper.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-
-/*
- * 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 "SkDebugDumper.h"
-#include "SkString.h"
-#include "SkPaint.h"
-#include "SkShader.h"
-#include "SkPathEffect.h"
-#include "SkXfermode.h"
-#include "SkColorFilter.h"
-#include "SkPathEffect.h"
-#include "SkMaskFilter.h"
-#include "DebuggerViews.h"
-
-SkDebugDumper::SkDebugDumper(SkEventSinkID cID, SkEventSinkID clID,
-                             SkEventSinkID ipID) {
-    fContentID = cID;
-    fCommandsID = clID;
-    fStateID = ipID;
-    fInit = false;
-    fDisabled = false;
-    fCount = 0;
-}
-
-static void appendPtr(SkString* str, const void* ptr, const char name[]) {
-    if (ptr) {
-        str->appendf("%s: %p\t", name, ptr);
-    }
-}
-
-static void appendFlattenable(SkString* str, const SkFlattenable* ptr,
-                              const char name[]) {
-    if (ptr) {
-        str->appendf("%s: %p\n", name, ptr);
-    }
-}
-
-static SkString dumpMatrix(SkDumpCanvas* canvas) {
-    SkString str;
-    SkMatrix m = canvas->getTotalMatrix();
-    str.append("Matrix:");
-    str.appendf("Translate (%0.4g, %0.4g) ",
-                 SkScalarToFloat(m.get(SkMatrix::kMTransX)),
-                 SkScalarToFloat(m.get(SkMatrix::kMTransY)));
-    str.appendf("Scale (%0.4g, %0.4g) ",
-                 SkScalarToFloat(m.get(SkMatrix::kMScaleX)),
-                 SkScalarToFloat(m.get(SkMatrix::kMScaleY)));
-    str.appendf("Skew (%0.4g, %0.4g) ",
-                 SkScalarToFloat(m.get(SkMatrix::kMSkewX)),
-                 SkScalarToFloat(m.get(SkMatrix::kMSkewY)));
-    str.appendf("Perspective (%0.4g, %0.4g, %0.4g) ",
-                 SkScalarToFloat(SkPerspToScalar(m.get(SkMatrix::kMPersp0))),
-                 SkScalarToFloat(SkPerspToScalar(m.get(SkMatrix::kMPersp1))),
-                 SkScalarToFloat(SkPerspToScalar(m.get(SkMatrix::kMPersp2))));
-    return str;
-}
-
-
-static const int maxPts = 50;
-static SkString dumpClip(SkDumpCanvas* canvas) {
-    SkString str;
-    SkPath p;
-    if (canvas->getTotalClip().getBoundaryPath(&p)) {
-        SkPoint pts[maxPts];
-        int numPts = p.getPoints(pts, maxPts);
-
-        str.append("Clip: [ ");
-        for (int i = 0; i < numPts; ++i) {
-            str.appendf("(%0.4g, %0.4g)", pts[i].x(), pts[i].y());
-            if (i < numPts-1)
-                str.append(" , ");
-        }
-        str.append(" ]");
-    }
-    return str;
-}
-
-static const char* gPaintFlags[] = {
-    "AntiAliasing",
-    "Bitmap Filtering",
-    "Dithering",
-    "Underline Text",
-    "Strike-Through Text",
-    "Fake Bold Text",
-    "Linear Text",
-    "Subpixel Positioned Text",
-    "Device Kerning Text",
-    "LCD/Subpixel Glyph Rendering",
-    "Embedded Bitmap Text",
-    "Freetype Autohinting",
-    "ALL"
-};
-
-
-static SkString dumpPaint(SkDumpCanvas* canvas, const SkPaint* p,
-                      SkDumpCanvas::Verb verb) {
-    SkString str;
-    str.appendf("Color: #%08X\n", p->getColor());
-    str.appendf("Flags: %s\n", gPaintFlags[p->getFlags()]);
-    appendFlattenable(&str, p->getShader(), "shader");
-    appendFlattenable(&str, p->getXfermode(), "xfermode");
-    appendFlattenable(&str, p->getPathEffect(), "pathEffect");
-    appendFlattenable(&str, p->getMaskFilter(), "maskFilter");
-    appendFlattenable(&str, p->getPathEffect(), "pathEffect");
-    appendFlattenable(&str, p->getColorFilter(), "filter");
-
-    if (SkDumpCanvas::kDrawText_Verb == verb) {
-        str.appendf("Text Size:%0.4g\n", SkScalarToFloat(p->getTextSize()));
-        appendPtr(&str, p->getTypeface(), "typeface");
-    }
-
-    return str;
-}
-
-void SkDebugDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb,
-                          const char str[], const SkPaint* p) {
-    if (!fDisabled) {
-        SkString msg, tab;
-
-        const int level = canvas->getNestLevel() + canvas->getSaveCount() - 1;
-        SkASSERT(level >= 0);
-        for (int i = 0; i < level; i++) {
-            tab.append("| ");
-        }
-
-        msg.appendf("%03d: %s%s\n", fCount, tab.c_str(), str);
-        ++fCount;
-        if (!fInit) {
-            SkEvent* cmd = new SkEvent(SKDEBUGGER_COMMANDTYPE, fCommandsID);
-            cmd->setString(SKDEBUGGER_ATOM, msg);
-            cmd->postDelay(100);
-        }
-        else {
-            SkEvent* state = new SkEvent(SKDEBUGGER_STATETYPE, fStateID);
-            state->setString(SKDEBUGGER_MATRIX, dumpMatrix(canvas));
-            state->setString(SKDEBUGGER_CLIP, dumpClip(canvas));
-            if (p) {
-                state->setString(SKDEBUGGER_PAINTINFO, dumpPaint(canvas, p, verb));
-                state->getMetaData().setPtr(SKDEBUGGER_PAINT, (void*)p, PaintProc);
-            }
-            state->post();
-        }
-    }
-}
diff --git a/experimental/Debugger/SkDebugDumper.h b/experimental/Debugger/SkDebugDumper.h
deleted file mode 100644
index e7d6956..0000000
--- a/experimental/Debugger/SkDebugDumper.h
+++ /dev/null
@@ -1,38 +0,0 @@
-
-/*
- * 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 SkDebugDumper_DEFINED
-#define SkDebugDumper_DEFINED
-#include "SkDumpCanvas.h"
-#include "SkEvent.h"
-
-/** Formats the draw commands, and send them to a function-pointer provided
- by the caller.
- */
-class SkDebugDumper : public SkDumpCanvas::Dumper {
-public:
-    SkDebugDumper(SkEventSinkID cID, SkEventSinkID clID, SkEventSinkID ipID);
-    // override from baseclass that does the formatting, and in turn calls
-    // the function pointer that was passed to the constructor
-    virtual void dump(SkDumpCanvas*, SkDumpCanvas::Verb, const char str[],
-                      const SkPaint*);
-
-    void load() { fInit = true; };
-    void unload() { fInit = false; fCount = 0;};
-    void disable() { fDisabled = true; };
-    void enable() { fDisabled = false; };
-private:
-    int             fCount;
-    bool            fInit;
-    bool            fDisabled;
-    SkEventSinkID   fContentID;
-    SkEventSinkID   fCommandsID;
-    SkEventSinkID   fStateID;
-
-    typedef SkDumpCanvas::Dumper INHERITED;
-};
-#endif
diff --git a/experimental/DrawingBoard/SampleDrawingClient.cpp b/experimental/DrawingBoard/SampleDrawingClient.cpp
index fbefbc8..b86c8c1 100644
--- a/experimental/DrawingBoard/SampleDrawingClient.cpp
+++ b/experimental/DrawingBoard/SampleDrawingClient.cpp
@@ -276,4 +276,3 @@
 
 static SkView* MyFactory() { return new DrawingClientView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/experimental/DrawingBoard/SkColorPalette.cpp b/experimental/DrawingBoard/SkColorPalette.cpp
index 566c134..f2776af 100644
--- a/experimental/DrawingBoard/SkColorPalette.cpp
+++ b/experimental/DrawingBoard/SkColorPalette.cpp
@@ -193,4 +193,3 @@
     retval += (int)(b * 255);
     return retval;
 }
-
diff --git a/experimental/DrawingBoard/SkColorPalette.h b/experimental/DrawingBoard/SkColorPalette.h
index b8e4c9f..0ee1dd5 100644
--- a/experimental/DrawingBoard/SkColorPalette.h
+++ b/experimental/DrawingBoard/SkColorPalette.h
@@ -32,4 +32,3 @@
 };
 
 #endif
-
diff --git a/experimental/DrawingBoard/SkNetPipeController.cpp b/experimental/DrawingBoard/SkNetPipeController.cpp
index a61ca64..3e4ded5 100644
--- a/experimental/DrawingBoard/SkNetPipeController.cpp
+++ b/experimental/DrawingBoard/SkNetPipeController.cpp
@@ -48,4 +48,3 @@
 
     fAtomsWritten += 1;
 }
-
diff --git a/experimental/DrawingBoard/SkNetPipeController.h b/experimental/DrawingBoard/SkNetPipeController.h
index 081239b..84b1714 100644
--- a/experimental/DrawingBoard/SkNetPipeController.h
+++ b/experimental/DrawingBoard/SkNetPipeController.h
@@ -34,4 +34,3 @@
     SkGPipeReader::Status   fStatus;
 };
 #endif
-
diff --git a/experimental/FileReaderApp/FileReaderWindow.h b/experimental/FileReaderApp/FileReaderWindow.h
index f83f918..e18a31c 100644
--- a/experimental/FileReaderApp/FileReaderWindow.h
+++ b/experimental/FileReaderApp/FileReaderWindow.h
@@ -12,4 +12,3 @@
     ReaderView* fReaderView;
 }
 @end
-
diff --git a/experimental/FileReaderApp/ReaderView.cpp b/experimental/FileReaderApp/ReaderView.cpp
index 78a7a21..d0792d2 100644
--- a/experimental/FileReaderApp/ReaderView.cpp
+++ b/experimental/FileReaderApp/ReaderView.cpp
@@ -73,4 +73,3 @@
     canvas->drawBitmap(fBufferBitmaps[fFront], 0, 0, NULL);
     this->inval(NULL);
 }
-
diff --git a/experimental/FileReaderApp/ReaderView.h b/experimental/FileReaderApp/ReaderView.h
index e3032d3..3f61eb8 100644
--- a/experimental/FileReaderApp/ReaderView.h
+++ b/experimental/FileReaderApp/ReaderView.h
@@ -27,4 +27,3 @@
     SkBitmap fBufferBitmaps[2];
     typedef SkView INHERITED;
 };
-
diff --git a/experimental/Intersection/ActiveEdge_Test.cpp b/experimental/Intersection/ActiveEdge_Test.cpp
index 1ac340e..1c82f1c 100755
--- a/experimental/Intersection/ActiveEdge_Test.cpp
+++ b/experimental/Intersection/ActiveEdge_Test.cpp
@@ -81,7 +81,3 @@
         SkASSERT(!operator_less_than(right, left));
     }
 }
-
-
-
-
diff --git a/experimental/Intersection/ConvexHull.cpp b/experimental/Intersection/ConvexHull.cpp
index ad932e3..e9a08c2 100644
--- a/experimental/Intersection/ConvexHull.cpp
+++ b/experimental/Intersection/ConvexHull.cpp
@@ -141,4 +141,3 @@
     return (1 << lower0Index | 1 << upper0Index
             | 1 << lower3Index | 1 << upper3Index) == 0x0F;
 }
-
diff --git a/experimental/Intersection/ConvexHull_Test.cpp b/experimental/Intersection/ConvexHull_Test.cpp
index bd83840..3979b1e 100644
--- a/experimental/Intersection/ConvexHull_Test.cpp
+++ b/experimental/Intersection/ConvexHull_Test.cpp
@@ -146,17 +146,17 @@
             int zeroes;
             zeroes = -1;
             bzero(sides, sizeof(sides));
-            if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] src=(%g,%g) rot=", __FUNCTION__,
+            if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] src=(%g,%g) rot=", __FUNCTION__,
                     (int)idx, (int)inr, (int)outer, (int)inner,
                     cubic[inner].x, cubic[inner].y);
             for (int index = 0; index < 4; ++index) {
-                if (debug_rotate_to_hull) printf("(%g,%g) ", rotPath[index].x, rotPath[index].y);
+                if (debug_rotate_to_hull) SkDebugf("(%g,%g) ", rotPath[index].x, rotPath[index].y);
                 sides[side(rotPath[index].y - rotPath[inner].y)]++;
                 if (index != outer && index != inner
                         && side(rotPath[index].y - rotPath[inner].y) == 1)
                     zeroes = index;
             }
-            if (debug_rotate_to_hull) printf("sides=(%d,%d,%d)\n", sides[0], sides[1], sides[2]);
+            if (debug_rotate_to_hull) SkDebugf("sides=(%d,%d,%d)\n", sides[0], sides[1], sides[2]);
             if (sides[0] && sides[2]) {
                 continue;
             }
@@ -165,25 +165,25 @@
                 // if either of remaining two equals outer or equal, pick lower
                 if (rotPath[zeroes].approximatelyEqual(rotPath[inner])
                         && zeroes < inner) {
-                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes < inner\n",
+                    if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes < inner\n",
                         __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
                     continue;
                 }
                  if (rotPath[zeroes].approximatelyEqual(rotPath[outer])
                         && zeroes < outer) {
-                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes < outer\n",
+                    if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes < outer\n",
                         __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
                     continue;
                 }
                 if (rotPath[zeroes].x < rotPath[inner].x
                         && rotPath[zeroes].x < rotPath[outer].x) {
-                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes < inner && outer\n",
+                    if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes < inner && outer\n",
                         __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
                     continue;
                 }
                 if (rotPath[zeroes].x > rotPath[inner].x
                         && rotPath[zeroes].x > rotPath[outer].x) {
-                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes > inner && outer\n",
+                    if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] zeroes > inner && outer\n",
                         __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
                     continue;
                 }
@@ -192,7 +192,7 @@
                 outsidePtSet[outer] = inner;
             } else {
                 if (outsidePtSet[inner] > 0) {
-                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] too many rays from one point\n",
+                    if (debug_rotate_to_hull) SkDebugf("%s [%d,%d] [o=%d,i=%d] too many rays from one point\n",
                         __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
                 }
                 outsidePtSet[inner] = outer;
@@ -237,7 +237,7 @@
             }
             int result = convex_hull(cubic, order);
             if (cmp != result) {
-                printf("%s [%d,%d] result=%d cmp=%d\n", __FUNCTION__,
+                SkDebugf("%s [%d,%d] result=%d cmp=%d\n", __FUNCTION__,
                     (int)index, (int)inner, result, cmp);
                 continue;
             }
@@ -247,23 +247,23 @@
             int pt, bit;
             for (pt = 0; pt < cmp; ++pt) {
                 if (pts & 1 << order[pt]) {
-                    printf("%s [%d,%d] duplicate index in order: %d,%d,%d",
+                    SkDebugf("%s [%d,%d] duplicate index in order: %d,%d,%d",
                             __FUNCTION__, (int)index, (int)inner,
                             order[0], order[1], order[2]);
                     if (cmp == 4) {
-                        printf(",%d", order[3]);
+                        SkDebugf(",%d", order[3]);
                     }
-                    printf("\n");
+                    SkDebugf("\n");
                     goto next;
                 }
                 if (cmpPts & 1 << cmpOrder[pt]) {
-                    printf("%s [%d,%d] duplicate index in order: %d,%d,%d",
+                    SkDebugf("%s [%d,%d] duplicate index in order: %d,%d,%d",
                             __FUNCTION__, (int)index, (int)inner,
                             cmpOrder[0], cmpOrder[1], cmpOrder[2]);
                     if (cmp == 4) {
-                        printf(",%d", cmpOrder[3]);
+                        SkDebugf(",%d", cmpOrder[3]);
                     }
-                    printf("\n");
+                    SkDebugf("\n");
                     goto next;
                 }
                 pts |= 1 << order[pt];
@@ -296,17 +296,17 @@
                 }
             }
             if (pts != cmpPts) {
-                printf("%s [%d,%d] mismatch indices: order=%d,%d,%d",
+                SkDebugf("%s [%d,%d] mismatch indices: order=%d,%d,%d",
                         __FUNCTION__, (int)index, (int)inner,
                         order[0], order[1], order[2]);
                 if (cmp == 4) {
-                    printf(",%d", order[3]);
+                    SkDebugf(",%d", order[3]);
                 }
-                printf(" cmpOrder=%d,%d,%d", cmpOrder[0], cmpOrder[1], cmpOrder[2]);
+                SkDebugf(" cmpOrder=%d,%d,%d", cmpOrder[0], cmpOrder[1], cmpOrder[2]);
                 if (cmp == 4) {
-                    printf(",%d", cmpOrder[3]);
+                    SkDebugf(",%d", cmpOrder[3]);
                 }
-                printf("\n");
+                SkDebugf("\n");
                 continue;
             }
             if (cmp == 4) { // check for bow ties
@@ -315,7 +315,7 @@
                     ++match;
                 }
                 if (cmpOrder[match ^ 2] != order[2]) {
-                    printf("%s [%d,%d] bowtie mismatch: order=%d,%d,%d,%d"
+                    SkDebugf("%s [%d,%d] bowtie mismatch: order=%d,%d,%d,%d"
                             " cmpOrder=%d,%d,%d,%d\n",
                             __FUNCTION__, (int)index, (int)inner,
                             order[0], order[1], order[2], order[3],
@@ -365,12 +365,12 @@
             if (connectTo0[idx] >= 1 && connectTo0[idx] < 4) {
                 continue;
             } else {
-                printf("%s connectTo0[idx]=%d", __FUNCTION__, connectTo0[idx]);
+                SkDebugf("%s connectTo0[idx]=%d", __FUNCTION__, connectTo0[idx]);
             }
             if (connectTo3[idx] >= 0 && connectTo3[idx] < 3) {
                 continue;
             } else {
-                printf("%s connectTo3[idx]=%d", __FUNCTION__, connectTo3[idx]);
+                SkDebugf("%s connectTo3[idx]=%d", __FUNCTION__, connectTo3[idx]);
             }
             goto nextTest;
         }
@@ -398,7 +398,7 @@
         }
         if (connectTo0[0] != connectTo0[1]) {
             if (rOrder[0] == rOrder[1]) {
-                printf("%s [%d] (1) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (1) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
@@ -407,7 +407,7 @@
             int unused = 6 - connectTo0[0] - connectTo0[1];
             int rUnused = 6 - rOrder[0] - rOrder[1];
             if (unused != rUnused) {
-                printf("%s [%d] (2) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (2) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
@@ -415,14 +415,14 @@
             }
         } else {
             if (rOrder[0] != rOrder[1]) {
-                printf("%s [%d] (3) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (3) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
                 continue;
             }
             if (connectTo0[0] != rOrder[0]) {
-                printf("%s [%d] (4) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (4) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
@@ -431,7 +431,7 @@
         }
         if (connectTo3[0] != connectTo3[1]) {
              if (rOrder[2] == rOrder[3]) {
-                printf("%s [%d] (5) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (5) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
@@ -440,7 +440,7 @@
            int unused = 6 - connectTo3[0] - connectTo3[1];
            int rUnused = 6 - rOrder[2] - rOrder[3];
             if (unused != rUnused) {
-                printf("%s [%d] (6) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (6) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
@@ -448,14 +448,14 @@
             }
         } else {
             if (rOrder[2] != rOrder[3]) {
-                printf("%s [%d] (7) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (7) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
                 continue;
             }
             if (connectTo3[1] != rOrder[3]) {
-                printf("%s [%d] (8) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                SkDebugf("%s [%d] (8) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
                     __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
                     connectTo3[0], connectTo3[1],
                     rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
@@ -466,6 +466,3 @@
         ;
     }
 }
-
-
-
diff --git a/experimental/Intersection/CubeRoot.cpp b/experimental/Intersection/CubeRoot.cpp
index 82d2732..5f785a0 100644
--- a/experimental/Intersection/CubeRoot.cpp
+++ b/experimental/Intersection/CubeRoot.cpp
@@ -374,7 +374,7 @@
 #endif
 
 double cube_root(double x) {
-    if (approximately_zero(x)) {
+    if (approximately_zero_cubed(x)) {
         return 0;
     }
     double result = halley_cbrt3d(fabs(x));
diff --git a/experimental/Intersection/CubicBezierClip.cpp b/experimental/Intersection/CubicBezierClip.cpp
index d0141e9..378e45f 100644
--- a/experimental/Intersection/CubicBezierClip.cpp
+++ b/experimental/Intersection/CubicBezierClip.cpp
@@ -7,7 +7,6 @@
 #include "CurveIntersection.h"
 #include "CurveUtilities.h"
 #include "LineParameters.h"
-#include <algorithm> // used for std::swap
 
 // return false if unable to clip (e.g., unable to create implicit line)
 // caller should subdivide, or create degenerate if the values are too small
@@ -26,13 +25,14 @@
     }
 
     double distance[2];
-    endLine.controlPtDistance(cubic1, distance);
+    distance[0] = endLine.controlPtDistance(cubic1, 1);
+    distance[1] = endLine.controlPtDistance(cubic1, 2);
 
     // find fat line
     double top = distance[0];
     double bottom = distance[1];
     if (top > bottom) {
-        std::swap(top, bottom);
+        SkTSwap(top, bottom);
     }
     if (top * bottom >= 0) {
         const double scale = 3/4.0; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf (13)
@@ -87,4 +87,3 @@
 
     return minT < maxT; // returns false if distance shows no intersection
 }
-
diff --git a/experimental/Intersection/CubicBezierClip_Test.cpp b/experimental/Intersection/CubicBezierClip_Test.cpp
index 2f1e3c6..1a9092f 100644
--- a/experimental/Intersection/CubicBezierClip_Test.cpp
+++ b/experimental/Intersection/CubicBezierClip_Test.cpp
@@ -16,10 +16,10 @@
         int order1 = reduceOrder(cubic1, reduce1, kReduceOrder_NoQuadraticsAllowed);
         int order2 = reduceOrder(cubic2, reduce2, kReduceOrder_NoQuadraticsAllowed);
         if (order1 < 4) {
-            printf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1);
+            SkDebugf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1);
         }
         if (order2 < 4) {
-            printf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2);
+            SkDebugf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2);
         }
         if (order1 == 4 && order2 == 4) {
             double minT = 0;
diff --git a/experimental/Intersection/CubicIntersection.cpp b/experimental/Intersection/CubicIntersection.cpp
index fcd4119..bedbfa1 100644
--- a/experimental/Intersection/CubicIntersection.cpp
+++ b/experimental/Intersection/CubicIntersection.cpp
@@ -4,10 +4,13 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
+#include "CubicUtilities.h"
 #include "CurveIntersection.h"
 #include "Intersections.h"
 #include "IntersectionUtilities.h"
 #include "LineIntersection.h"
+#include "LineUtilities.h"
 
 static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
 
@@ -159,3 +162,282 @@
     return c.intersect();
 }
 
+#include "CubicUtilities.h"
+
+static void cubicTangent(const Cubic& cubic, double t, _Line& tangent, _Point& pt, _Point& dxy) {
+    xy_at_t(cubic, t, tangent[0].x, tangent[0].y);
+    pt = tangent[1] = tangent[0];
+    dxdy_at_t(cubic, t, dxy);
+    tangent[0] -= dxy;
+    tangent[1] += dxy;
+}
+
+static double cubicDelta(const _Point& dxy, _Line& tangent, double scale)  {
+    double tangentLen = dxy.length();
+    tangent[0] -= tangent[1];
+    double intersectLen = tangent[0].length();
+    double result = intersectLen / tangentLen + scale;
+    return result;
+}
+
+// FIXME: after testing, make this static
+void computeDelta(const Cubic& c1, double t1, double scale1, const Cubic& c2, double t2,
+        double scale2, double& delta1, double& delta2) {
+    _Line tangent1, tangent2, line1, line2;
+    _Point dxy1, dxy2;
+    cubicTangent(c1, t1, line1, tangent1[0], dxy1);
+    cubicTangent(c2, t2, line2, tangent2[0], dxy2);
+    double range1[2], range2[2];
+    int found = intersect(line1, line2, range1, range2);
+    if (found == 0) {
+        range1[0] = 0.5;
+    } else {
+        SkASSERT(found == 1);
+    }
+    xy_at_t(line1, range1[0], tangent1[1].x, tangent1[1].y);
+#if SK_DEBUG
+    if (found == 1) {
+        xy_at_t(line2, range2[0], tangent2[1].x, tangent2[1].y);
+        SkASSERT(tangent2[1].approximatelyEqual(tangent1[1]));
+    }
+#endif
+    tangent2[1] = tangent1[1];
+    delta1 = cubicDelta(dxy1, tangent1, scale1 / precisionUnit);
+    delta2 = cubicDelta(dxy2, tangent2, scale2 / precisionUnit);
+}
+
+#if SK_DEBUG
+int debugDepth;
+#endif
+
+// this flavor approximates the cubics with quads to find the intersecting ts
+// OPTIMIZE: if this strategy proves successful, the quad approximations, or the ts used
+// to create the approximations, could be stored in the cubic segment
+// FIXME: this strategy needs to intersect the convex hull on either end with the opposite to
+// account for inset quadratics that cause the endpoint intersection to avoid detection
+// the segments can be very short -- the length of the maximum quadratic error (precision)
+// FIXME: this needs to recurse on itself, taking a range of T values and computing the new
+// t range ala is linear inner. The range can be figured by taking the dx/dy and determining
+// the fraction that matches the precision. That fraction is the change in t for the smaller cubic.
+static bool intersect2(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2,
+        double t2s, double t2e, double precisionScale, Intersections& i) {
+    Cubic c1, c2;
+    sub_divide(cubic1, t1s, t1e, c1);
+    sub_divide(cubic2, t2s, t2e, c2);
+    SkTDArray<double> ts1;
+    cubic_to_quadratics(c1, calcPrecision(c1) * precisionScale, ts1);
+    SkTDArray<double> ts2;
+    cubic_to_quadratics(c2, calcPrecision(c2) * precisionScale, ts2);
+    double t1Start = t1s;
+    int ts1Count = ts1.count();
+    for (int i1 = 0; i1 <= ts1Count; ++i1) {
+        const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1;
+        const double t1 = t1s + (t1e - t1s) * tEnd1;
+        Cubic part1;
+        sub_divide(cubic1, t1Start, t1, part1);
+        Quadratic q1;
+        demote_cubic_to_quad(part1, q1);
+  //      start here;
+        // should reduceOrder be looser in this use case if quartic is going to blow up on an
+        // extremely shallow quadratic?
+        Quadratic s1;
+        int o1 = reduceOrder(q1, s1);
+        double t2Start = t2s;
+        int ts2Count = ts2.count();
+        for (int i2 = 0; i2 <= ts2Count; ++i2) {
+            const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1;
+            const double t2 = t2s + (t2e - t2s) * tEnd2;
+            Cubic part2;
+            sub_divide(cubic2, t2Start, t2, part2);
+            Quadratic q2;
+            demote_cubic_to_quad(part2, q2);
+            Quadratic s2;
+            double o2 = reduceOrder(q2, s2);
+            Intersections locals;
+            if (o1 == 3 && o2 == 3) {
+                intersect2(q1, q2, locals);
+            } else if (o1 <= 2 && o2 <= 2) {
+                locals.fUsed = intersect((const _Line&) s1, (const _Line&) s2, locals.fT[0],
+                        locals.fT[1]);
+            } else if (o1 == 3 && o2 <= 2) {
+                intersect(q1, (const _Line&) s2, locals);
+            } else {
+                SkASSERT(o1 <= 2 && o2 == 3);
+                intersect(q2, (const _Line&) s1, locals);
+                for (int s = 0; s < locals.fUsed; ++s) {
+                    SkTSwap(locals.fT[0][s], locals.fT[1][s]);
+                }
+            }
+            for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
+                double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx];
+                double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx];
+    // if the computed t is not sufficiently precise, iterate
+                _Point p1, p2;
+                xy_at_t(cubic1, to1, p1.x, p1.y);
+                xy_at_t(cubic2, to2, p2.x, p2.y);
+                if (p1.approximatelyEqual(p2)) {
+                    i.insert(i.swapped() ? to2 : to1, i.swapped() ? to1 : to2);
+                } else {
+                    double dt1, dt2;
+                    computeDelta(cubic1, to1, (t1e - t1s), cubic2, to2, (t2e - t2s), dt1, dt2);
+                    double scale = precisionScale;
+                    if (dt1 > 0.125 || dt2 > 0.125) {
+                        scale /= 2;
+                        SkDebugf("%s scale=%1.9g\n", __FUNCTION__, scale);
+                    }
+#if SK_DEBUG
+                    ++debugDepth;
+                    SkASSERT(debugDepth < 10);
+#endif
+                    i.swap();
+                    intersect2(cubic2, SkTMax(to2 - dt2, 0.), SkTMin(to2 + dt2, 1.),
+                            cubic1, SkTMax(to1 - dt1, 0.), SkTMin(to1 + dt1, 1.), scale, i);
+                    i.swap();
+#if SK_DEBUG
+                    --debugDepth;
+#endif
+                }
+            }
+            t2Start = t2;
+        }
+        t1Start = t1;
+    }
+    return i.intersected();
+}
+
+static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, const _Rect& bounds2,
+        Intersections& i) {
+    _Line line1;
+    line1[0] = line1[1] = cubic1[start ? 0 : 3];
+    _Point dxy1 = line1[0] - cubic1[start ? 1 : 2];
+    dxy1 /= precisionUnit;
+    line1[1] += dxy1;
+    _Rect line1Bounds;
+    line1Bounds.setBounds(line1);
+    if (!bounds2.intersects(line1Bounds)) {
+        return false;
+    }
+    _Line line2;
+    line2[0] = line2[1] = line1[0];
+    _Point dxy2 = line2[0] - cubic1[start ? 3 : 0];
+    dxy2 /= precisionUnit;
+    line2[1] += dxy2;
+#if 0 // this is so close to the first bounds test it isn't worth the short circuit test
+    _Rect line2Bounds;
+    line2Bounds.setBounds(line2);
+    if (!bounds2.intersects(line2Bounds)) {
+        return false;
+    }
+#endif
+    Intersections local1;
+    if (!intersect(cubic2, line1, local1)) {
+        return false;
+    }
+    Intersections local2;
+    if (!intersect(cubic2, line2, local2)) {
+        return false;
+    }
+    double tMin, tMax;
+    tMin = tMax = local1.fT[0][0];
+    for (int index = 1; index < local1.fUsed; ++index) {
+        tMin = SkTMin(tMin, local1.fT[0][index]);
+        tMax = SkTMax(tMax, local1.fT[0][index]);
+    }
+    for (int index = 1; index < local2.fUsed; ++index) {
+        tMin = SkTMin(tMin, local2.fT[0][index]);
+        tMax = SkTMax(tMax, local2.fT[0][index]);
+    }
+#if SK_DEBUG
+    debugDepth = 0;
+#endif
+    return intersect2(cubic1, start ? 0 : 1, start ? 1.0 / precisionUnit : 1 - 1.0 / precisionUnit,
+            cubic2, tMin, tMax, 1, i);
+}
+
+// FIXME: add intersection of convex null on cubics' ends with the opposite cubic. The hull line
+// segments can be constructed to be only as long as the calculated precision suggests. If the hull
+// line segments intersect the cubic, then use the intersections to construct a subdivision for
+// quadratic curve fitting.
+bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) {
+#if SK_DEBUG
+    debugDepth = 0;
+#endif
+    bool result = intersect2(c1, 0, 1, c2, 0, 1, 1, i);
+    // FIXME: pass in cached bounds from caller
+    _Rect c1Bounds, c2Bounds;
+    c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
+    c2Bounds.setBounds(c2);
+    result |= intersectEnd(c1, false, c2, c2Bounds, i);
+    result |= intersectEnd(c1, true, c2, c2Bounds, i);
+    i.swap();
+    result |= intersectEnd(c2, false, c1, c1Bounds, i);
+    result |= intersectEnd(c2, true, c1, c1Bounds, i);
+    i.swap();
+    return result;
+}
+
+int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& i) {
+    SkTDArray<double> ts;
+    double precision = calcPrecision(cubic);
+    cubic_to_quadratics(cubic, precision, ts);
+    double tStart = 0;
+    Cubic part;
+    int tsCount = ts.count();
+    for (int idx = 0; idx <= tsCount; ++idx) {
+        double t = idx < tsCount ? ts[idx] : 1;
+        Quadratic q1;
+        sub_divide(cubic, tStart, t, part);
+        demote_cubic_to_quad(part, q1);
+        Intersections locals;
+        intersect2(q1, quad, locals);
+        for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
+            double globalT = tStart + (t - tStart) * locals.fT[0][tIdx];
+            i.insertOne(globalT, 0);
+            globalT = locals.fT[1][tIdx];
+            i.insertOne(globalT, 1);
+        }
+        tStart = t;
+    }
+    return i.used();
+}
+
+bool intersect(const Cubic& cubic, Intersections& i) {
+    SkTDArray<double> ts;
+    double precision = calcPrecision(cubic);
+    cubic_to_quadratics(cubic, precision, ts);
+    int tsCount = ts.count();
+    if (tsCount == 1) {
+        return false;
+    }
+    double t1Start = 0;
+    Cubic part;
+    for (int idx = 0; idx < tsCount; ++idx) {
+        double t1 = ts[idx];
+        Quadratic q1;
+        sub_divide(cubic, t1Start, t1, part);
+        demote_cubic_to_quad(part, q1);
+        double t2Start = t1;
+        for (int i2 = idx + 1; i2 <= tsCount; ++i2) {
+            const double t2 = i2 < tsCount ? ts[i2] : 1;
+            Quadratic q2;
+            sub_divide(cubic, t2Start, t2, part);
+            demote_cubic_to_quad(part, q2);
+            Intersections locals;
+            intersect2(q1, q2, locals);
+            for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
+            // discard intersections at cusp? (maximum curvature)
+                double t1sect = locals.fT[0][tIdx];
+                double t2sect = locals.fT[1][tIdx];
+                if (idx + 1 == i2 && t1sect == 1 && t2sect == 0) {
+                    continue;
+                }
+                double to1 = t1Start + (t1 - t1Start) * t1sect;
+                double to2 = t2Start + (t2 - t2Start) * t2sect;
+                i.insert(to1, to2);
+            }
+            t2Start = t2;
+        }
+        t1Start = t1;
+    }
+    return i.intersected();
+}
diff --git a/experimental/Intersection/CubicIntersection_Test.cpp b/experimental/Intersection/CubicIntersection_Test.cpp
index 983c4e3..d6816e3 100644
--- a/experimental/Intersection/CubicIntersection_Test.cpp
+++ b/experimental/Intersection/CubicIntersection_Test.cpp
@@ -45,14 +45,359 @@
             double tt2 = tIntersections.fT[1][pt];
             double tx2, ty2;
             xy_at_t(cubic2, tt2, tx2, ty2);
-            if (!approximately_equal(tx1, tx2)) {
+            if (!AlmostEqualUlps(tx1, tx2)) {
                 printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                     __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
             }
-            if (!approximately_equal(ty1, ty2)) {
+            if (!AlmostEqualUlps(ty1, ty2)) {
                 printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                     __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
             }
         }
     }
 }
+
+#define ONE_OFF_DEBUG 0
+
+static void oneOff(const Cubic& cubic1, const Cubic& cubic2) {
+    SkTDArray<Quadratic> quads1;
+    cubic_to_quadratics(cubic1, calcPrecision(cubic1), quads1);
+#if ONE_OFF_DEBUG
+    for (int index = 0; index < quads1.count(); ++index) {
+        const Quadratic& q = quads1[index];
+        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].x, q[0].y,
+                 q[1].x, q[1].y,  q[2].x, q[2].y);
+    }
+    SkDebugf("\n");
+#endif
+    SkTDArray<Quadratic> quads2;
+    cubic_to_quadratics(cubic2, calcPrecision(cubic2), quads2);
+#if ONE_OFF_DEBUG
+    for (int index = 0; index < quads2.count(); ++index) {
+        const Quadratic& q = quads2[index];
+        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].x, q[0].y,
+                 q[1].x, q[1].y,  q[2].x, q[2].y);
+    }
+    SkDebugf("\n");
+#endif
+    Intersections intersections2;
+    intersect2(cubic1, cubic2, intersections2);
+    for (int pt = 0; pt < intersections2.used(); ++pt) {
+        double tt1 = intersections2.fT[0][pt];
+        _Point xy1, xy2;
+        xy_at_t(cubic1, tt1, xy1.x, xy1.y);
+        int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
+        double tt2 = intersections2.fT[1][pt2];
+        xy_at_t(cubic2, tt2, xy2.x, xy2.y);
+#if ONE_OFF_DEBUG
+        SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
+            tt1, xy1.x, xy1.y, xy2.x, xy2.y, tt2);
+#endif
+        SkASSERT(xy1.approximatelyEqual(xy2));
+    }
+}
+
+static const Cubic testSet[] = {
+{{0, 1}, {0, 2}, {1, 0}, {1, 0}},
+{{0, 1}, {0, 1}, {1, 0}, {2, 0}},
+
+{{0, 0}, {0, 1}, {1, 1}, {1, 0}},
+{{1, 0}, {0, 0}, {0, 1}, {1, 1}},
+
+{{95.837747722788592, 45.025976907939643}, {16.564570095652982, 0.72959763963222402}, {63.209855865319199, 68.047528419665767}, {57.640240647662544, 59.524565264361243}},
+{{51.593891741518817, 38.53849970667553}, {62.34752929878772, 74.924924725166022}, {74.810149322641152, 34.17966562983564}, {29.368398119401373, 94.66719277886078}},
+
+{{39.765160968417838, 33.060396198677083}, {5.1922921581157908, 66.854301452103215}, {31.619281802149157, 25.269248720849514}, {81.541621071073038, 70.025341524754353}},
+{{46.078911165743556, 48.259962651999651}, {20.24450549867214, 49.403916182650214}, {0.26325131778756683, 24.46489805563581}, {15.915006546264051, 83.515023059917155}},
+
+{{65.454505973241524, 93.881892270353575}, {45.867360264932437, 92.723972719499827}, {2.1464054482739447, 74.636369140183717}, {33.774068594804994, 40.770872887582925}},
+{{72.963387832494163, 95.659300729473728}, {11.809496633619768, 82.209921247423594}, {13.456139067865974, 57.329313623406605}, {36.060621606214262, 70.867335643091849}},
+
+{{32.484981432782945, 75.082940782924624}, {42.467313093350882, 48.131159948246157}, {3.5963115764764657, 43.208665839959245}, {79.442476890721579, 89.709102357602262}},
+{{18.98573861410177, 93.308887208490106}, {40.405250173250792, 91.039661826118675}, {8.0467721950480584, 42.100282172719147}, {40.883324221187891, 26.030185504830527}},
+
+{{7.5374809128872498, 82.441702896003477}, {22.444346930107265, 22.138854312775123}, {66.76091829629658, 50.753805856571446}, {78.193478508942519, 97.7932997968948}},
+{{97.700573130371311, 53.53260215070685}, {87.72443481149358, 84.575876772671876}, {19.215031396232092, 47.032676472809484}, {11.989686410869325, 10.659507480757082}},
+
+{{26.192053931854691, 9.8504326817814416}, {10.174241480498686, 98.476562741434464}, {21.177712558385782, 33.814968789841501}, {75.329030899018534, 55.02231980442177}},
+{{56.222082700683771, 24.54395039218662}, {95.589995289030483, 81.050822735322086}, {28.180450866082897, 28.837706255185282}, {60.128952916771617, 87.311672180570511}},
+
+{{42.449716172390481, 52.379709366885805}, {27.896043159019225, 48.797373636065686}, {92.770268299044233, 89.899302036454571}, {12.102066544863426, 99.43241951960718}},
+{{45.77532924980639, 45.958701495993274}, {37.458701356062065, 68.393691335056758}, {37.569326692060258, 27.673713456687381}, {60.674866037757539, 62.47349659096146}},
+
+{{67.426548091427676, 37.993772624988935}, {23.483695892376684, 90.476863174921306}, {35.597065061143162, 79.872482633158796}, {75.38634169631932, 18.244890038969412}},
+{{61.336508189019057, 82.693132843213675}, {44.639380902349664, 54.074825790745592}, {16.815615499771951, 20.049704667203923}, {41.866884958868326, 56.735503699973002}},
+
+{{67.4265481, 37.9937726}, {23.4836959, 90.4768632}, {35.5970651, 79.8724826}, {75.3863417, 18.24489}},
+{{61.3365082, 82.6931328}, {44.6393809, 54.0748258}, {16.8156155, 20.0497047}, {41.866885, 56.7355037}},
+
+{{18.1312339, 31.6473732}, {95.5711034, 63.5350219}, {92.3283165, 62.0158945}, {18.5656052, 32.1268808}},
+{{97.402018, 35.7169972}, {33.1127443, 25.8935163}, {1.13970027, 54.9424981}, {56.4860195, 60.529264}},
+};
+
+const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
+
+void CubicIntersection_OneOffTest() {
+    for (size_t outer = 0; outer < testSetCount - 1; ++outer) {
+#if ONE_OFF_DEBUG
+        SkDebugf("%s quads1[%d]\n", __FUNCTION__, outer);
+#endif
+        const Cubic& cubic1 = testSet[outer];
+        for (size_t inner = outer + 1; inner < testSetCount; ++inner) {
+#if ONE_OFF_DEBUG
+        SkDebugf("%s quads2[%d]\n", __FUNCTION__, inner);
+#endif
+            const Cubic& cubic2 = testSet[inner];
+            oneOff(cubic1, cubic2);
+        }
+    }
+}
+
+#define DEBUG_CRASH 1
+
+class CubicChopper {
+public:
+
+// only finds one intersection
+CubicChopper(const Cubic& c1, const Cubic& c2)
+    : cubic1(c1)
+    , cubic2(c2)
+    , depth(0) {
+}
+
+bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
+    Cubic sub1, sub2;
+    // FIXME: carry last subdivide and reduceOrder result with cubic
+    sub_divide(cubic1, minT1, maxT1, sub1);
+    sub_divide(cubic2, minT2, maxT2, sub2);
+    Intersections i;
+    intersect2(sub1, sub2, i);
+    if (i.used() == 0) {
+        return false;
+    }
+    double x1, y1, x2, y2;
+    t1 = minT1 + i.fT[0][0] * (maxT1 - minT1);
+    t2 = minT2 + i.fT[1][0] * (maxT2 - minT2);
+    xy_at_t(cubic1, t1, x1, y1);
+    xy_at_t(cubic2, t2, x2, y2);
+    if (AlmostEqualUlps(x1, x2) && AlmostEqualUlps(y1, y2)) {
+        return true;
+    }
+    double half1 = (minT1 + maxT1) / 2;
+    double half2 = (minT2 + maxT2) / 2;
+    ++depth;
+    bool result;
+    if (depth & 1) {
+        result = intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2)
+            || intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2);
+    } else {
+        result = intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2)
+            || intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2);
+    }
+    --depth;
+    return result;
+}
+
+const Cubic& cubic1;
+const Cubic& cubic2;
+double t1;
+double t2;
+int depth;
+};
+
+#define TRY_OLD 0 // old way fails on test == 1
+
+void CubicIntersection_RandTestOld() {
+    srand(0);
+    const int tests = 1000000; // 10000000;
+    double largestFactor = DBL_MAX;
+    for (int test = 0; test < tests; ++test) {
+        Cubic cubic1, cubic2;
+        for (int i = 0; i < 4; ++i) {
+            cubic1[i].x = (double) rand() / RAND_MAX * 100;
+            cubic1[i].y = (double) rand() / RAND_MAX * 100;
+            cubic2[i].x = (double) rand() / RAND_MAX * 100;
+            cubic2[i].y = (double) rand() / RAND_MAX * 100;
+        }
+        if (test == 2513) { // the pair crosses three times, but the quadratic approximation
+            continue; // only sees one -- should be OK to ignore the other two?
+        }
+        if (test == 12932) { // this exposes a weakness when one cubic touches the other but
+            continue; // does not touch the quad approximation. Captured in qc.htm as cubic15
+        }
+    #if DEBUG_CRASH
+        char str[1024];
+        sprintf(str, "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n"
+            "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
+                cubic1[0].x, cubic1[0].y,  cubic1[1].x, cubic1[1].y, cubic1[2].x, cubic1[2].y,
+                cubic1[3].x, cubic1[3].y,
+                cubic2[0].x, cubic2[0].y,  cubic2[1].x, cubic2[1].y, cubic2[2].x, cubic2[2].y,
+                cubic2[3].x, cubic2[3].y);
+    #endif
+        _Rect rect1, rect2;
+        rect1.setBounds(cubic1);
+        rect2.setBounds(cubic2);
+        bool boundsIntersect = rect1.left <= rect2.right && rect2.left <= rect2.right
+                && rect1.top <= rect2.bottom && rect2.top <= rect1.bottom;
+        Intersections i1, i2;
+    #if TRY_OLD
+        bool oldIntersects = intersect(cubic1, cubic2, i1);
+    #else
+        bool oldIntersects = false;
+    #endif
+        if (test == -1) {
+            SkDebugf("ready...\n");
+        }
+        bool newIntersects = intersect2(cubic1, cubic2, i2);
+        if (!boundsIntersect && (oldIntersects || newIntersects)) {
+            SkDebugf("%s %d unexpected intersection boundsIntersect=%d oldIntersects=%d"
+                    " newIntersects=%d\n%s %s\n", __FUNCTION__, test, boundsIntersect,
+                    oldIntersects, newIntersects, __FUNCTION__, str);
+            SkASSERT(0);
+        }
+        if (oldIntersects && !newIntersects) {
+            SkDebugf("%s %d missing intersection oldIntersects=%d newIntersects=%d\n%s %s\n",
+                    __FUNCTION__, test, oldIntersects, newIntersects, __FUNCTION__, str);
+            SkASSERT(0);
+        }
+        if (!oldIntersects && !newIntersects) {
+            continue;
+        }
+        if (i2.used() > 1) {
+            continue;
+            // just look at single intercepts for simplicity
+        }
+        Intersections self1, self2; // self-intersect checks
+        if (intersect(cubic1, self1)) {
+            continue;
+        }
+        if (intersect(cubic2, self2)) {
+            continue;
+        }
+        // binary search for range necessary to enclose real intersection
+        CubicChopper c(cubic1, cubic2);
+        bool result = c.intersect(0, 1, 0, 1);
+        if (!result) {
+            // FIXME: a failure here probably means that a core routine used by CubicChopper is failing
+            continue;
+        }
+        double delta1 = fabs(c.t1 - i2.fT[0][0]);
+        double delta2 = fabs(c.t2 - i2.fT[1][0]);
+        double calc1 = calcPrecision(cubic1);
+        double calc2 = calcPrecision(cubic2);
+        double factor1 = calc1 / delta1;
+        double factor2 = calc2 / delta2;
+        SkDebugf("%s %d calc1=%1.9g delta1=%1.9g factor1=%1.9g calc2=%1.9g delta2=%1.9g"
+                " factor2=%1.9g\n", __FUNCTION__, test,
+                calc1, delta1, factor1, calc2, delta2, factor2);
+        if (factor1 < largestFactor) {
+            SkDebugf("WE HAVE A WINNER! %1.9g\n", factor1);
+            SkDebugf("%s\n", str);
+            oneOff(cubic1, cubic2);
+            largestFactor = factor1;
+        }
+        if (factor2 < largestFactor) {
+            SkDebugf("WE HAVE A WINNER! %1.9g\n", factor2);
+            SkDebugf("%s\n", str);
+            oneOff(cubic1, cubic2);
+            largestFactor = factor2;
+        }
+    }
+}
+
+void CubicIntersection_RandTest() {
+    srand(0);
+    const int tests = 10000000;
+    for (int test = 0; test < tests; ++test) {
+        Cubic cubic1, cubic2;
+        for (int i = 0; i < 4; ++i) {
+            cubic1[i].x = (double) rand() / RAND_MAX * 100;
+            cubic1[i].y = (double) rand() / RAND_MAX * 100;
+            cubic2[i].x = (double) rand() / RAND_MAX * 100;
+            cubic2[i].y = (double) rand() / RAND_MAX * 100;
+        }
+    #if DEBUG_CRASH
+        char str[1024];
+        sprintf(str, "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n"
+            "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
+                cubic1[0].x, cubic1[0].y,  cubic1[1].x, cubic1[1].y, cubic1[2].x, cubic1[2].y,
+                cubic1[3].x, cubic1[3].y,
+                cubic2[0].x, cubic2[0].y,  cubic2[1].x, cubic2[1].y, cubic2[2].x, cubic2[2].y,
+                cubic2[3].x, cubic2[3].y);
+    #endif
+        _Rect rect1, rect2;
+        rect1.setBounds(cubic1);
+        rect2.setBounds(cubic2);
+        bool boundsIntersect = rect1.left <= rect2.right && rect2.left <= rect2.right
+                && rect1.top <= rect2.bottom && rect2.top <= rect1.bottom;
+        if (test == -1) {
+            SkDebugf("ready...\n");
+        }
+        Intersections intersections2;
+        bool newIntersects = intersect2(cubic1, cubic2, intersections2);
+        if (!boundsIntersect && newIntersects) {
+            SkDebugf("%s %d unexpected intersection boundsIntersect=%d "
+                    " newIntersects=%d\n%s %s\n", __FUNCTION__, test, boundsIntersect,
+                    newIntersects, __FUNCTION__, str);
+            SkASSERT(0);
+        }
+        for (int pt = 0; pt < intersections2.used(); ++pt) {
+            double tt1 = intersections2.fT[0][pt];
+            _Point xy1, xy2;
+            xy_at_t(cubic1, tt1, xy1.x, xy1.y);
+            int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
+            double tt2 = intersections2.fT[1][pt2];
+            xy_at_t(cubic2, tt2, xy2.x, xy2.y);
+        #if 0
+            SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
+                tt1, xy1.x, xy1.y, xy2.x, xy2.y, tt2);
+        #endif
+            SkASSERT(xy1.approximatelyEqual(xy2));
+        }
+    }
+}
+
+static Cubic deltaTestSet[] = {
+  {{1, 4}, {1, 4.*2/3}, {1, 4.*1/3}, {1, 0}},
+  {{0, 3}, {1, 2}, {2, 1}, {3, 0}},
+  {{1, 4}, {1, 4.*2/3}, {1, 4.*1/3}, {1, 0}},
+  {{3.5, 1}, {2.5, 2}, {1.5, 3}, {0.5, 4}}
+};
+
+size_t deltaTestSetLen = sizeof(deltaTestSet) / sizeof(deltaTestSet[0]);
+
+static double deltaTestSetT[] = {
+    3./8,
+    5./12,
+    6./8,
+    9./12
+};
+
+size_t deltaTestSetTLen = sizeof(deltaTestSetT) / sizeof(deltaTestSetT[0]);
+
+static double expectedT[] = {
+    0.5,
+    1./3,
+    1./8,
+    5./6
+};
+
+size_t expectedTLen = sizeof(expectedT) / sizeof(expectedT[0]);
+
+// FIXME: this test no longer valid -- does not take minimum scale contribution into account
+void CubicIntersection_ComputeDeltaTest() {
+    SkASSERT(deltaTestSetLen == deltaTestSetTLen);
+    SkASSERT(expectedTLen == deltaTestSetTLen);
+    for (size_t index = 0; index < deltaTestSetLen; index += 2) {
+        const Cubic& c1 = deltaTestSet[index];
+        const Cubic& c2 = deltaTestSet[index + 1];
+        double t1 = deltaTestSetT[index];
+        double t2 = deltaTestSetT[index + 1];
+        double d1, d2;
+        computeDelta(c1, t1, 1, c2, t2, 1, d1, d2);
+        SkASSERT(approximately_equal(t1 + d1, expectedT[index])
+            || approximately_equal(t1 - d1, expectedT[index]));
+        SkASSERT(approximately_equal(t2 + d2, expectedT[index + 1])
+            || approximately_equal(t2 - d2, expectedT[index + 1]));
+    }
+}
diff --git a/experimental/Intersection/CubicIntersection_TestData.cpp b/experimental/Intersection/CubicIntersection_TestData.cpp
index 645d7b0..c138a67 100644
--- a/experimental/Intersection/CubicIntersection_TestData.cpp
+++ b/experimental/Intersection/CubicIntersection_TestData.cpp
@@ -4,7 +4,6 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#define IN_TEST 1
 #include "CubicIntersection_TestData.h"
 #include <limits>
 
diff --git a/experimental/Intersection/CubicIntersection_TestData.h b/experimental/Intersection/CubicIntersection_TestData.h
index 27436f8..9893b3c 100644
--- a/experimental/Intersection/CubicIntersection_TestData.h
+++ b/experimental/Intersection/CubicIntersection_TestData.h
@@ -4,11 +4,8 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#if !defined(IN_TEST)
-    #define IN_TEST 1
-#endif
-
 #include "DataTypes.h"
+#include "DataTypes_Test.h"
 
 extern const Cubic pointDegenerates[];
 extern const Cubic notPointDegenerates[];
diff --git a/experimental/Intersection/CubicLineSegments.cpp b/experimental/Intersection/CubicLineSegments.cpp
index b7408a8..58b7b85 100644
--- a/experimental/Intersection/CubicLineSegments.cpp
+++ b/experimental/Intersection/CubicLineSegments.cpp
@@ -6,7 +6,6 @@
  */
 #include "CubicLineSegments.h"
 #include "QuadraticLineSegments.h"
-#include <algorithm> // used for std::max
 
 // http://cagd.cs.byu.edu/~557/text/cagd.pdf 2.7
 // A hodograph is the first derivative curve
@@ -31,8 +30,8 @@
 double subDivisions(const Cubic& cubic) {
     _Line hodo2;
     secondHodograph(cubic, hodo2);
-    double maxX = std::max(hodo2[1].x, hodo2[1].x);
-    double maxY = std::max(hodo2[1].y, hodo2[1].y);
+    double maxX = SkTMax(hodo2[1].x, hodo2[1].x);
+    double maxY = SkTMax(hodo2[1].y, hodo2[1].y);
     double dist = sqrt(maxX * maxX + maxY * maxY);
     double segments = sqrt(dist / (8 * FLT_EPSILON));
     return segments;
diff --git a/experimental/Intersection/CubicParameterization.cpp b/experimental/Intersection/CubicParameterization.cpp
index 2bae8b6..caa7c24 100644
--- a/experimental/Intersection/CubicParameterization.cpp
+++ b/experimental/Intersection/CubicParameterization.cpp
@@ -491,8 +491,7 @@
         if (first == index) {
             continue;
         }
-        if (!approximately_equal(p1[index] * p2[first],
-                p1[first] * p2[index])) {
+        if (!AlmostEqualUlps(p1[index] * p2[first], p1[first] * p2[index])) {
             return false;
         }
     }
@@ -517,5 +516,3 @@
 
 // unit test to return and validate parametric coefficients
 #include "CubicParameterization_TestUtility.cpp"
-
-
diff --git a/experimental/Intersection/CubicParameterization_Test.cpp b/experimental/Intersection/CubicParameterization_Test.cpp
index 114331a..cc095b6 100644
--- a/experimental/Intersection/CubicParameterization_Test.cpp
+++ b/experimental/Intersection/CubicParameterization_Test.cpp
@@ -4,6 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "QuadraticUtilities.h"
 #include "CurveIntersection.h"
 #include "Intersection_Tests.h"
 #include "Parameterization_Test.h"
@@ -33,19 +34,19 @@
         quad_to_cubic(split.second(), second);
         quad_to_cubic(midThird, mid);
         if (!implicit_matches(whole, first)) {
-            printf("%s-1 %d\n", __FUNCTION__, (int)index);
+            SkDebugf("%s-1 %d\n", __FUNCTION__, (int)index);
         }
         if (!implicit_matches(whole, second)) {
-            printf("%s-2 %d\n", __FUNCTION__, (int)index);
+            SkDebugf("%s-2 %d\n", __FUNCTION__, (int)index);
         }
         if (!implicit_matches(mid, first)) {
-            printf("%s-3 %d\n", __FUNCTION__, (int)index);
+            SkDebugf("%s-3 %d\n", __FUNCTION__, (int)index);
         }
         if (!implicit_matches(mid, second)) {
-            printf("%s-4 %d\n", __FUNCTION__, (int)index);
+            SkDebugf("%s-4 %d\n", __FUNCTION__, (int)index);
         }
         if (!implicit_matches(first, second)) {
-            printf("%s-5 %d\n", __FUNCTION__, (int)index);
+            SkDebugf("%s-5 %d\n", __FUNCTION__, (int)index);
         }
     }
 }
@@ -97,16 +98,16 @@
     for (size_t index = firstCubicParameterizationTest; index < cubics_count; ++index) {
         for (size_t inner = 0; inner < 4; inner += 3) {
             if (!point_on_parameterized_curve(cubics[index], cubics[index][inner])) {
-                    printf("%s [%zu,%zu] 1 parameterization failed\n",
+                    SkDebugf("%s [%zu,%zu] 1 parameterization failed\n",
                         __FUNCTION__, index, inner);
             }
             if (!point_on_parameterized_curve(cubics[index], cubics[index ^ 1][inner])) {
-                    printf("%s [%zu,%zu] 2 parameterization failed\n",
+                    SkDebugf("%s [%zu,%zu] 2 parameterization failed\n",
                         __FUNCTION__, index, inner);
             }
         }
         if (!implicit_matches(cubics[index], cubics[index ^ 1])) {
-            printf("%s %d\n", __FUNCTION__, (int)index);
+            SkDebugf("%s %d\n", __FUNCTION__, (int)index);
         }
     }
 }
diff --git a/experimental/Intersection/CubicReduceOrder.cpp b/experimental/Intersection/CubicReduceOrder.cpp
index 294d18c..29920ab 100644
--- a/experimental/Intersection/CubicReduceOrder.cpp
+++ b/experimental/Intersection/CubicReduceOrder.cpp
@@ -69,13 +69,13 @@
     double dx10 = cubic[1].x - cubic[0].x;
     double dx23 = cubic[2].x - cubic[3].x;
     double midX = cubic[0].x + dx10 * 3 / 2;
-    if (!approximately_equal(midX - cubic[3].x, dx23 * 3 / 2)) {
+    if (!AlmostEqualUlps(midX - cubic[3].x, dx23 * 3 / 2)) {
         return 0;
     }
     double dy10 = cubic[1].y - cubic[0].y;
     double dy23 = cubic[2].y - cubic[3].y;
     double midY = cubic[0].y + dy10 * 3 / 2;
-    if (!approximately_equal(midY - cubic[3].y, dy23 * 3 / 2)) {
+    if (!AlmostEqualUlps(midY - cubic[3].y, dy23 * 3 / 2)) {
         return 0;
     }
     reduction[0] = cubic[0];
@@ -92,8 +92,8 @@
     while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) {
         --endIndex;
         if (endIndex == 0) {
-            printf("%s shouldn't get here if all four points are about equal", __FUNCTION__);
-            assert(0);
+            printf("%s shouldn't get here if all four points are about equal\n", __FUNCTION__);
+            SkASSERT(0);
         }
     }
     if (!isLinear(cubic, startIndex, endIndex)) {
@@ -149,22 +149,14 @@
 bool isLinear(const Cubic& cubic, int startIndex, int endIndex) {
     LineParameters lineParameters;
     lineParameters.cubicEndPoints(cubic, startIndex, endIndex);
-    double normalSquared = lineParameters.normalSquared();
-    double distance[2]; // distance is not normalized
-    int mask = other_two(startIndex, endIndex);
-    int inner1 = startIndex ^ mask;
-    int inner2 = endIndex ^ mask;
-    lineParameters.controlPtDistance(cubic, inner1, inner2, distance);
-    double limit = normalSquared;
-    int index;
-    for (index = 0; index < 2; ++index) {
-        double distSq = distance[index];
-        distSq *= distSq;
-        if (approximately_greater(distSq, limit)) {
-            return false;
-        }
+    // FIXME: maybe it's possible to avoid this and compare non-normalized
+    lineParameters.normalize();
+    double distance = lineParameters.controlPtDistance(cubic, 1);
+    if (!approximately_zero(distance)) {
+        return false;
     }
-    return true;
+    distance = lineParameters.controlPtDistance(cubic, 2);
+    return approximately_zero(distance);
 }
 
 /* food for thought:
@@ -213,10 +205,10 @@
         }
     }
     for (index = 0; index < 4; ++index) {
-        if (approximately_equal(cubic[index].x, cubic[minX].x)) {
+        if (AlmostEqualUlps(cubic[index].x, cubic[minX].x)) {
             minXSet |= 1 << index;
         }
-        if (approximately_equal(cubic[index].y, cubic[minY].y)) {
+        if (AlmostEqualUlps(cubic[index].y, cubic[minY].y)) {
             minYSet |= 1 << index;
         }
     }
diff --git a/experimental/Intersection/CubicReduceOrder_Test.cpp b/experimental/Intersection/CubicReduceOrder_Test.cpp
index 2981156..1011fab 100644
--- a/experimental/Intersection/CubicReduceOrder_Test.cpp
+++ b/experimental/Intersection/CubicReduceOrder_Test.cpp
@@ -33,64 +33,64 @@
     run = RunComputedLines;
     firstTestIndex = 18;
 #endif
-    int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates ? firstTestIndex : INT_MAX;
-    int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates ? firstTestIndex : INT_MAX;
-    int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : INT_MAX;
-    int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : INT_MAX;
-    int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines ? firstTestIndex : INT_MAX;
-    int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines ? firstTestIndex : INT_MAX;
-    int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines ? firstTestIndex : INT_MAX;
-    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : INT_MAX;
-    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : INT_MAX;
-    int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines ? firstTestIndex : INT_MAX;
+    int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates ? firstTestIndex : SK_MaxS32;
+    int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates ? firstTestIndex : SK_MaxS32;
+    int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32;
+    int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32;
+    int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines ? firstTestIndex : SK_MaxS32;
+    int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines ? firstTestIndex : SK_MaxS32;
+    int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines ? firstTestIndex : SK_MaxS32;
+    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : SK_MaxS32;
+    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : SK_MaxS32;
+    int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines ? firstTestIndex : SK_MaxS32;
 
     for (index = firstPointDegeneratesTest; index < pointDegenerates_count; ++index) {
         const Cubic& cubic = pointDegenerates[index];
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order != 1) {
-            printf("[%d] pointDegenerates order=%d\n", (int) index, order);
+            SkDebugf("[%d] pointDegenerates order=%d\n", (int) index, order);
         }
     }
     for (index = firstNotPointDegeneratesTest; index < notPointDegenerates_count; ++index) {
         const Cubic& cubic = notPointDegenerates[index];
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order == 1) {
-            printf("[%d] notPointDegenerates order=%d\n", (int) index, order);
+            SkDebugf("[%d] notPointDegenerates order=%d\n", (int) index, order);
         }
     }
     for (index = firstLinesTest; index < lines_count; ++index) {
         const Cubic& cubic = lines[index];
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order != 2) {
-            printf("[%d] lines order=%d\n", (int) index, order);
+            SkDebugf("[%d] lines order=%d\n", (int) index, order);
         }
     }
     for (index = firstNotLinesTest; index < notLines_count; ++index) {
         const Cubic& cubic = notLines[index];
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order == 2) {
-            printf("[%d] notLines order=%d\n", (int) index, order);
+            SkDebugf("[%d] notLines order=%d\n", (int) index, order);
         }
     }
     for (index = firstModEpsilonTest; index < modEpsilonLines_count; ++index) {
         const Cubic& cubic = modEpsilonLines[index];
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order == 2) {
-            printf("[%d] line mod by epsilon order=%d\n", (int) index, order);
+            SkDebugf("[%d] line mod by epsilon order=%d\n", (int) index, order);
         }
     }
     for (index = firstLessEpsilonTest; index < lessEpsilonLines_count; ++index) {
         const Cubic& cubic = lessEpsilonLines[index];
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order != 2) {
-            printf("[%d] line less by epsilon/2 order=%d\n", (int) index, order);
+            SkDebugf("[%d] line less by epsilon/2 order=%d\n", (int) index, order);
         }
     }
     for (index = firstNegEpsilonTest; index < negEpsilonLines_count; ++index) {
         const Cubic& cubic = negEpsilonLines[index];
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order != 2) {
-            printf("[%d] line neg by epsilon/2 order=%d\n", (int) index, order);
+            SkDebugf("[%d] line neg by epsilon/2 order=%d\n", (int) index, order);
         }
     }
     for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
@@ -99,7 +99,7 @@
         quad_to_cubic(quad, cubic);
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order != 2) {
-            printf("[%d] line quad order=%d\n", (int) index, order);
+            SkDebugf("[%d] line quad order=%d\n", (int) index, order);
         }
     }
     for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
@@ -108,7 +108,7 @@
         quad_to_cubic(quad, cubic);
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (order != 3) {
-            printf("[%d] line mod quad order=%d\n", (int) index, order);
+            SkDebugf("[%d] line mod quad order=%d\n", (int) index, order);
         }
     }
 
@@ -118,25 +118,25 @@
         bool controlsInside = controls_inside(cubic);
         order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
         if (reduce[0].x == reduce[1].x && reduce[0].y == reduce[1].y) {
-            printf("[%d] line computed ends match order=%d\n", (int) index, order);
+            SkDebugf("[%d] line computed ends match order=%d\n", (int) index, order);
         }
         if (controlsInside) {
             if (       (reduce[0].x != cubic[0].x && reduce[0].x != cubic[3].x)
                     || (reduce[0].y != cubic[0].y && reduce[0].y != cubic[3].y)
                     || (reduce[1].x != cubic[0].x && reduce[1].x != cubic[3].x)
                     || (reduce[1].y != cubic[0].y && reduce[1].y != cubic[3].y)) {
-                printf("[%d] line computed ends order=%d\n", (int) index, order);
+                SkDebugf("[%d] line computed ends order=%d\n", (int) index, order);
             }
         } else {
             // binary search for extrema, compare against actual results
                 // while a control point is outside of bounding box formed by end points, split
             _Rect bounds = {DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX};
             find_tight_bounds(cubic, bounds);
-            if (       (!approximately_equal(reduce[0].x, bounds.left) && !approximately_equal(reduce[0].x, bounds.right))
-                    || (!approximately_equal(reduce[0].y, bounds.top) && !approximately_equal(reduce[0].y, bounds.bottom))
-                    || (!approximately_equal(reduce[1].x, bounds.left) && !approximately_equal(reduce[1].x, bounds.right))
-                    || (!approximately_equal(reduce[1].y, bounds.top) && !approximately_equal(reduce[1].y, bounds.bottom))) {
-                printf("[%d] line computed tight bounds order=%d\n", (int) index, order);
+            if (       (!AlmostEqualUlps(reduce[0].x, bounds.left) && !AlmostEqualUlps(reduce[0].x, bounds.right))
+                    || (!AlmostEqualUlps(reduce[0].y, bounds.top) && !AlmostEqualUlps(reduce[0].y, bounds.bottom))
+                    || (!AlmostEqualUlps(reduce[1].x, bounds.left) && !AlmostEqualUlps(reduce[1].x, bounds.right))
+                    || (!AlmostEqualUlps(reduce[1].y, bounds.top) && !AlmostEqualUlps(reduce[1].y, bounds.bottom))) {
+                SkDebugf("[%d] line computed tight bounds order=%d\n", (int) index, order);
             }
 
         }
diff --git a/experimental/Intersection/CubicSubDivide.cpp b/experimental/Intersection/CubicSubDivide.cpp
index e363a0d..b7cc127 100644
--- a/experimental/Intersection/CubicSubDivide.cpp
+++ b/experimental/Intersection/CubicSubDivide.cpp
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "CurveIntersection.h"
+#include "CubicUtilities.h"
 #include "IntersectionUtilities.h"
 
 /*
@@ -104,6 +104,21 @@
 
 void chop_at(const Cubic& src, CubicPair& dst, double t)
 {
+    if (t == 0.5) {
+        dst.pts[0] = src[0];
+        dst.pts[1].x = (src[0].x + src[1].x) / 2;
+        dst.pts[1].y = (src[0].y + src[1].y) / 2;
+        dst.pts[2].x = (src[0].x + 2 * src[1].x + src[2].x) / 4;
+        dst.pts[2].y = (src[0].y + 2 * src[1].y + src[2].y) / 4;
+        dst.pts[3].x = (src[0].x + 3 * (src[1].x + src[2].x) + src[3].x) / 8;
+        dst.pts[3].y = (src[0].y + 3 * (src[1].y + src[2].y) + src[3].y) / 8;
+        dst.pts[4].x = (src[1].x + 2 * src[2].x + src[3].x) / 4;
+        dst.pts[4].y = (src[1].y + 2 * src[2].y + src[3].y) / 4;
+        dst.pts[5].x = (src[2].x + src[3].x) / 2;
+        dst.pts[5].y = (src[2].y + src[3].y) / 2;
+        dst.pts[6] = src[3];
+        return;
+    }
     interp_cubic_coords(&src[0].x, &dst.pts[0].x, t);
     interp_cubic_coords(&src[0].y, &dst.pts[0].y, t);
 }
diff --git a/experimental/Intersection/CubicToQuadratics.cpp b/experimental/Intersection/CubicToQuadratics.cpp
new file mode 100644
index 0000000..66288b4
--- /dev/null
+++ b/experimental/Intersection/CubicToQuadratics.cpp
@@ -0,0 +1,176 @@
+/*
+http://stackoverflow.com/questions/2009160/how-do-i-convert-the-2-control-points-of-a-cubic-curve-to-the-single-control-poi
+*/
+
+/*
+Let's call the control points of the cubic Q0..Q3 and the control points of the quadratic P0..P2.
+Then for degree elevation, the equations are:
+
+Q0 = P0
+Q1 = 1/3 P0 + 2/3 P1
+Q2 = 2/3 P1 + 1/3 P2
+Q3 = P2
+In your case you have Q0..Q3 and you're solving for P0..P2. There are two ways to compute P1 from
+ the equations above:
+
+P1 = 3/2 Q1 - 1/2 Q0
+P1 = 3/2 Q2 - 1/2 Q3
+If this is a degree-elevated cubic, then both equations will give the same answer for P1. Since
+ it's likely not, your best bet is to average them. So,
+
+P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3
+
+
+Cubic defined by: P1/2 - anchor points, C1/C2 control points
+|x| is the euclidean norm of x
+mid-point approx of cubic: a quad that shares the same anchors with the cubic and has the
+ control point at C = (3·C2 - P2 + 3·C1 - P1)/4
+
+Algorithm
+
+pick an absolute precision (prec)
+Compute the Tdiv as the root of (cubic) equation
+sqrt(3)/18 · |P2 - 3·C2 + 3·C1 - P1|/2 · Tdiv ^ 3 = prec
+if Tdiv < 0.5 divide the cubic at Tdiv. First segment [0..Tdiv] can be approximated with by a
+ quadratic, with a defect less than prec, by the mid-point approximation.
+ Repeat from step 2 with the second resulted segment (corresponding to 1-Tdiv)
+0.5<=Tdiv<1 - simply divide the cubic in two. The two halves can be approximated by the mid-point
+ approximation
+Tdiv>=1 - the entire cubic can be approximated by the mid-point approximation
+
+confirmed by (maybe stolen from)
+http://www.caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
+// maybe in turn derived from  http://www.cccg.ca/proceedings/2004/36.pdf
+// also stored at http://www.cis.usouthal.edu/~hain/general/Publications/Bezier/bezier%20cccg04%20paper.pdf
+
+*/
+
+#include "CubicUtilities.h"
+#include "CurveIntersection.h"
+#include "LineIntersection.h"
+
+const bool AVERAGE_END_POINTS = true; // results in better fitting curves
+
+#define USE_CUBIC_END_POINTS 1
+
+static double calcTDiv(const Cubic& cubic, double precision, double start) {
+    const double adjust = sqrt(3) / 36;
+    Cubic sub;
+    const Cubic* cPtr;
+    if (start == 0) {
+        cPtr = &cubic;
+    } else {
+        // OPTIMIZE: special-case half-split ?
+        sub_divide(cubic, start, 1, sub);
+        cPtr = &sub;
+    }
+    const Cubic& c = *cPtr;
+    double dx = c[3].x - 3 * (c[2].x - c[1].x) - c[0].x;
+    double dy = c[3].y - 3 * (c[2].y - c[1].y) - c[0].y;
+    double dist = sqrt(dx * dx + dy * dy);
+    double tDiv3 = precision / (adjust * dist);
+    double t = cube_root(tDiv3);
+    if (start > 0) {
+        t = start + (1 - start) * t;
+    }
+    return t;
+}
+
+void demote_cubic_to_quad(const Cubic& cubic, Quadratic& quad) {
+    quad[0] = cubic[0];
+if (AVERAGE_END_POINTS) {
+    const _Point fromC1 = { (3 * cubic[1].x - cubic[0].x) / 2, (3 * cubic[1].y - cubic[0].y) / 2 };
+    const _Point fromC2 = { (3 * cubic[2].x - cubic[3].x) / 2, (3 * cubic[2].y - cubic[3].y) / 2 };
+    quad[1].x = (fromC1.x + fromC2.x) / 2;
+    quad[1].y = (fromC1.y + fromC2.y) / 2;
+} else {
+    lineIntersect((const _Line&) cubic[0], (const _Line&) cubic[2], quad[1]);
+}
+    quad[2] = cubic[3];
+}
+
+int cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<Quadratic>& quadratics) {
+    SkTDArray<double> ts;
+    cubic_to_quadratics(cubic, precision, ts);
+    int tsCount = ts.count();
+    double t1Start = 0;
+    int order = 0;
+    for (int idx = 0; idx <= tsCount; ++idx) {
+        double t1 = idx < tsCount ? ts[idx] : 1;
+        Cubic part;
+        sub_divide(cubic, t1Start, t1, part);
+        Quadratic q1;
+        demote_cubic_to_quad(part, q1);
+        Quadratic s1;
+        int o1 = reduceOrder(q1, s1);
+        if (order < o1) {
+            order = o1;
+        }
+        memcpy(quadratics.append(), o1 < 2 ? s1 : q1, sizeof(Quadratic));
+        t1Start = t1;
+    }
+    return order;
+}
+
+static bool addSimpleTs(const Cubic& cubic, double precision, SkTDArray<double>& ts) {
+    double tDiv = calcTDiv(cubic, precision, 0);
+    if (tDiv >= 1) {
+        return true;
+    }
+    if (tDiv >= 0.5) {
+        *ts.append() = 0.5;
+        return true;
+    }
+    return false;
+}
+
+static void addTs(const Cubic& cubic, double precision, double start, double end,
+        SkTDArray<double>& ts) {
+    double tDiv = calcTDiv(cubic, precision, 0);
+    double parts = ceil(1.0 / tDiv);
+    for (double index = 0; index < parts; ++index) {
+        double newT = start + (index / parts) * (end - start);
+        if (newT > 0 && newT < 1) {
+            *ts.append() = newT;
+        }
+    }
+}
+
+// flavor that returns T values only, deferring computing the quads until they are needed
+// FIXME: when called from recursive intersect 2, this could take the original cubic
+// and do a more precise job when calling chop at and sub divide by computing the fractional ts.
+// it would still take the prechopped cubic for reduce order and find cubic inflections
+void cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<double>& ts) {
+    Cubic reduced;
+    int order = reduceOrder(cubic, reduced, kReduceOrder_QuadraticsAllowed);
+    if (order < 3) {
+        return;
+    }
+    double inflectT[2];
+    int inflections = find_cubic_inflections(cubic, inflectT);
+    SkASSERT(inflections <= 2);
+    if (inflections == 0 && addSimpleTs(cubic, precision, ts)) {
+        return;
+    }
+    if (inflections == 1) {
+        CubicPair pair;
+        chop_at(cubic, pair, inflectT[0]);
+        addTs(pair.first(), precision, 0, inflectT[0], ts);
+        addTs(pair.second(), precision, inflectT[0], 1, ts);
+        return;
+    }
+    if (inflections == 2) {
+        if (inflectT[0] > inflectT[1]) {
+            SkTSwap(inflectT[0], inflectT[1]);
+        }
+        Cubic part;
+        sub_divide(cubic, 0, inflectT[0], part);
+        addTs(part, precision, 0, inflectT[0], ts);
+        sub_divide(cubic, inflectT[0], inflectT[1], part);
+        addTs(part, precision, inflectT[0], inflectT[1], ts);
+        sub_divide(cubic, inflectT[1], 1, part);
+        addTs(part, precision, inflectT[1], 1, ts);
+        return;
+    }
+    addTs(cubic, precision, 0, 1, ts);
+}
diff --git a/experimental/Intersection/CubicToQuadratics_Test.cpp b/experimental/Intersection/CubicToQuadratics_Test.cpp
new file mode 100644
index 0000000..9688164
--- /dev/null
+++ b/experimental/Intersection/CubicToQuadratics_Test.cpp
@@ -0,0 +1,255 @@
+#include "CubicIntersection_TestData.h"
+#include "CubicUtilities.h"
+#include "Intersection_Tests.h"
+#include "QuadraticIntersection_TestData.h"
+#include "TestUtilities.h"
+#include "SkGeometry.h"
+
+static void test(const Cubic* cubics, const char* name, int firstTest, size_t testCount) {
+    SkTDArray<Quadratic> quads;
+    for (size_t index = firstTest; index < testCount; ++index) {
+        const Cubic& cubic = cubics[index];
+        double precision = calcPrecision(cubic);
+        (void) cubic_to_quadratics(cubic, precision, quads);
+        if (quads.count() != 1) {
+            printf("%s [%d] cubic to quadratics failed count=%d\n", name, (int) index,
+                    quads.count());
+        }
+    }
+}
+
+static void test(const Quadratic* quadTests, const char* name, int firstTest, size_t testCount) {
+    SkTDArray<Quadratic> quads;
+    for (size_t index = firstTest; index < testCount; ++index) {
+        const Quadratic& quad = quadTests[index];
+        Cubic cubic;
+        quad_to_cubic(quad, cubic);
+        double precision = calcPrecision(cubic);
+        (void) cubic_to_quadratics(cubic, precision, quads);
+        if (quads.count() != 1) {
+            printf("%s [%d] cubic to quadratics failed count=%d\n", name, (int) index,
+                    quads.count());
+        }
+    }
+}
+
+static void testC(const Cubic* cubics, const char* name, int firstTest, size_t testCount) {
+    SkTDArray<Quadratic> quads;
+    // test if computed line end points are valid
+    for (size_t index = firstTest; index < testCount; ++index) {
+        const Cubic& cubic = cubics[index];
+        double precision = calcPrecision(cubic);
+        int order = cubic_to_quadratics(cubic, precision, quads);
+        SkASSERT(order != 4);
+        if (order < 3) {
+            continue;
+        }
+        if (!AlmostEqualUlps(cubic[0].x, quads[0][0].x)
+                || !AlmostEqualUlps(cubic[0].y, quads[0][0].y)) {
+            printf("[%d] unmatched start\n", (int) index);
+        }
+        int last = quads.count() - 1;
+        if (!AlmostEqualUlps(cubic[3].x, quads[last][2].x)
+                || !AlmostEqualUlps(cubic[3].y, quads[last][2].y)) {
+            printf("[%d] unmatched end\n", (int) index);
+        }
+    }
+}
+
+static void testC(const Cubic(* cubics)[2], const char* name, int firstTest, size_t testCount) {
+    SkTDArray<Quadratic> quads;
+    for (size_t index = firstTest; index < testCount; ++index) {
+        for (int idx2 = 0; idx2 < 2; ++idx2) {
+            const Cubic& cubic = cubics[index][idx2];
+            double precision = calcPrecision(cubic);
+            int order = cubic_to_quadratics(cubic, precision, quads);
+        SkASSERT(order != 4);
+        if (order < 3) {
+                continue;
+            }
+            if (!AlmostEqualUlps(cubic[0].x, quads[0][0].x)
+                    || !AlmostEqualUlps(cubic[0].y, quads[0][0].y)) {
+                printf("[%d][%d] unmatched start\n", (int) index, idx2);
+            }
+            int last = quads.count() - 1;
+            if (!AlmostEqualUlps(cubic[3].x, quads[last][2].x)
+                    || !AlmostEqualUlps(cubic[3].y, quads[last][2].y)) {
+                printf("[%d][%d] unmatched end\n", (int) index, idx2);
+            }
+        }
+    }
+}
+
+void CubicToQuadratics_Test() {
+    enum {
+        RunAll,
+        RunPointDegenerates,
+        RunNotPointDegenerates,
+        RunLines,
+        RunNotLines,
+        RunModEpsilonLines,
+        RunLessEpsilonLines,
+        RunNegEpsilonLines,
+        RunQuadraticLines,
+        RunQuadraticModLines,
+        RunComputedLines,
+        RunComputedTests,
+        RunNone
+    } run = RunAll;
+    int firstTestIndex = 0;
+#if 0
+    run = RunComputedLines;
+    firstTestIndex = 18;
+#endif
+    int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates ? firstTestIndex : SK_MaxS32;
+    int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates ? firstTestIndex : SK_MaxS32;
+    int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32;
+    int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32;
+    int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines ? firstTestIndex : SK_MaxS32;
+    int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines ? firstTestIndex : SK_MaxS32;
+    int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines ? firstTestIndex : SK_MaxS32;
+    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : SK_MaxS32;
+    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : SK_MaxS32;
+    int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines ? firstTestIndex : SK_MaxS32;
+    int firstComputedCubicsTest = run == RunAll ? 0 : run == RunComputedTests ? firstTestIndex : SK_MaxS32;
+
+    test(pointDegenerates, "pointDegenerates", firstPointDegeneratesTest, pointDegenerates_count);
+    test(notPointDegenerates, "notPointDegenerates", firstNotPointDegeneratesTest, notPointDegenerates_count);
+    test(lines, "lines", firstLinesTest, lines_count);
+    test(notLines, "notLines", firstNotLinesTest, notLines_count);
+    test(modEpsilonLines, "modEpsilonLines", firstModEpsilonTest, modEpsilonLines_count);
+    test(lessEpsilonLines, "lessEpsilonLines", firstLessEpsilonTest, lessEpsilonLines_count);
+    test(negEpsilonLines, "negEpsilonLines", firstNegEpsilonTest, negEpsilonLines_count);
+    test(quadraticLines, "quadraticLines", firstQuadraticLineTest, quadraticLines_count);
+    test(quadraticModEpsilonLines, "quadraticModEpsilonLines", firstQuadraticModLineTest,
+            quadraticModEpsilonLines_count);
+    testC(lines, "computed lines", firstComputedLinesTest, lines_count);
+    testC(tests, "computed tests", firstComputedCubicsTest, tests_count);
+    printf("%s end\n", __FUNCTION__);
+}
+
+static Cubic locals[] = {
+ {{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139}, {8.03767257, 89.1628526}},
+ {{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441}, {80.392774, 61.3533852}},
+ {{
+    60.776536520932126,
+    71.249307306133829
+  }, {
+    87.107894191103014,
+    22.377669868235323
+  }, {
+    1.4974754310666936,
+    68.069569937917208
+  }, {
+    45.261946574441133,
+    17.536076632112298
+  }},
+};
+
+static size_t localsCount = sizeof(locals) / sizeof(locals[0]);
+
+#define DEBUG_CRASH 0
+#define TEST_AVERAGE_END_POINTS 0 // must take const off to test
+extern const bool AVERAGE_END_POINTS;
+
+void CubicsToQuadratics_RandTest() {
+    for (size_t x = 0; x < localsCount; ++x) {
+        const Cubic& cubic = locals[x];
+        const SkPoint skcubic[4] = {{(float) cubic[0].x, (float) cubic[0].y},
+                {(float) cubic[1].x, (float) cubic[1].y}, {(float) cubic[2].x, (float) cubic[2].y},
+                {(float) cubic[3].x, (float) cubic[3].y}};
+        SkScalar skinflect[2];
+        int skin = SkFindCubicInflections(skcubic, skinflect);
+        SkDebugf("%s %d %1.9g\n", __FUNCTION__, skin, skinflect[0]);
+        SkTDArray<Quadratic> quads;
+        double precision = calcPrecision(cubic);
+        (void) cubic_to_quadratics(cubic, precision, quads);
+        SkDebugf("%s quads=%d\n", __FUNCTION__, quads.count());
+    }
+    srand(0);
+    const int arrayMax = 8;
+    const int sampleMax = 10;
+    const int tests = 1000000; // 10000000;
+    int quadDist[arrayMax];
+    bzero(quadDist, sizeof(quadDist));
+    Cubic samples[arrayMax][sampleMax];
+    int sampleCount[arrayMax];
+    bzero(sampleCount, sizeof(sampleCount));
+    for (int x = 0; x < tests; ++x) {
+        Cubic cubic;
+        for (int i = 0; i < 4; ++i) {
+            cubic[i].x = (double) rand() / RAND_MAX * 100;
+            cubic[i].y = (double) rand() / RAND_MAX * 100;
+        }
+    #if DEBUG_CRASH
+        char str[1024];
+        sprintf(str, "{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
+                cubic[0].x, cubic[0].y,  cubic[1].x, cubic[1].y, cubic[2].x, cubic[2].y,
+                cubic[3].x, cubic[3].y);
+    #endif
+        SkTDArray<Quadratic> quads;
+        double precision = calcPrecision(cubic);
+        (void) cubic_to_quadratics(cubic, precision, quads);
+        int count = quads.count();
+        SkASSERT(count > 0);
+        SkASSERT(--count < arrayMax);
+        quadDist[count]++;
+        int sCount = sampleCount[count];
+        if (sCount < sampleMax) {
+            memcpy(samples[count][sCount], cubic, sizeof(Cubic));
+            sampleCount[count]++;
+        }
+    }
+    for (int x = 0; x < arrayMax; ++x) {
+        if (!quadDist[x]) {
+            continue;
+        }
+        SkDebugf("%d %1.9g%%\n", x + 1, (double) quadDist[x] / tests * 100);
+    }
+    SkDebugf("\n");
+    for (int x = 0; x < arrayMax; ++x) {
+        for (int y = 0; y < sampleCount[x]; ++y) {
+#if TEST_AVERAGE_END_POINTS
+            for (int w = 0; w < 2; ++w) {
+                AVERAGE_END_POINTS = w;
+#else
+                int w = 0;
+#endif
+                SkDebugf("<div id=\"cubic%dx%d%s\">\n", x + 1, y, w ? "x" : "");
+                const Cubic& cubic = samples[x][y];
+                SkDebugf("{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
+                    cubic[0].x, cubic[0].y,  cubic[1].x, cubic[1].y, cubic[2].x, cubic[2].y,
+                    cubic[3].x, cubic[3].y);
+                SkTDArray<Quadratic> quads;
+                double precision = calcPrecision(cubic);
+                (void) cubic_to_quadratics(cubic, precision, quads);
+                for (int z = 0; z < quads.count(); ++z) {
+                    const Quadratic& quad = quads[z];
+                    SkDebugf("{{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}},\n",
+                        quad[0].x, quad[0].y,  quad[1].x, quad[1].y, quad[2].x, quad[2].y);
+                }
+                SkDebugf("</div>\n\n");
+#if TEST_AVERAGE_END_POINTS
+            }
+#endif
+        }
+    }
+    SkDebugf("</div>\n\n");
+    SkDebugf("<script type=\"text/javascript\">\n\n");
+    SkDebugf("var testDivs = [\n");
+    for (int x = 0; x < arrayMax; ++x) {
+        for (int y = 0; y < sampleCount[x]; ++y) {
+#if TEST_AVERAGE_END_POINTS
+            for (int w = 0; w < 2; ++w) {
+#else
+            int w = 0;
+#endif
+                SkDebugf("    cubic%dx%d%s,\n", x + 1, y, w ? "x" : "");
+#if TEST_AVERAGE_END_POINTS
+            }
+#endif
+        }
+    }
+    SkDebugf("\n\n\n");
+    SkDebugf("%s end\n", __FUNCTION__);
+}
diff --git a/experimental/Intersection/CubicUtilities.cpp b/experimental/Intersection/CubicUtilities.cpp
index 657d4a3..70f10c1 100644
--- a/experimental/Intersection/CubicUtilities.cpp
+++ b/experimental/Intersection/CubicUtilities.cpp
@@ -5,9 +5,28 @@
  * found in the LICENSE file.
  */
 #include "CubicUtilities.h"
-#include "DataTypes.h"
 #include "QuadraticUtilities.h"
 
+const int precisionUnit = 256; // FIXME: arbitrary -- should try different values in test framework
+
+// FIXME: cache keep the bounds and/or precision with the caller?
+double calcPrecision(const Cubic& cubic) {
+    _Rect dRect;
+    dRect.setBounds(cubic); // OPTIMIZATION: just use setRawBounds ?
+    double width = dRect.right - dRect.left;
+    double height = dRect.bottom - dRect.top;
+    return (width > height ? width : height) / precisionUnit;
+}
+
+#if SK_DEBUG
+double calcPrecision(const Cubic& cubic, double t, double scale) {
+    Cubic part;
+    sub_divide(cubic, SkTMax(0., t - scale), SkTMin(1., t + scale), part);
+    return calcPrecision(part);
+}
+#endif
+
+
 void coefficients(const double* cubic, double& A, double& B, double& C, double& D) {
     A = cubic[6]; // d
     B = cubic[4] * 3; // 3*c
@@ -22,14 +41,11 @@
 
 const double PI = 4 * atan(1);
 
-static bool is_unit_interval(double x) {
-    return x > 0 && x < 1;
-}
-
 // from SkGeometry.cpp (and Numeric Solutions, 5.6)
-int cubicRoots(double A, double B, double C, double D, double t[3]) {
+int cubicRootsValidT(double A, double B, double C, double D, double t[3]) {
+#if 0
     if (approximately_zero(A)) {  // we're just a quadratic
-        return quadraticRoots(B, C, D, t);
+        return quadraticRootsValidT(B, C, D, t);
     }
     double a, b, c;
     {
@@ -79,44 +95,159 @@
             *roots++ = r;
     }
     return (int)(roots - t);
+#else
+    double s[3];
+    int realRoots = cubicRootsReal(A, B, C, D, s);
+    int foundRoots = add_valid_ts(s, realRoots, t);
+    return foundRoots;
+#endif
+}
+
+int cubicRootsReal(double A, double B, double C, double D, double s[3]) {
+#if SK_DEBUG
+    // create a string mathematica understands
+    // GDB set print repe 15 # if repeated digits is a bother
+    //     set print elements 400 # if line doesn't fit
+    char str[1024];
+    bzero(str, sizeof(str));
+    sprintf(str, "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", A, B, C, D);
+#endif
+    if (approximately_zero(A)) {  // we're just a quadratic
+        return quadraticRootsReal(B, C, D, s);
+    }
+    if (approximately_zero(D)) { // 0 is one root
+        int num = quadraticRootsReal(A, B, C, s);
+        for (int i = 0; i < num; ++i) {
+            if (approximately_zero(s[i])) {
+                return num;
+            }
+        }
+        s[num++] = 0;
+        return num;
+    }
+    if (approximately_zero(A + B + C + D)) { // 1 is one root
+        int num = quadraticRootsReal(A, A + B, -D, s);
+        for (int i = 0; i < num; ++i) {
+            if (AlmostEqualUlps(s[i], 1)) {
+                return num;
+            }
+        }
+        s[num++] = 1;
+        return num;
+    }
+    double a, b, c;
+    {
+        double invA = 1 / A;
+        a = B * invA;
+        b = C * invA;
+        c = D * invA;
+    }
+    double a2 = a * a;
+    double Q = (a2 - b * 3) / 9;
+    double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
+    double R2 = R * R;
+    double Q3 = Q * Q * Q;
+    double R2MinusQ3 = R2 - Q3;
+    double adiv3 = a / 3;
+    double r;
+    double* roots = s;
+#if 0
+    if (approximately_zero_squared(R2MinusQ3) && AlmostEqualUlps(R2, Q3)) {
+        if (approximately_zero_squared(R)) {/* one triple solution */
+            *roots++ = -adiv3;
+        } else { /* one single and one double solution */
+
+            double u = cube_root(-R);
+            *roots++ = 2 * u - adiv3;
+            *roots++ = -u - adiv3;
+        }
+    }
+    else
+#endif
+    if (R2MinusQ3 < 0)   // we have 3 real roots
+    {
+        double theta = acos(R / sqrt(Q3));
+        double neg2RootQ = -2 * sqrt(Q);
+
+        r = neg2RootQ * cos(theta / 3) - adiv3;
+        *roots++ = r;
+
+        r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
+        if (!AlmostEqualUlps(s[0], r)) {
+            *roots++ = r;
+        }
+        r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
+        if (!AlmostEqualUlps(s[0], r) && (roots - s == 1 || !AlmostEqualUlps(s[1], r))) {
+            *roots++ = r;
+        }
+    }
+    else                // we have 1 real root
+    {
+        double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
+        double A = fabs(R) + sqrtR2MinusQ3;
+        A = cube_root(A);
+        if (R > 0) {
+            A = -A;
+        }
+        if (A != 0) {
+            A += Q / A;
+        }
+        r = A - adiv3;
+        *roots++ = r;
+        if (AlmostEqualUlps(R2, Q3)) {
+            r = -A / 2 - adiv3;
+            if (!AlmostEqualUlps(s[0], r)) {
+                *roots++ = r;
+            }
+        }
+    }
+    return (int)(roots - s);
 }
 
 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
 // c(t)  = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
 // c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
 //       = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2
-double derivativeAtT(const double* cubic, double t) {
+static double derivativeAtT(const double* cubic, double t) {
     double one_t = 1 - t;
     double a = cubic[0];
     double b = cubic[2];
     double c = cubic[4];
     double d = cubic[6];
-    return (b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t;
+    return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
 }
 
-// same as derivativeAtT
-// which is more accurate? which is faster?
-double derivativeAtT_2(const double* cubic, double t) {
-    double a = cubic[2] - cubic[0];
-    double b = cubic[4] - 2 * cubic[2] + cubic[0];
-    double c = cubic[6] + 3 * (cubic[2] - cubic[4]) - cubic[0];
-    return c * c * t * t + 2 * b * t + a;
+double dx_at_t(const Cubic& cubic, double t) {
+    return derivativeAtT(&cubic[0].x, t);
 }
 
-void dxdy_at_t(const Cubic& cubic, double t, double& dx, double& dy) {
-    if (&dx) {
-        dx = derivativeAtT(&cubic[0].x, t);
-    }
-    if (&dy) {
-        dy = derivativeAtT(&cubic[0].y, t);
-    }
+double dy_at_t(const Cubic& cubic, double t) {
+    return derivativeAtT(&cubic[0].y, t);
+}
+
+// OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
+void dxdy_at_t(const Cubic& cubic, double t, _Point& dxdy) {
+    dxdy.x = derivativeAtT(&cubic[0].x, t);
+    dxdy.y = derivativeAtT(&cubic[0].y, t);
+}
+
+
+int find_cubic_inflections(const Cubic& src, double tValues[])
+{
+    double Ax = src[1].x - src[0].x;
+    double Ay = src[1].y - src[0].y;
+    double Bx = src[2].x - 2 * src[1].x + src[0].x;
+    double By = src[2].y - 2 * src[1].y + src[0].y;
+    double Cx = src[3].x + 3 * (src[1].x - src[2].x) - src[0].x;
+    double Cy = src[3].y + 3 * (src[1].y - src[2].y) - src[0].y;
+    return quadraticRootsValidT(Bx * Cy - By * Cx, (Ax * Cy - Ay * Cx), Ax * By - Ay * Bx, tValues);
 }
 
 bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath) {
     double dy = cubic[index].y - cubic[zero].y;
     double dx = cubic[index].x - cubic[zero].x;
-    if (approximately_equal(dy, 0)) {
-        if (approximately_equal(dx, 0)) {
+    if (approximately_zero(dy)) {
+        if (approximately_zero(dx)) {
             return false;
         }
         memcpy(rotPath, cubic, sizeof(Cubic));
@@ -129,6 +260,7 @@
     return true;
 }
 
+#if 0 // unused for now
 double secondDerivativeAtT(const double* cubic, double t) {
     double a = cubic[0];
     double b = cubic[2];
@@ -136,6 +268,7 @@
     double d = cubic[6];
     return (c - 2 * b + a) * (1 - t) + (d - 2 * c + b) * t;
 }
+#endif
 
 void xy_at_t(const Cubic& cubic, double t, double& x, double& y) {
     double one_t = 1 - t;
diff --git a/experimental/Intersection/CubicUtilities.h b/experimental/Intersection/CubicUtilities.h
index ffaeeb2..186ed43 100644
--- a/experimental/Intersection/CubicUtilities.h
+++ b/experimental/Intersection/CubicUtilities.h
@@ -8,16 +8,31 @@
 #define CUBIC_UTILITIES_H
 
 #include "DataTypes.h"
+#include "SkTDArray.h"
 
+double calcPrecision(const Cubic& cubic);
+#if SK_DEBUG
+double calcPrecision(const Cubic& cubic, double t, double scale);
+#endif
+void chop_at(const Cubic& src, CubicPair& dst, double t);
+// FIXME: should be private static but public here for testing
+void computeDelta(const Cubic& c1, double t1, double scale1, const Cubic& c2, double t2,
+    double scale2, double& delta1, double& delta2);
 double cube_root(double x);
+int cubic_to_quadratics(const Cubic& cubic, double precision,
+        SkTDArray<Quadratic>& quadratics);
+void cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<double>& ts);
 void coefficients(const double* cubic, double& A, double& B, double& C, double& D);
-int cubicRoots(double A, double B, double C, double D, double t[3]);
-double derivativeAtT(const double* cubic, double t);
-// competing version that should produce same results
-double derivativeAtT_2(const double* cubic, double t);
-void dxdy_at_t(const Cubic& , double t, double& x, double& y);
+int cubicRootsValidT(double A, double B, double C, double D, double t[3]);
+int cubicRootsReal(double A, double B, double C, double D, double s[3]);
+void demote_cubic_to_quad(const Cubic& cubic, Quadratic& quad);
+double dx_at_t(const Cubic& , double t);
+double dy_at_t(const Cubic& , double t);
+void dxdy_at_t(const Cubic& , double t, _Point& y);
+int find_cubic_inflections(const Cubic& src, double tValues[]);
 bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath);
-double secondDerivativeAtT(const double* cubic, double t);
+void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst);
 void xy_at_t(const Cubic& , double t, double& x, double& y);
 
+extern const int precisionUnit;
 #endif
diff --git a/experimental/Intersection/CurveIntersection.h b/experimental/Intersection/CurveIntersection.h
index 664145b..5f27456 100644
--- a/experimental/Intersection/CurveIntersection.h
+++ b/experimental/Intersection/CurveIntersection.h
@@ -15,17 +15,12 @@
 double axialIntersect(const Quadratic& q1, const _Point& p, bool vert);
 bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double& maxT);
 bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double& maxT);
-void chop_at(const Cubic& src, CubicPair& dst, double t);
-void chop_at(const Quadratic& src, QuadraticPair& dst, double t);
 int convex_hull(const Cubic& cubic, char order[4]);
 bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]);
 bool implicit_matches(const Cubic& cubic1, const Cubic& cubic2);
 bool implicit_matches(const _Line& line1, const _Line& line2);
 bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps);
 bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2);
-void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst);
-void sub_divide(const _Line& src, double t1, double t2, _Line& dst);
-void sub_divide(const Quadratic& src, double t1, double t2, Quadratic& dst);
 void tangent(const Cubic& cubic, double t, _Point& result);
 void tangent(const _Line& line, _Point& result);
 void tangent(const Quadratic& quad, double t, _Point& result);
@@ -50,7 +45,11 @@
 int horizontalIntersect(const Quadratic& quad, double left, double right,
         double y, bool flipped, Intersections& );
 bool intersect(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
-int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]);
+// the following flavor uses quadratic approximation instead of convex hulls
+bool intersect2(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
+bool intersect(const Cubic& cubic, Intersections& i); // return true if cubic self-intersects
+int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& );
+int intersect(const Cubic& cubic, const _Line& line, Intersections& );
 bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& );
 int intersect(const Quadratic& quad, const _Line& line, Intersections& );
 // the following flavor uses the implicit form instead of convex hulls
diff --git a/experimental/Intersection/DataTypes.cpp b/experimental/Intersection/DataTypes.cpp
index 2832ab2..2be8938 100644
--- a/experimental/Intersection/DataTypes.cpp
+++ b/experimental/Intersection/DataTypes.cpp
@@ -74,11 +74,4 @@
 
     return abs(uA.i - uB.i);
 }
-
-int FloatAsInt(float A)
-{
-    Float_t uA(A);
-    return uA.i;
-}
 #endif
-
diff --git a/experimental/Intersection/DataTypes.h b/experimental/Intersection/DataTypes.h
index 33d88fa..e3d18af 100644
--- a/experimental/Intersection/DataTypes.h
+++ b/experimental/Intersection/DataTypes.h
@@ -7,27 +7,22 @@
 #ifndef __DataTypes_h__
 #define __DataTypes_h__
 
-#include <assert.h>
-#include <float.h>
-#include <limits.h>
-#include <math.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <sys/types.h>
+#include <float.h> // for FLT_EPSILON
+#include <math.h> // for fabs, sqrt
+
+#include "SkTypes.h"
 
 extern bool AlmostEqualUlps(float A, float B);
+inline bool AlmostEqualUlps(double A, double B) { return AlmostEqualUlps((float) A, (float) B); }
+
 // FIXME: delete
 int UlpsDiff(float A, float B);
-int FloatAsInt(float A);
 
-#if defined(IN_TEST)
-// FIXME: move to test-only header
-const double PointEpsilon = 0.000001;
-const double SquaredEpsilon = PointEpsilon * PointEpsilon;
-#endif
+// FLT_EPSILON == 1.19209290E-07 == 1 / (2 ^ 23)
+const double FLT_EPSILON_CUBED = FLT_EPSILON * FLT_EPSILON * FLT_EPSILON;
+const double FLT_EPSILON_SQUARED = FLT_EPSILON * FLT_EPSILON;
+const double FLT_EPSILON_SQRT = sqrt(FLT_EPSILON);
+const double FLT_EPSILON_INVERSE = 1 / FLT_EPSILON;
 
 inline bool approximately_zero(double x) {
 
@@ -44,12 +39,44 @@
     return fabs(x) < FLT_EPSILON;
 }
 
-inline bool approximately_zero_squared(double x) {
-    return fabs(x) < FLT_EPSILON * FLT_EPSILON;
+inline bool approximately_zero_cubed(double x) {
+    return fabs(x) < FLT_EPSILON_CUBED;
 }
 
+inline bool approximately_zero_squared(double x) {
+    return fabs(x) < FLT_EPSILON_SQUARED;
+}
+
+inline bool approximately_zero_sqrt(double x) {
+    return fabs(x) < FLT_EPSILON_SQRT;
+}
+
+inline bool approximately_zero_inverse(double x) {
+    return fabs(x) > FLT_EPSILON_INVERSE;
+}
+
+// Use this for comparing Ts in the range of 0 to 1. For general numbers (larger and smaller) use
+// AlmostEqualUlps instead.
 inline bool approximately_equal(double x, double y) {
+#if 1
     return approximately_zero(x - y);
+#else
+// see http://visualstudiomagazine.com/blogs/tool-tracker/2011/11/compare-floating-point-numbers.aspx
+// this allows very small (e.g. degenerate) values to compare unequally, but in this case,
+// AlmostEqualUlps should be used instead.
+    if (x == y) {
+        return true;
+    }
+    double absY = fabs(y);
+    if (x == 0) {
+        return absY < FLT_EPSILON;
+    }
+    double absX = fabs(x);
+    if (y == 0) {
+        return absX < FLT_EPSILON;
+    }
+    return fabs(x - y) < (absX > absY ? absX : absY) * FLT_EPSILON;
+#endif
 }
 
 inline bool approximately_equal_squared(double x, double y) {
@@ -105,7 +132,7 @@
 }
 
 inline bool approximately_positive_squared(double x) {
-    return x > -(FLT_EPSILON * FLT_EPSILON);
+    return x > -(FLT_EPSILON_SQUARED);
 }
 
 inline bool approximately_zero_or_more(double x) {
@@ -113,14 +140,14 @@
 }
 
 inline bool approximately_between(double a, double b, double c) {
-    assert(a <= c);
+    SkASSERT(a <= c);
     return a <= c ? approximately_negative(a - b) && approximately_negative(b - c)
             : approximately_negative(b - a) && approximately_negative(c - b);
 }
 
 // returns true if (a <= b <= c) || (a >= b >= c)
 inline bool between(double a, double b, double c) {
-    assert(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0));
+    SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0));
     return (a - b) * (c - b) <= 0;
 }
 
@@ -128,17 +155,37 @@
     double x;
     double y;
 
+    friend _Point operator-(const _Point& a, const _Point& b) {
+        _Point v = {a.x - b.x, a.y - b.y};
+        return v;
+    }
+
+    void operator+=(const _Point& v) {
+        x += v.x;
+        y += v.y;
+    }
+
     void operator-=(const _Point& v) {
         x -= v.x;
         y -= v.y;
     }
 
+    void operator/=(const double s) {
+        x /= s;
+        y /= s;
+    }
+
+    void operator*=(const double s) {
+        x *= s;
+        y *= s;
+    }
+
     friend bool operator==(const _Point& a, const _Point& b) {
         return a.x == b.x && a.y == b.y;
     }
 
     friend bool operator!=(const _Point& a, const _Point& b) {
-        return a.x!= b.x || a.y != b.y;
+        return a.x != b.x || a.y != b.y;
     }
 
     // note: this can not be implemented with
@@ -149,6 +196,22 @@
                 && AlmostEqualUlps((float) y, (float) a.y);
     }
 
+    double cross(const _Point& a) const {
+        return x * a.y - y * a.x;
+    }
+
+    double dot(const _Point& a) const {
+        return x * a.x + y * a.y;
+    }
+
+    double length() const {
+        return sqrt(lengthSquared());
+    }
+
+    double lengthSquared() const {
+        return x * x + y * y;
+    }
+
 };
 
 typedef _Point _Line[2];
@@ -177,11 +240,19 @@
     }
 
     // FIXME: used by debugging only ?
-    bool contains(const _Point& pt) {
+    bool contains(const _Point& pt) const {
         return approximately_between(left, pt.x, right)
                 && approximately_between(top, pt.y, bottom);
     }
 
+    bool intersects(_Rect& r) const {
+        SkASSERT(left <= right);
+        SkASSERT(top <= bottom);
+        SkASSERT(r.left <= r.right);
+        SkASSERT(r.top <= r.bottom);
+        return r.left <= right && left <= r.right && r.top <= bottom && top <= r.bottom;
+    }
+
     void set(const _Point& pt) {
         left = right = pt.x;
         top = bottom = pt.y;
@@ -210,4 +281,17 @@
     _Point pts[5];
 };
 
+// FIXME: move these into SkTypes.h
+template <typename T> inline T SkTMax(T a, T b) {
+    if (a < b)
+        a = b;
+    return a;
+}
+
+template <typename T> inline T SkTMin(T a, T b) {
+    if (a > b)
+        a = b;
+    return a;
+}
+
 #endif // __DataTypes_h__
diff --git a/experimental/Intersection/DataTypes_Test.h b/experimental/Intersection/DataTypes_Test.h
new file mode 100644
index 0000000..a8f14fa
--- /dev/null
+++ b/experimental/Intersection/DataTypes_Test.h
@@ -0,0 +1,7 @@
+#ifndef __DataTypes_Test_h__
+#define __DataTypes_Test_h__
+
+const double PointEpsilon = 0.000001;
+const double SquaredEpsilon = PointEpsilon * PointEpsilon;
+
+#endif
diff --git a/experimental/Intersection/EdgeDemo.cpp b/experimental/Intersection/EdgeDemo.cpp
index 841678a..684dfd9 100644
--- a/experimental/Intersection/EdgeDemo.cpp
+++ b/experimental/Intersection/EdgeDemo.cpp
@@ -150,6 +150,7 @@
     return drawPaths(canvas, path, useOld);
 }
 
+#if 0
 static void tryRoncoOnce(const SkPath& path, const SkRect& target, bool show) {
     // capture everything in a desired rectangle
     SkPath tiny;
@@ -226,7 +227,9 @@
     }
     testSimplifyx(tiny);
 }
+#endif
 
+#if 0
 static void tryRonco(const SkPath& path) {
     int divMax = 64;
     int divMin = 1;
@@ -261,6 +264,7 @@
         }
     }
 }
+#endif
 
 static bool drawLetters(SkCanvas* canvas, int step, bool useOld)
 {
@@ -329,7 +333,7 @@
 
 static size_t drawDemosCount = sizeof(drawDemos) / sizeof(drawDemos[0]);
 
-static bool (*firstTest)(SkCanvas* , int , bool) = drawLetters;
+static bool (*firstTest)(SkCanvas* , int , bool) = drawStars;
 
 
 bool DrawEdgeDemo(SkCanvas* canvas, int step, bool useOld) {
diff --git a/experimental/Intersection/EdgeWalker.cpp b/experimental/Intersection/EdgeWalker.cpp
index 12fe30d..79334b4 100644
--- a/experimental/Intersection/EdgeWalker.cpp
+++ b/experimental/Intersection/EdgeWalker.cpp
@@ -80,7 +80,7 @@
     const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
             {a[3].fX, a[3].fY}};
     const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
-    return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
+    return intersect(aCubic, bLine, intersections);
 }
 
 static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
diff --git a/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp b/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp
index 44e6b9d..9a5c1d0 100755
--- a/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp
+++ b/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp
@@ -288,4 +288,3 @@
     testsRun += waitForCompletion();
     SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
 }
-
diff --git a/experimental/Intersection/EdgeWalkerPolygons_Test.cpp b/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
index 5c7f250..8cda8e6 100644
--- a/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
+++ b/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
@@ -788,4 +788,3 @@
         firstTestComplete = true;
     }
 }
-
diff --git a/experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp b/experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp
index a2b6bef..a55f6ab 100644
--- a/experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp
+++ b/experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp
@@ -8,7 +8,6 @@
 #include "Intersection_Tests.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
-#include <assert.h>
 
 
 static void* testSimplify4x4QuadraticsMain(void* data)
@@ -84,7 +83,12 @@
     const char testStr[] = "testQuadratic";
     initializeTests(testStr, sizeof(testStr));
     int testsStart = testsRun;
-    for (int a = 0; a < 16; ++a) {
+    int a = 0;
+#define SKIP_A 0
+#if SKIP_A
+    a = 2;
+#endif
+    for (; a < 16; ++a) {
         for (int b = a ; b < 16; ++b) {
             for (int c = b ; c < 16; ++c) {
                 for (int d = c; d < 16; ++d) {
diff --git a/experimental/Intersection/EdgeWalkerRectangles_Test.cpp b/experimental/Intersection/EdgeWalkerRectangles_Test.cpp
index 31edd74..5f1b7f8 100644
--- a/experimental/Intersection/EdgeWalkerRectangles_Test.cpp
+++ b/experimental/Intersection/EdgeWalkerRectangles_Test.cpp
@@ -467,4 +467,3 @@
         (*simplifyTests[index])();
     }
 }
-
diff --git a/experimental/Intersection/EdgeWalker_Test.h b/experimental/Intersection/EdgeWalker_Test.h
index 205051e..8799b35 100644
--- a/experimental/Intersection/EdgeWalker_Test.h
+++ b/experimental/Intersection/EdgeWalker_Test.h
@@ -13,7 +13,8 @@
 
 //extern int comparePaths(const SkPath& one, const SkPath& two);
 extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap);
-extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
+extern int comparePaths(const SkPath& one, const SkPath& scaledOne, const SkPath& two,
+        const SkPath& scaledTwo, SkBitmap& bitmap,
         const SkPath& a, const SkPath& b, const ShapeOp shapeOp);
 extern void comparePathsTiny(const SkPath& one, const SkPath& two);
 extern bool drawAsciiPaths(const SkPath& one, const SkPath& two,
diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp
index f07eebe..ae5632e 100644
--- a/experimental/Intersection/EdgeWalker_TestUtility.cpp
+++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp
@@ -12,7 +12,6 @@
 #include "SkStream.h"
 
 #include <algorithm>
-#include <assert.h>
 #include <errno.h>
 #include <pthread.h>
 #include <unistd.h>
@@ -53,109 +52,10 @@
 static bool gPathStrAssert = true;
 static bool gUsePhysicalFiles = false;
 
-static bool isRectContour(SkPath::Iter& iter, SkRect& rect, SkPath::Direction& direction) {
-    int corners = 0;
-    SkPoint first, last;
-    first.set(0, 0);
-    last.set(0, 0);
-    int firstDirection = 0;
-    int lastDirection = 0;
-    int nextDirection = 0;
-    bool closedOrMoved = false;
-    bool autoClose = false;
-    rect.setEmpty();
-    uint8_t verb;
-    SkPoint data[4];
-    SkTDArray<SkPoint> sides;
-    bool empty = true;
-    while ((verb = iter.next(data)) != SkPath::kDone_Verb && !autoClose) {
-        empty = false;
-        SkPoint* pts = &data[1];
-        switch (verb) {
-            case SkPath::kClose_Verb:
-                pts = &last;
-                autoClose = true;
-            case SkPath::kLine_Verb: {
-                SkScalar left = last.fX;
-                SkScalar top = last.fY;
-                SkScalar right = pts->fX;
-                SkScalar bottom = pts->fY;
-                *sides.append() = *pts;
-                ++pts;
-                if (left != right && top != bottom) {
-                    return false; // diagonal
-                }
-                if (left == right && top == bottom) {
-                    break; // single point on side OK
-                }
-                nextDirection = (left != right) << 0 |
-                    (left < right || top < bottom) << 1;
-                if (0 == corners) {
-                    firstDirection = nextDirection;
-                    first = last;
-                    last = pts[-1];
-                    corners = 1;
-                    closedOrMoved = false;
-                    break;
-                }
-                if (closedOrMoved) {
-                    return false; // closed followed by a line
-                }
-                if (autoClose && nextDirection == firstDirection) {
-                    break; // colinear with first
-                }
-                closedOrMoved = autoClose;
-                if (lastDirection != nextDirection) {
-                    if (++corners > 4) {
-                        return false; // too many direction changes
-                    }
-                }
-                last = pts[-1];
-                if (lastDirection == nextDirection) {
-                    break; // colinear segment
-                }
-                // Possible values for corners are 2, 3, and 4.
-                // When corners == 3, nextDirection opposes firstDirection.
-                // Otherwise, nextDirection at corner 2 opposes corner 4.
-                int turn = firstDirection ^ (corners - 1);
-                int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
-                if ((directionCycle ^ turn) != nextDirection) {
-                    return false; // direction didn't follow cycle
-                }
-                break;
-            }
-            case SkPath::kQuad_Verb:
-            case SkPath::kCubic_Verb:
-                return false; // quadratic, cubic not allowed
-            case SkPath::kMove_Verb:
-                last = *pts++;
-                *sides.append() = last;
-                closedOrMoved = true;
-                break;
-        }
-        lastDirection = nextDirection;
-    }
-    // Success if 4 corners and first point equals last
-    bool result = 4 == corners && (first == last || autoClose);
-    if (result) {
-        direction = firstDirection == (lastDirection + 1 & 3) ? SkPath::kCCW_Direction
-                : SkPath::kCW_Direction;
-        rect.set(&sides[0], sides.count());
-    } else {
-        rect.setEmpty();
-    }
-    return !empty;
-}
-
-static void showPathContour(SkPath::Iter& iter, bool skip) {
+static void showPathContour(SkPath::Iter& iter) {
     uint8_t verb;
     SkPoint pts[4];
     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        if (skip) {
-            if (verb == SkPath::kClose_Verb) {
-                return;
-            }
-        }
         switch (verb) {
             case SkPath::kMove_Verb:
                 SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pts[0].fX, pts[0].fY);
@@ -174,7 +74,7 @@
                 break;
             case SkPath::kClose_Verb:
                 SkDebugf("path.close();\n");
-                return;
+                break;
             default:
                 SkDEBUGFAIL("bad verb");
                 return;
@@ -185,36 +85,29 @@
 void showPath(const SkPath& path, const char* str) {
     SkDebugf("%s\n", !str ? "original:" : str);
     SkPath::Iter iter(path, true);
-    SkTDArray<SkRect> rects;
-    SkTDArray<SkPath::Direction> directions;
-    SkRect rect;
-    SkPath::Direction direction;
-    while (isRectContour(iter, rect, direction)) {
-        *rects.append() = rect;
-        *directions.append() = direction;
-    }
-    iter.setPath(path, true);
-    for (int contour = 0; contour < rects.count(); ++contour) {
-        const SkRect& rect = rects[contour];
-        bool useRect = !rect.isEmpty();
-        showPathContour(iter, useRect);
-        if (useRect) {
+    int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
+    if (rectCount > 0) {
+        SkTDArray<SkRect> rects;
+        SkTDArray<SkPath::Direction> directions;
+        rects.setCount(rectCount);
+        directions.setCount(rectCount);
+        path.rectContours(rects.begin(), directions.begin());
+        for (int contour = 0; contour < rectCount; ++contour) {
+            const SkRect& rect = rects[contour];
             SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
                     rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
                     ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
         }
+        return;
     }
+    iter.setPath(path, true);
+    showPathContour(iter);
 }
 
-static int pathsDrawTheSame(const SkPath& one, const SkPath& two,
-        SkBitmap& bits, SkPath& scaledOne, SkPath& scaledTwo, int& error2x2) {
     const int bitWidth = 64;
     const int bitHeight = 64;
-    if (bits.width() == 0) {
-        bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
-        bits.allocPixels();
-    }
 
+static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
     SkRect larger = one.getBounds();
     larger.join(two.getBounds());
     SkScalar largerWidth = larger.width();
@@ -227,17 +120,21 @@
     }
     SkScalar hScale = (bitWidth - 2) / largerWidth;
     SkScalar vScale = (bitHeight - 2) / largerHeight;
-    SkMatrix scale;
     scale.reset();
     scale.preScale(hScale, vScale);
-    one.transform(scale, &scaledOne);
-    two.transform(scale, &scaledTwo);
-    const SkRect& bounds1 = scaledOne.getBounds();
+}
 
+static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
+        int& error2x2) {
+    if (bits.width() == 0) {
+        bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
+        bits.allocPixels();
+    }
     SkCanvas canvas(bits);
     canvas.drawColor(SK_ColorWHITE);
     SkPaint paint;
     canvas.save();
+    const SkRect& bounds1 = scaledOne.getBounds();
     canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
     canvas.drawPath(scaledOne, paint);
     canvas.restore();
@@ -269,6 +166,15 @@
     return errors;
 }
 
+static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
+        SkPath& scaledTwo, int& error2x2) {
+    SkMatrix scale;
+    scaleMatrix(one, two, scale);
+    one.transform(scale, &scaledOne);
+    two.transform(scale, &scaledTwo);
+    return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
+}
+
 bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
     if (!drawPaths) {
         return true;
@@ -331,7 +237,7 @@
     if (errors2x2 == 0) {
         return 0;
     }
-    const int MAX_ERRORS = 8;
+    const int MAX_ERRORS = 9;
     if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
         showSimplifiedPath(one, two, scaledOne, scaledTwo);
     }
@@ -354,11 +260,11 @@
     drawAsciiPaths(scaledOne, scaledTwo, true);
 }
 
-int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
-        const SkPath& a, const SkPath& b, const ShapeOp shapeOp) {
+int comparePaths(const SkPath& one, const SkPath& scaledOne, const SkPath& two,
+        const SkPath& scaledTwo,
+        SkBitmap& bitmap, const SkPath& a, const SkPath& b, const ShapeOp shapeOp) {
     int errors2x2;
-    SkPath scaledOne, scaledTwo;
-    int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
+    int errors = pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
     if (errors2x2 == 0) {
         return 0;
     }
@@ -461,15 +367,31 @@
 bool testShapeOp(const SkPath& a, const SkPath& b, const ShapeOp shapeOp) {
     SkPath out;
     operate(a, b, shapeOp, out);
-    SkPath pathOut;
+    SkPath pathOut, scaledPathOut;
     SkRegion rgnA, rgnB, openClip, rgnOut;
     openClip.setRect(-16000, -16000, 16000, 16000);
     rgnA.setPath(a, openClip);
     rgnB.setPath(b, openClip);
     rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
     rgnOut.getBoundaryPath(&pathOut);
+
+    SkMatrix scale;
+    scaleMatrix(a, b, scale);
+    SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
+    SkPath scaledA, scaledB;
+    scaledA.addPath(a, scale);
+    scaledA.setFillType(a.getFillType());
+    scaledB.addPath(b, scale);
+    scaledB.setFillType(b.getFillType());
+    scaledRgnA.setPath(scaledA, openClip);
+    scaledRgnB.setPath(scaledB, openClip);
+    scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
+    scaledRgnOut.getBoundaryPath(&scaledPathOut);
     SkBitmap bitmap;
-    int result = comparePaths(pathOut, out, bitmap, a, b, shapeOp);
+    SkPath scaledOut;
+    scaledOut.addPath(out, scale);
+    scaledOut.setFillType(out.getFillType());
+    int result = comparePaths(pathOut, scaledPathOut, out, scaledOut, bitmap, a, b, shapeOp);
     if (result && gPathStrAssert) {
         SkASSERT(0);
     }
@@ -682,7 +604,11 @@
 
     outFile.writeText("static void ");
     writeTestName(nameSuffix, outFile);
-    outFile.writeText("() {\n    SkPath path, pathB;\n");
+    outFile.writeText("() {\n    SkPath path");
+    if (!pathPrefix) {
+        outFile.writeText(", pathB");
+    }
+    outFile.writeText(";\n");
     if (pathPrefix) {
         outFile.writeText(pathPrefix);
     }
@@ -699,7 +625,7 @@
     outFile.writeText("    const char* str;\n");
     outFile.writeText("} tests[] = {\n");
     outFile.writeText("    TEST(");
-    writeTestName(pathPrefix, outFile);
+    writeTestName(nameSuffix, outFile);
     outFile.writeText("),\n");
     outFile.flush();
 }
diff --git a/experimental/Intersection/Extrema.cpp b/experimental/Intersection/Extrema.cpp
index 780d877..7a41ddf 100644
--- a/experimental/Intersection/Extrema.cpp
+++ b/experimental/Intersection/Extrema.cpp
@@ -45,7 +45,7 @@
     double Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
     r += validUnitDivide(Q, A, r);
     r += validUnitDivide(C, Q, r);
-    if (r - roots == 2 && approximately_equal(roots[0], roots[1])) { // nearly-equal?
+    if (r - roots == 2 && AlmostEqualUlps(roots[0], roots[1])) { // nearly-equal?
         r -= 1; // skip the double root
     }
     return (int)(r - roots);
diff --git a/experimental/Intersection/Inline_Tests.cpp b/experimental/Intersection/Inline_Tests.cpp
index 00a1c6c..90f5e7a 100644
--- a/experimental/Intersection/Inline_Tests.cpp
+++ b/experimental/Intersection/Inline_Tests.cpp
@@ -12,7 +12,7 @@
     if (x == y) {
         return;
     }
-    printf("result=%d expected=%d %s\n", x, y, s);
+    SkDebugf("result=%d expected=%d %s\n", x, y, s);
 }
 
 static void side_test() {
@@ -41,7 +41,7 @@
             if (all == 0x0F) {
                 continue;
             }
-            printf("[%d,%d] other_two failed mask=%d [%d,%d]\n",
+            SkDebugf("[%d,%d] other_two failed mask=%d [%d,%d]\n",
                 x, y, mask, x ^ mask, y ^ mask);
         }
     }
diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp
index 9ae4ff5..fc68da4 100644
--- a/experimental/Intersection/Intersection_Tests.cpp
+++ b/experimental/Intersection/Intersection_Tests.cpp
@@ -6,7 +6,6 @@
  */
 #include "CubicIntersection_TestData.h"
 #include "Intersection_Tests.h"
-#include "SkTypes.h"
 
 void cubecode_test(int test);
 
@@ -16,19 +15,24 @@
     int testsRun = 0;
 
     SimplifyNew_Test();
+    CubicIntersection_OneOffTest();
+    ShapeOps4x4CubicsThreaded_Test(testsRun);
+    CubicToQuadratics_Test();
+    QuadraticIntersection_Test();
+    QuarticRoot_Test();
+    CubicIntersection_RandTest();
+    CubicsToQuadratics_RandTest();
+    Simplify4x4RectsThreaded_Test(testsRun);
     Simplify4x4QuadraticsThreaded_Test(testsRun);
     QuadLineIntersectThreaded_Test(testsRun);
-    Simplify4x4RectsThreaded_Test(testsRun);
     SimplifyNondegenerate4x4TrianglesThreaded_Test(testsRun);
     SimplifyDegenerate4x4TrianglesThreaded_Test(testsRun);
     Simplify4x4QuadralateralsThreaded_Test(testsRun);
     ShapeOps4x4RectsThreaded_Test(testsRun);
     SkDebugf("%s total testsRun=%d\n", __FUNCTION__, testsRun);
-    QuadraticIntersection_Test();
     LineQuadraticIntersection_Test();
     MiniSimplify_Test();
     SimplifyAngle_Test();
-    QuarticRoot_Test();
     QuadraticBezierClip_Test();
     SimplifyFindNext_Test();
     SimplifyFindTop_Test();
diff --git a/experimental/Intersection/Intersection_Tests.h b/experimental/Intersection/Intersection_Tests.h
index 46e6b7b..c5a6d33 100644
--- a/experimental/Intersection/Intersection_Tests.h
+++ b/experimental/Intersection/Intersection_Tests.h
@@ -4,18 +4,22 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#if !defined(IN_TEST)
-    #define IN_TEST 1
-#endif
+#include "DataTypes_Test.h"
 
 void ActiveEdge_Test();
 void ConvexHull_Test();
 void ConvexHull_X_Test();
 void CubicBezierClip_Test();
 void CubicCoincidence_Test();
+void CubicIntersection_ComputeDeltaTest();
+void CubicIntersection_OneOffTest();
 void CubicIntersection_Test();
+void CubicIntersection_RandTest();
+void CubicIntersection_RandTestOld();
 void CubicParameterization_Test();
 void CubicReduceOrder_Test();
+void CubicsToQuadratics_RandTest();
+void CubicToQuadratics_Test();
 void Inline_Tests();
 void Intersection_Tests();
 void LineCubicIntersection_Test();
@@ -37,6 +41,7 @@
 void Simplify4x4QuadraticsThreaded_Test(int& );
 void Simplify4x4RectsThreaded_Test(int& );
 void SimplifyRectangularPaths_Test();
+void ShapeOps4x4CubicsThreaded_Test(int& );
 void ShapeOps4x4RectsThreaded_Test(int& );
 void QuadLineIntersectThreaded_Test(int& );
 void QuadraticBezierClip_Test();
diff --git a/experimental/Intersection/Intersections.cpp b/experimental/Intersection/Intersections.cpp
index 3f4e8cf..cffeaec 100644
--- a/experimental/Intersection/Intersections.cpp
+++ b/experimental/Intersection/Intersections.cpp
@@ -8,9 +8,109 @@
 #include "DataTypes.h"
 #include "Intersections.h"
 
+void Intersections::addCoincident(double s1, double e1, double s2, double e2) {
+    SkASSERT((fCoincidentUsed & 1) != 1);
+    for (int index = 0; index < fCoincidentUsed; index += 2) {
+        double cs1 = fCoincidentT[fSwap][index];
+        double ce1 = fCoincidentT[fSwap][index + 1];
+        bool s1in = approximately_between(cs1, s1, ce1);
+        bool e1in = approximately_between(cs1, e1, ce1);
+        double cs2 = fCoincidentT[fSwap ^ 1][index];
+        double ce2 = fCoincidentT[fSwap ^ 1][index + 1];
+        bool s2in = approximately_between(cs2, s2, ce2);
+        bool e2in = approximately_between(cs2, e2, ce2);
+        if ((s1in | e1in) & (s2in | e2in)) {
+            double lesser1 = SkTMin(cs1, ce1);
+            index += cs1 > ce1;
+            if (s1in < lesser1) {
+                fCoincidentT[fSwap][index] = s1in;
+            } else if (e1in < lesser1) {
+                fCoincidentT[fSwap][index] = e1in;
+            }
+            index ^= 1;
+            double greater1 = fCoincidentT[fSwap][index];
+            if (s1in > greater1) {
+                fCoincidentT[fSwap][index] = s1in;
+            } else if (e1in > greater1) {
+                fCoincidentT[fSwap][index] = e1in;
+            }
+            index &= ~1;
+            double lesser2 = SkTMin(cs2, ce2);
+            index += cs2 > ce2;
+            if (s2in < lesser2) {
+                fCoincidentT[fSwap ^ 1][index] = s2in;
+            } else if (e2in < lesser2) {
+                fCoincidentT[fSwap ^ 1][index] = e2in;
+            }
+            index ^= 1;
+            double greater2 = fCoincidentT[fSwap ^ 1][index];
+            if (s2in > greater2) {
+                fCoincidentT[fSwap ^ 1][index] = s2in;
+            } else if (e2in > greater2) {
+                fCoincidentT[fSwap ^ 1][index] = e2in;
+            }
+            return;
+        }
+    }
+    SkASSERT(fCoincidentUsed < 9);
+    fCoincidentT[fSwap][fCoincidentUsed] = s1;
+    fCoincidentT[fSwap ^ 1][fCoincidentUsed] = s2;
+    ++fCoincidentUsed;
+    fCoincidentT[fSwap][fCoincidentUsed] = e1;
+    fCoincidentT[fSwap ^ 1][fCoincidentUsed] = e2;
+    ++fCoincidentUsed;
+}
+
 void Intersections::cleanUp() {
-    assert(fCoincidentUsed);
-    assert(fUsed);
+    SkASSERT(fCoincidentUsed);
+    SkASSERT(fUsed);
     // find any entries in fT that could be part of the coincident range
 
 }
+
+// FIXME: this doesn't respect swap, but add coincident does -- seems inconsistent
+void Intersections::insert(double one, double two) {
+    SkASSERT(fUsed <= 1 || fT[0][0] < fT[0][1]);
+    int index;
+    for (index = 0; index < fUsed; ++index) {
+        if (approximately_equal(fT[0][index], one)
+                && approximately_equal(fT[1][index], two)) {
+            return;
+        }
+        if (fT[0][index] > one) {
+            break;
+        }
+    }
+    SkASSERT(fUsed < 9);
+    int remaining = fUsed - index;
+    if (remaining > 0) {
+        memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining);
+        memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining);
+    }
+    fT[0][index] = one;
+    fT[1][index] = two;
+    ++fUsed;
+}
+
+// FIXME: all callers should be moved to regular insert. Failures are likely
+// if two separate callers differ on whether ts are equal or not
+void Intersections::insertOne(double t, int side) {
+    int used = side ? fUsed2 : fUsed;
+    SkASSERT(used <= 1 || fT[side][0] < fT[side][1]);
+    int index;
+    for (index = 0; index < used; ++index) {
+        if (approximately_equal(fT[side][index], t)) {
+            return;
+        }
+        if (fT[side][index] > t) {
+            break;
+        }
+    }
+    SkASSERT(used < 9);
+    int remaining = used - index;
+    if (remaining > 0) {
+        memmove(&fT[side][index + 1], &fT[side][index], sizeof(fT[side][0]) * remaining);
+    }
+    fT[side][index] = t;
+    side ? ++fUsed2 : ++fUsed;
+}
diff --git a/experimental/Intersection/Intersections.h b/experimental/Intersection/Intersections.h
index fe12b25..779ff33 100644
--- a/experimental/Intersection/Intersections.h
+++ b/experimental/Intersection/Intersections.h
@@ -7,20 +7,16 @@
 #ifndef Intersections_DEFINE
 #define Intersections_DEFINE
 
-#include <algorithm> // for std::min
-
 class Intersections {
 public:
     Intersections()
-        : fUsed(0)
-        , fUsed2(0)
-        , fCoincidentUsed(0)
+        : fFlip(0)
         , fSwap(0)
-        , fFlip(0)
     {
         // OPTIMIZE: don't need to be initialized in release
         bzero(fT, sizeof(fT));
         bzero(fCoincidentT, sizeof(fCoincidentT));
+        reset();
     }
 
     void add(double one, double two) {
@@ -30,7 +26,7 @@
                 return;
             }
         }
-        assert(fUsed < 9);
+        SkASSERT(fUsed < 9);
         fT[fSwap][fUsed] = one;
         fT[fSwap ^ 1][fUsed] = two;
         ++fUsed;
@@ -44,70 +40,19 @@
                 return;
             }
         }
-        assert(fCoincidentUsed < 9);
+        SkASSERT(fCoincidentUsed < 9);
         fCoincidentT[fSwap][fCoincidentUsed] = one;
         fCoincidentT[fSwap ^ 1][fCoincidentUsed] = two;
         ++fCoincidentUsed;
     }
 
-    void addCoincident(double s1, double e1, double s2, double e2) {
-        assert((fCoincidentUsed & 1) != 1);
-        for (int index = 0; index < fCoincidentUsed; index += 2) {
-            double cs1 = fCoincidentT[fSwap][index];
-            double ce1 = fCoincidentT[fSwap][index + 1];
-            bool s1in = approximately_between(cs1, s1, ce1);
-            bool e1in = approximately_between(cs1, e1, ce1);
-            double cs2 = fCoincidentT[fSwap ^ 1][index];
-            double ce2 = fCoincidentT[fSwap ^ 1][index + 1];
-            bool s2in = approximately_between(cs2, s2, ce2);
-            bool e2in = approximately_between(cs2, e2, ce2);
-            if ((s1in | e1in) & (s2in | e2in)) {
-                double lesser1 = std::min(cs1, ce1);
-                index += cs1 > ce1;
-                if (s1in < lesser1) {
-                    fCoincidentT[fSwap][index] = s1in;
-                } else if (e1in < lesser1) {
-                    fCoincidentT[fSwap][index] = e1in;
-                }
-                index ^= 1;
-                double greater1 = fCoincidentT[fSwap][index];
-                if (s1in > greater1) {
-                    fCoincidentT[fSwap][index] = s1in;
-                } else if (e1in > greater1) {
-                    fCoincidentT[fSwap][index] = e1in;
-                }
-                index &= ~1;
-                double lesser2 = std::min(cs2, ce2);
-                index += cs2 > ce2;
-                if (s2in < lesser2) {
-                    fCoincidentT[fSwap ^ 1][index] = s2in;
-                } else if (e2in < lesser2) {
-                    fCoincidentT[fSwap ^ 1][index] = e2in;
-                }
-                index ^= 1;
-                double greater2 = fCoincidentT[fSwap ^ 1][index];
-                if (s2in > greater2) {
-                    fCoincidentT[fSwap ^ 1][index] = s2in;
-                } else if (e2in > greater2) {
-                    fCoincidentT[fSwap ^ 1][index] = e2in;
-                }
-                return;
-            }
-        }
-        assert(fCoincidentUsed < 9);
-        fCoincidentT[fSwap][fCoincidentUsed] = s1;
-        fCoincidentT[fSwap ^ 1][fCoincidentUsed] = s2;
-        ++fCoincidentUsed;
-        fCoincidentT[fSwap][fCoincidentUsed] = e1;
-        fCoincidentT[fSwap ^ 1][fCoincidentUsed] = e2;
-        ++fCoincidentUsed;
-    }
+    void addCoincident(double s1, double e1, double s2, double e2);
 
     // FIXME: this is necessary because curve/curve intersections are noisy
     // remove once curve/curve intersections are improved
     void cleanUp();
 
-    int coincidentUsed() {
+    int coincidentUsed() const{
         return fCoincidentUsed;
     }
 
@@ -120,51 +65,8 @@
         }
     }
 
-    void insert(double one, double two) {
-        assert(fUsed <= 1 || fT[0][0] < fT[0][1]);
-        int index;
-        for (index = 0; index < fUsed; ++index) {
-            if (approximately_equal(fT[0][index], one)
-                    && approximately_equal(fT[1][index], two)) {
-                return;
-            }
-            if (fT[0][index] > one) {
-                break;
-            }
-        }
-        assert(fUsed < 9);
-        int remaining = fUsed - index;
-        if (remaining > 0) {
-            memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining);
-            memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining);
-        }
-        fT[0][index] = one;
-        fT[1][index] = two;
-        ++fUsed;
-    }
-
-    // FIXME: all callers should be moved to regular insert. Failures are likely
-    // if two separate callers differ on whether ts are equal or not
-    void insertOne(double t, int side) {
-        int used = side ? fUsed2 : fUsed;
-        assert(used <= 1 || fT[side][0] < fT[side][1]);
-        int index;
-        for (index = 0; index < used; ++index) {
-            if (approximately_equal(fT[side][index], t)) {
-                return;
-            }
-            if (fT[side][index] > t) {
-                break;
-            }
-        }
-        assert(used < 9);
-        int remaining = used - index;
-        if (remaining > 0) {
-            memmove(&fT[side][index + 1], &fT[side][index], sizeof(fT[side][0]) * remaining);
-        }
-        fT[side][index] = t;
-        side ? ++fUsed2 : ++fUsed;
-    }
+    void insert(double one, double two);
+    void insertOne(double t, int side);
 
     bool intersected() const {
         return fUsed > 0;
@@ -174,15 +76,32 @@
         return fUsed == fUsed2;
     }
 
-    void swap() {
-        fSwap ^= 1;
+    // leaves flip, swap alone
+    void reset() {
+        fUsed = fUsed2 = fCoincidentUsed = 0;
+        fUnsortable = false;
     }
 
-    bool swapped() {
+    void swap() {
+        fSwap ^= true;
+    }
+
+    void swapPts() {
+        int index;
+        for (index = 0; index < fUsed; ++index) {
+            SkTSwap(fT[0][index], fT[1][index]);
+        }
+    }
+
+    bool swapped() const {
         return fSwap;
     }
 
-    int used() {
+    bool unsortable() const {
+        return fUnsortable;
+    }
+
+    int used() const {
         return fUsed;
     }
 
@@ -191,10 +110,10 @@
     int fUsed;
     int fUsed2;
     int fCoincidentUsed;
-    int fFlip;
+    bool fFlip;
+    bool fUnsortable;
 private:
     int fSwap;
 };
 
 #endif
-
diff --git a/experimental/Intersection/LineCubicIntersection.cpp b/experimental/Intersection/LineCubicIntersection.cpp
index 3111ddd..cc63099 100644
--- a/experimental/Intersection/LineCubicIntersection.cpp
+++ b/experimental/Intersection/LineCubicIntersection.cpp
@@ -77,160 +77,213 @@
 
  */
 
-class LineCubicIntersections : public Intersections {
+class LineCubicIntersections {
 public:
 
-LineCubicIntersections(const Cubic& c, const _Line& l, double r[3])
+LineCubicIntersections(const Cubic& c, const _Line& l, Intersections& i)
     : cubic(c)
     , line(l)
-    , range(r) {
+    , intersections(i) {
+}
+
+// see parallel routine in line quadratic intersections
+int intersectRay(double roots[3]) {
+    double adj = line[1].x - line[0].x;
+    double opp = line[1].y - line[0].y;
+    Cubic r;
+    for (int n = 0; n < 4; ++n) {
+        r[n].x = (cubic[n].y - line[0].y) * adj - (cubic[n].x - line[0].x) * opp;
+    }
+    double A, B, C, D;
+    coefficients(&r[0].x, A, B, C, D);
+    return cubicRootsValidT(A, B, C, D, roots);
 }
 
 int intersect() {
-    double slope;
-    double axisIntercept;
-    moreHorizontal = implicitLine(line, slope, axisIntercept);
-    double A, B, C, D;
-    coefficients(&cubic[0].x, A, B, C, D);
-    double E, F, G, H;
-    coefficients(&cubic[0].y, E, F, G, H);
-    if (moreHorizontal) {
-        A = A * slope - E;
-        B = B * slope - F;
-        C = C * slope - G;
-        D = D * slope - H + axisIntercept;
-    } else {
-        A = A - E * slope;
-        B = B - F * slope;
-        C = C - G * slope;
-        D = D - H * slope - axisIntercept;
+    addEndPoints();
+    double rootVals[3];
+    int roots = intersectRay(rootVals);
+    for (int index = 0; index < roots; ++index) {
+        double cubicT = rootVals[index];
+        double lineT = findLineT(cubicT);
+        if (pinTs(cubicT, lineT)) {
+            intersections.insert(cubicT, lineT);
+        }
     }
-    return cubicRoots(A, B, C, D, range);
+    return intersections.fUsed;
 }
 
-int horizontalIntersect(double axisIntercept) {
+int horizontalIntersect(double axisIntercept, double roots[3]) {
     double A, B, C, D;
     coefficients(&cubic[0].y, A, B, C, D);
     D -= axisIntercept;
-    return cubicRoots(A, B, C, D, range);
+    return cubicRootsValidT(A, B, C, D, roots);
 }
 
-int verticalIntersect(double axisIntercept) {
+int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
+    addHorizontalEndPoints(left, right, axisIntercept);
+    double rootVals[3];
+    int roots = horizontalIntersect(axisIntercept, rootVals);
+    for (int index = 0; index < roots; ++index) {
+        double x;
+        double cubicT = rootVals[index];
+        xy_at_t(cubic, cubicT, x, *(double*) NULL);
+        double lineT = (x - left) / (right - left);
+        if (pinTs(cubicT, lineT)) {
+            intersections.insert(cubicT, lineT);
+        }
+    }
+    if (flipped) {
+        flip();
+    }
+    return intersections.fUsed;
+}
+
+int verticalIntersect(double axisIntercept, double roots[3]) {
     double A, B, C, D;
     coefficients(&cubic[0].x, A, B, C, D);
     D -= axisIntercept;
-    return cubicRoots(A, B, C, D, range);
+    return cubicRootsValidT(A, B, C, D, roots);
+}
+
+int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
+    addVerticalEndPoints(top, bottom, axisIntercept);
+    double rootVals[3];
+    int roots = verticalIntersect(axisIntercept, rootVals);
+    for (int index = 0; index < roots; ++index) {
+        double y;
+        double cubicT = rootVals[index];
+        xy_at_t(cubic, cubicT, *(double*) NULL, y);
+        double lineT = (y - top) / (bottom - top);
+        if (pinTs(cubicT, lineT)) {
+            intersections.insert(cubicT, lineT);
+        }
+    }
+    if (flipped) {
+        flip();
+    }
+    return intersections.fUsed;
+}
+
+protected:
+
+void addEndPoints()
+{
+    for (int cIndex = 0; cIndex < 4; cIndex += 3) {
+        for (int lIndex = 0; lIndex < 2; lIndex++) {
+            if (cubic[cIndex] == line[lIndex]) {
+                intersections.insert(cIndex >> 1, lIndex);
+            }
+        }
+    }
+}
+
+void addHorizontalEndPoints(double left, double right, double y)
+{
+    for (int cIndex = 0; cIndex < 4; cIndex += 3) {
+        if (cubic[cIndex].y != y) {
+            continue;
+        }
+        if (cubic[cIndex].x == left) {
+            intersections.insert(cIndex >> 1, 0);
+        }
+        if (cubic[cIndex].x == right) {
+            intersections.insert(cIndex >> 1, 1);
+        }
+    }
+}
+
+void addVerticalEndPoints(double top, double bottom, double x)
+{
+    for (int cIndex = 0; cIndex < 4; cIndex += 3) {
+        if (cubic[cIndex].x != x) {
+            continue;
+        }
+        if (cubic[cIndex].y == top) {
+            intersections.insert(cIndex >> 1, 0);
+        }
+        if (cubic[cIndex].y == bottom) {
+            intersections.insert(cIndex >> 1, 1);
+        }
+    }
 }
 
 double findLineT(double t) {
-    const double* cPtr;
-    const double* lPtr;
-    if (moreHorizontal) {
-        cPtr = &cubic[0].x;
-        lPtr = &line[0].x;
-    } else {
-        cPtr = &cubic[0].y;
-        lPtr = &line[0].y;
+    double x, y;
+    xy_at_t(cubic, t, x, y);
+    double dx = line[1].x - line[0].x;
+    double dy = line[1].y - line[0].y;
+    if (fabs(dx) > fabs(dy)) {
+        return (x - line[0].x) / dx;
     }
-    // FIXME: should fold the following in with TestUtilities.cpp xy_at_t()
-    double s = 1 - t;
-    double cubicVal = cPtr[0] * s * s * s + 3 * cPtr[2] * s * s * t
-                + 3 * cPtr[4] * s * t * t + cPtr[6] * t * t * t;
-    return (cubicVal - lPtr[0]) / (lPtr[2] - lPtr[0]);
+    return (y - line[0].y) / dy;
+}
+
+void flip() {
+    // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
+    int roots = intersections.fUsed;
+    for (int index = 0; index < roots; ++index) {
+        intersections.fT[1][index] = 1 - intersections.fT[1][index];
+    }
+}
+
+bool pinTs(double& cubicT, double& lineT) {
+    if (!approximately_one_or_less(lineT)) {
+        return false;
+    }
+    if (!approximately_zero_or_more(lineT)) {
+        return false;
+    }
+    if (cubicT < 0) {
+        cubicT = 0;
+    } else if (cubicT > 1) {
+        cubicT = 1;
+    }
+    if (lineT < 0) {
+        lineT = 0;
+    } else if (lineT > 1) {
+        lineT = 1;
+    }
+    return true;
 }
 
 private:
 
 const Cubic& cubic;
 const _Line& line;
-double* range;
-bool moreHorizontal;
-
+Intersections& intersections;
 };
 
-int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]) {
-    LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
-    return c.horizontalIntersect(y);
-}
-
 int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
         double tRange[3]) {
-    LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
-    int result = c.horizontalIntersect(y);
-    for (int index = 0; index < result; ) {
+    LineCubicIntersections c(cubic, *((_Line*) 0), *((Intersections*) 0));
+    double rootVals[3];
+    int result = c.horizontalIntersect(y, rootVals);
+    int tCount = 0;
+    for (int index = 0; index < result; ++index) {
         double x, y;
-        xy_at_t(cubic, tRange[index], x, y);
+        xy_at_t(cubic, rootVals[index], x, y);
         if (x < left || x > right) {
-            if (--result > index) {
-                tRange[index] = tRange[result];
-            }
             continue;
         }
-        ++index;
+        tRange[tCount++] = rootVals[index];
     }
     return result;
 }
 
 int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
         bool flipped, Intersections& intersections) {
-    LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
-    int result = c.horizontalIntersect(y);
-    for (int index = 0; index < result; ) {
-        double x, y;
-        xy_at_t(cubic, intersections.fT[0][index], x, y);
-        if (x < left || x > right) {
-            if (--result > index) {
-                intersections.fT[0][index] = intersections.fT[0][result];
-            }
-            continue;
-        }
-        intersections.fT[1][index] = (x - left) / (right - left);
-        ++index;
-    }
-    if (flipped) {
-        // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
-        for (int index = 0; index < result; ++index) {
-            intersections.fT[1][index] = 1 - intersections.fT[1][index];
-        }
-    }
-    return result;
+    LineCubicIntersections c(cubic, *((_Line*) 0), intersections);
+    return c.horizontalIntersect(y, left, right, flipped);
 }
 
 int verticalIntersect(const Cubic& cubic, double top, double bottom, double x,
         bool flipped, Intersections& intersections) {
-    LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
-    int result = c.verticalIntersect(x);
-    for (int index = 0; index < result; ) {
-        double x, y;
-        xy_at_t(cubic, intersections.fT[0][index], x, y);
-        if (y < top || y > bottom) {
-            if (--result > index) {
-                intersections.fT[1][index] = intersections.fT[0][result];
-            }
-            continue;
-        }
-        intersections.fT[0][index] = (y - top) / (bottom - top);
-        ++index;
-    }
-    if (flipped) {
-        // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
-        for (int index = 0; index < result; ++index) {
-            intersections.fT[1][index] = 1 - intersections.fT[1][index];
-        }
-    }
-    return result;
+    LineCubicIntersections c(cubic, *((_Line*) 0), intersections);
+    return c.verticalIntersect(x, top, bottom, flipped);
 }
 
-int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) {
-    LineCubicIntersections c(cubic, line, cRange);
-    int roots;
-    if (approximately_equal(line[0].y, line[1].y)) {
-        roots = c.horizontalIntersect(line[0].y);
-    } else {
-        roots = c.intersect();
-    }
-    for (int index = 0; index < roots; ++index) {
-        lRange[index] = c.findLineT(cRange[index]);
-    }
-    return roots;
+int intersect(const Cubic& cubic, const _Line& line, Intersections& i) {
+    LineCubicIntersections c(cubic, line, i);
+    return c.intersect();
 }
diff --git a/experimental/Intersection/LineCubicIntersection_Test.cpp b/experimental/Intersection/LineCubicIntersection_Test.cpp
index cc993a7..b81a87e 100644
--- a/experimental/Intersection/LineCubicIntersection_Test.cpp
+++ b/experimental/Intersection/LineCubicIntersection_Test.cpp
@@ -36,8 +36,10 @@
             printf("[%d] line order=%d\n", (int) index, order2);
         }
         if (order1 == 4 && order2 == 2) {
-            double range1[2], range2[2];
-            int roots = intersect(reduce1, reduce2, range1, range2);
+            Intersections i;
+            double* range1 = i.fT[0];
+            double* range2 = i.fT[1];
+            int roots = intersect(reduce1, reduce2, i);
             for (int pt = 0; pt < roots; ++pt) {
                 double tt1 = range1[pt];
                 double tx1, ty1;
@@ -45,11 +47,11 @@
                 double tt2 = range2[pt];
                 double tx2, ty2;
                 xy_at_t(line, tt2, tx2, ty2);
-                if (!approximately_equal(tx1, tx2)) {
+                if (!AlmostEqualUlps(tx1, tx2)) {
                     printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                         __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
                 }
-                if (!approximately_equal(ty1, ty2)) {
+                if (!AlmostEqualUlps(ty1, ty2)) {
                     printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                         __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
                 }
diff --git a/experimental/Intersection/LineIntersection.cpp b/experimental/Intersection/LineIntersection.cpp
index c425cd1..f5501db 100644
--- a/experimental/Intersection/LineIntersection.cpp
+++ b/experimental/Intersection/LineIntersection.cpp
@@ -7,14 +7,29 @@
 #include "CurveIntersection.h"
 #include "Intersections.h"
 #include "LineIntersection.h"
-#include <algorithm> // used for std::swap
 
+/* Determine the intersection point of two lines. This assumes the lines are not parallel,
+   and that that the lines are infinite.
+   From http://en.wikipedia.org/wiki/Line-line_intersection
+ */
+void lineIntersect(const _Line& a, const _Line& b, _Point& p) {
+    double axLen = a[1].x - a[0].x;
+    double ayLen = a[1].y - a[0].y;
+    double bxLen = b[1].x - b[0].x;
+    double byLen = b[1].y - b[0].y;
+    double denom = byLen * axLen - ayLen * bxLen;
+    SkASSERT(denom);
+    double term1 = a[1].x * a[0].y - a[1].y * a[0].x;
+    double term2 = b[1].x * b[0].y - b[1].y * b[0].x;
+    p.x = (term1 * bxLen - axLen * term2) / denom;
+    p.y = (term1 * byLen - ayLen * term2) / denom;
+}
 
 /*
    Determine the intersection point of two line segments
    Return FALSE if the lines don't intersect
    from: http://paulbourke.net/geometry/lineline2d/
-*/
+ */
 
 int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]) {
     double axLen = a[1].x - a[0].x;
@@ -27,13 +42,14 @@
              byLen  * axLen         ==  ayLen          * bxLen
              byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
      */
-    double denom  = byLen * axLen - ayLen * bxLen;
+    double denom = byLen * axLen - ayLen * bxLen;
     if (approximately_zero(denom)) {
        /* See if the axis intercepts match:
                   ay - ax * ayLen / axLen  ==          by - bx * ayLen / axLen
          axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
          axLen *  ay - ax * ayLen          == axLen *  by - bx * ayLen
         */
+        // FIXME: need to use AlmostEqualUlps variant instead
         if (approximately_equal_squared(axLen * a[0].y - ayLen * a[0].x,
                 axLen * b[0].y - ayLen * b[0].x)) {
             const double* aPtr;
@@ -51,10 +67,10 @@
             double bMin = bPtr[0];
             double bMax = bPtr[2];
             if (aMin > aMax) {
-                std::swap(aMin, aMax);
+                SkTSwap(aMin, aMax);
             }
             if (bMin > bMax) {
-                std::swap(bMin, bMax);
+                SkTSwap(bMin, bMax);
             }
             if (aMax < bMin || bMax < aMin) {
                 return 0;
@@ -70,24 +86,32 @@
             }
             return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
         #else
-            assert(aRange);
-            assert(bRange);
+            SkASSERT(aRange);
+            SkASSERT(bRange);
             double a0 = aPtr[0];
             double a1 = aPtr[2];
             double b0 = bPtr[0];
             double b1 = bPtr[2];
+            // OPTIMIZATION: restructure to reject before the divide
+            // e.g., if ((a0 - b0) * (a0 - a1) < 0 || abs(a0 - b0) > abs(a0 - a1))
+            // (except efficient)
             double at0 = (a0 - b0) / (a0 - a1);
             double at1 = (a0 - b1) / (a0 - a1);
             if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
                 return 0;
             }
-            aRange[0] = std::max(std::min(at0, 1.0), 0.0);
-            aRange[1] = std::max(std::min(at1, 1.0), 0.0);
+            aRange[0] = SkTMax(SkTMin(at0, 1.0), 0.0);
+            aRange[1] = SkTMax(SkTMin(at1, 1.0), 0.0);
             int bIn = (a0 - a1) * (b0 - b1) < 0;
-            bRange[bIn] = std::max(std::min((b0 - a0) / (b0 - b1), 1.0), 0.0);
-            bRange[!bIn] = std::max(std::min((b0 - a1) / (b0 - b1), 1.0), 0.0);
+            double bDenom = b0 - b1;
+            if (approximately_zero(bDenom)) {
+                bRange[0] = bRange[1] = 0;
+            } else {
+                bRange[bIn] = SkTMax(SkTMin((b0 - a0) / bDenom, 1.0), 0.0);
+                bRange[!bIn] = SkTMax(SkTMin((b0 - a1) / bDenom, 1.0), 0.0);
+            }
             bool second = fabs(aRange[0] - aRange[1]) > FLT_EPSILON;
-            assert((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
+            SkASSERT((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
             return 1 + second;
         #endif
         }
@@ -116,12 +140,12 @@
     double min = line[0].y;
     double max = line[1].y;
     if (min > max) {
-        std::swap(min, max);
+        SkTSwap(min, max);
     }
     if (min > y || max < y) {
         return 0;
     }
-    if (approximately_equal(min, max)) {
+    if (AlmostEqualUlps(min, max)) {
         tRange[0] = 0;
         tRange[1] = 1;
         return 2;
@@ -172,10 +196,10 @@
             double lineL = line[0].x;
             double lineR = line[1].x;
             if (lineL > lineR) {
-                std::swap(lineL, lineR);
+                SkTSwap(lineL, lineR);
             }
-            double overlapL = std::max(left, lineL);
-            double overlapR = std::min(right, lineR);
+            double overlapL = SkTMax(left, lineL);
+            double overlapR = SkTMin(right, lineR);
             if (overlapL > overlapR) {
                 return 0;
             }
@@ -199,16 +223,16 @@
             if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
                 return 0;
             }
-            intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
-            intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
+            intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
+            intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
             int bIn = (a0 - a1) * (b0 - b1) < 0;
-            intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
+            intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
                     1.0), 0.0);
-            intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
+            intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
                     1.0), 0.0);
             bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
                     > FLT_EPSILON;
-            assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
+            SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
                     <= FLT_EPSILON) ^ second);
             return 1 + second;
         #endif
@@ -227,12 +251,12 @@
     double min = line[0].x;
     double max = line[1].x;
     if (min > max) {
-        std::swap(min, max);
+        SkTSwap(min, max);
     }
     if (min > x || max < x) {
         return 0;
     }
-    if (approximately_equal(min, max)) {
+    if (AlmostEqualUlps(min, max)) {
         tRange[0] = 0;
         tRange[1] = 1;
         return 2;
@@ -261,10 +285,10 @@
             double lineT = line[0].y;
             double lineB = line[1].y;
             if (lineT > lineB) {
-                std::swap(lineT, lineB);
+                SkTSwap(lineT, lineB);
             }
-            double overlapT = std::max(top, lineT);
-            double overlapB = std::min(bottom, lineB);
+            double overlapT = SkTMax(top, lineT);
+            double overlapB = SkTMin(bottom, lineB);
             if (overlapT > overlapB) {
                 return 0;
             }
@@ -288,16 +312,16 @@
             if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
                 return 0;
             }
-            intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
-            intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
+            intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
+            intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
             int bIn = (a0 - a1) * (b0 - b1) < 0;
-            intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
+            intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
                     1.0), 0.0);
-            intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
+            intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
                     1.0), 0.0);
             bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
                     > FLT_EPSILON;
-            assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
+            SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
                     <= FLT_EPSILON) ^ second);
             return 1 + second;
         #endif
diff --git a/experimental/Intersection/LineIntersection.h b/experimental/Intersection/LineIntersection.h
index 33076b6..adb5789 100644
--- a/experimental/Intersection/LineIntersection.h
+++ b/experimental/Intersection/LineIntersection.h
@@ -12,9 +12,10 @@
 int horizontalIntersect(const _Line& line, double y, double tRange[2]);
 int horizontalLineIntersect(const _Line& line, double left, double right,
         double y, double tRange[2]);
-int verticalLineIntersect(const _Line& line, double top, double bottom,
-        double x, double tRange[2]);
+void lineIntersect(const _Line& a, const _Line& b, _Point& p);
 int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]);
 bool testIntersect(const _Line& a, const _Line& b);
+int verticalLineIntersect(const _Line& line, double top, double bottom,
+        double x, double tRange[2]);
 
 #endif
diff --git a/experimental/Intersection/LineParameterization.cpp b/experimental/Intersection/LineParameterization.cpp
index 4ef6fbd..1973945 100644
--- a/experimental/Intersection/LineParameterization.cpp
+++ b/experimental/Intersection/LineParameterization.cpp
@@ -19,7 +19,7 @@
           (dy1 * dy2) * dx1 / dy1 == (dy1 * dy2) * dx2 / dy2
                  dy2  * dx1       ==  dy1        * dx2
      */
-    if (!approximately_equal(oneD.x * twoD.y, twoD.x * oneD.y)) {
+    if (!AlmostEqualUlps(oneD.x * twoD.y, twoD.x * oneD.y)) {
         return false;
     }
     /* See if the axis intercepts match, i.e.
@@ -27,7 +27,7 @@
          dx * (y0 - x0 * dy / dx) == dx * (y1 - x1 * dy / dx)
          dx *  y0 - x0 * dy       == dx *  y1 - x1 * dy
      */
-    if (!approximately_equal(oneD.x * one[0].y - oneD.y * one[0].x,
+    if (!AlmostEqualUlps(oneD.x * one[0].y - oneD.y * one[0].x,
             oneD.x * two[0].y - oneD.y * two[0].x)) {
         return false;
     }
diff --git a/experimental/Intersection/LineParameters.h b/experimental/Intersection/LineParameters.h
index fc1bcc8..637b3b6 100644
--- a/experimental/Intersection/LineParameters.h
+++ b/experimental/Intersection/LineParameters.h
@@ -80,15 +80,9 @@
         }
     }
 
-    void controlPtDistance(const Cubic& pts, double distance[2]) const {
-        for (int index = 0; index < 2; ++index) {
-            distance[index] = a * pts[index + 1].x + b * pts[index + 1].y + c;
-        }
-    }
-
-    void controlPtDistance(const Cubic& pts, int i, int j, double distance[2]) const {
-        distance[0] = a * pts[i].x + b * pts[i].y + c;
-        distance[1] = a * pts[j].x + b * pts[j].y + c;
+    double controlPtDistance(const Cubic& pts, int index) const {
+        SkASSERT(index == 1 || index == 2);
+        return a * pts[index].x + b * pts[index].y + c;
     }
 
     double controlPtDistance(const Quadratic& pts) const {
diff --git a/experimental/Intersection/LineParameteters_Test.cpp b/experimental/Intersection/LineParameteters_Test.cpp
index e8adf33..462ca16 100644
--- a/experimental/Intersection/LineParameteters_Test.cpp
+++ b/experimental/Intersection/LineParameteters_Test.cpp
@@ -45,7 +45,8 @@
         const Cubic& cubic = tests[index];
         lineParameters.cubicEndPoints(cubic);
         double denormalizedDistance[2];
-        lineParameters.controlPtDistance(cubic, denormalizedDistance);
+        denormalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1);
+        denormalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2);
         double normalSquared = lineParameters.normalSquared();
         size_t inner;
         for (inner = 0; inner < 2; ++inner) {
@@ -53,10 +54,10 @@
             distSq *= distSq;
             double answersSq = answers[index][inner];
             answersSq *= answersSq;
-            if (approximately_equal(distSq, normalSquared * answersSq)) {
+            if (AlmostEqualUlps(distSq, normalSquared * answersSq)) {
                 continue;
             }
-            printf("%s [%d,%d] denormalizedDistance:%g != answer:%g"
+            SkDebugf("%s [%d,%d] denormalizedDistance:%g != answer:%g"
                     " distSq:%g answerSq:%g normalSquared:%g\n",
                     __FUNCTION__, (int)index, (int)inner,
                     denormalizedDistance[inner], answers[index][inner],
@@ -64,13 +65,13 @@
         }
         lineParameters.normalize();
         double normalizedDistance[2];
-        lineParameters.controlPtDistance(cubic, normalizedDistance);
+        normalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1);
+        normalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2);
         for (inner = 0; inner < 2; ++inner) {
-            if (approximately_equal(fabs(normalizedDistance[inner]),
-                    answers[index][inner])) {
+            if (AlmostEqualUlps(fabs(normalizedDistance[inner]), answers[index][inner])) {
                 continue;
             }
-            printf("%s [%d,%d] normalizedDistance:%1.10g != answer:%g\n",
+            SkDebugf("%s [%d,%d] normalizedDistance:%1.10g != answer:%g\n",
                     __FUNCTION__, (int)index, (int)inner,
                     normalizedDistance[inner], answers[index][inner]);
         }
diff --git a/experimental/Intersection/LineQuadraticIntersection.cpp b/experimental/Intersection/LineQuadraticIntersection.cpp
index b3303cf..9ec3fb7 100644
--- a/experimental/Intersection/LineQuadraticIntersection.cpp
+++ b/experimental/Intersection/LineQuadraticIntersection.cpp
@@ -124,7 +124,7 @@
     double C = r[0];
     A += C - 2 * B; // A = a - 2*b + c
     B -= C; // B = -(b - c)
-    return quadraticRoots(A, B, C, roots);
+    return quadraticRootsValidT(A, 2 * B, C, roots);
 }
 
 int intersect() {
@@ -148,7 +148,7 @@
     D += F - 2 * E; // D = d - 2*e + f
     E -= F; // E = -(d - e)
     F -= axisIntercept;
-    return quadraticRoots(D, E, F, roots);
+    return quadraticRootsValidT(D, 2 * E, F, roots);
 }
 
 int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
@@ -177,7 +177,7 @@
     D += F - 2 * E; // D = d - 2*e + f
     E -= F; // E = -(d - e)
     F -= axisIntercept;
-    return quadraticRoots(D, E, F, roots);
+    return quadraticRootsValidT(D, 2 * E, F, roots);
 }
 
 int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
@@ -298,7 +298,7 @@
         double x;
         double t = rootVals[index];
         xy_at_t(quad, t, x, *(double*) 0);
-        if (approximately_equal(x, pt.x)) {
+        if (AlmostEqualUlps(x, pt.x)) {
             return t;
         }
     }
@@ -313,7 +313,7 @@
         double y;
         double t = rootVals[index];
         xy_at_t(quad, t, *(double*) 0, y);
-        if (approximately_equal(y, pt.y)) {
+        if (AlmostEqualUlps(y, pt.y)) {
             return t;
         }
     }
diff --git a/experimental/Intersection/LineQuadraticIntersection_Test.cpp b/experimental/Intersection/LineQuadraticIntersection_Test.cpp
index 69227c3..6c3986c 100644
--- a/experimental/Intersection/LineQuadraticIntersection_Test.cpp
+++ b/experimental/Intersection/LineQuadraticIntersection_Test.cpp
@@ -80,8 +80,8 @@
             double lineT = intersections.fT[1][inner];
             double lineX, lineY;
             xy_at_t(line, lineT, lineX, lineY);
-            assert(approximately_equal(quadX, lineX)
-                    && approximately_equal(quadY, lineY));
+            SkASSERT(AlmostEqualUlps(quadX, lineX)
+                    && AlmostEqualUlps(quadY, lineY));
         }
     }
 }
@@ -120,12 +120,12 @@
             double tt2 = intersections.fT[1][pt];
             SkASSERT(tt2 >= 0 && tt2 <= 1);
             xy_at_t(line, tt2, t2.x, t2.y);
-            if (!approximately_equal(t1.x, t2.x)) {
+            if (!AlmostEqualUlps(t1.x, t2.x)) {
                 SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
                     __FUNCTION__, (int)index, pt, tt1, t1.x, t1.y, tt2, t2.x, t2.y);
                 SkASSERT(0);
             }
-            if (!approximately_equal(t1.y, t2.y)) {
+            if (!AlmostEqualUlps(t1.y, t2.y)) {
                 SkDebugf("%s [%d,%d] y!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
                     __FUNCTION__, (int)index, pt, tt1, t1.x, t1.y, tt2, t2.x, t2.y);
                 SkASSERT(0);
diff --git a/experimental/Intersection/LineUtilities.cpp b/experimental/Intersection/LineUtilities.cpp
index 25bd88d..fa756b3 100644
--- a/experimental/Intersection/LineUtilities.cpp
+++ b/experimental/Intersection/LineUtilities.cpp
@@ -90,7 +90,7 @@
 
 void x_at(const _Point& p1, const _Point& p2, double top, double bottom,
         int flags, double& minX, double& maxX) {
-    if (approximately_equal(p1.y, p2.y)) {
+    if (AlmostEqualUlps(p1.y, p2.y)) {
         // It should be OK to bail early in this case. There's another edge
         // which shares this end point which can intersect without failing to
         // have a slope ... maybe
diff --git a/experimental/Intersection/LineUtilities.h b/experimental/Intersection/LineUtilities.h
index 49e6a32..9d53812 100644
--- a/experimental/Intersection/LineUtilities.h
+++ b/experimental/Intersection/LineUtilities.h
@@ -8,7 +8,7 @@
 
 bool implicitLine(const _Line& line, double& slope, double& axisIntercept);
 int reduceOrder(const _Line& line, _Line& reduced);
-
+void sub_divide(const _Line& src, double t1, double t2, _Line& dst);
 double t_at(const _Line&, const _Point& );
 void xy_at_t(const _Line& , double t, double& x, double& y);
 
@@ -21,4 +21,3 @@
 
 void x_at(const _Point& p1, const _Point& p2, double minY, double maxY,
         int flags, double& tMin, double& tMax);
-
diff --git a/experimental/Intersection/QuadraticBezierClip.cpp b/experimental/Intersection/QuadraticBezierClip.cpp
index 6100914..0e948a0 100644
--- a/experimental/Intersection/QuadraticBezierClip.cpp
+++ b/experimental/Intersection/QuadraticBezierClip.cpp
@@ -7,7 +7,6 @@
 #include "CurveIntersection.h"
 #include "CurveUtilities.h"
 #include "LineParameters.h"
-#include <algorithm> // used for std::swap
 
 #define DEBUG_BEZIER_CLIP 1
 
@@ -24,7 +23,7 @@
     endLine.quadEndPoints(q1);
     if (!endLine.normalize()) {
         printf("line cannot be normalized: need more code here\n");
-        assert(0);
+        SkASSERT(0);
         return false;
     }
 
@@ -34,7 +33,7 @@
     double top = 0;
     double bottom = distance / 2; // http://students.cs.byu.edu/~tom/557/text/cic.pdf (7.6)
     if (top > bottom) {
-        std::swap(top, bottom);
+        SkTSwap(top, bottom);
     }
 
     // compute intersecting candidate distance
diff --git a/experimental/Intersection/QuadraticBezierClip_Test.cpp b/experimental/Intersection/QuadraticBezierClip_Test.cpp
index 4c6f0d7..0d25faf 100644
--- a/experimental/Intersection/QuadraticBezierClip_Test.cpp
+++ b/experimental/Intersection/QuadraticBezierClip_Test.cpp
@@ -50,10 +50,10 @@
         int order1 = reduceOrder(quad1, reduce1);
         int order2 = reduceOrder(quad2, reduce2);
         if (order1 < 3) {
-            printf("%s [%d] quad1 order=%d\n", __FUNCTION__, (int)index, order1);
+            SkDebugf("%s [%d] quad1 order=%d\n", __FUNCTION__, (int)index, order1);
         }
         if (order2 < 3) {
-            printf("%s [%d] quad2 order=%d\n", __FUNCTION__, (int)index, order2);
+            SkDebugf("%s [%d] quad2 order=%d\n", __FUNCTION__, (int)index, order2);
         }
         if (order1 == 3 && order2 == 3) {
             double minT = 0;
diff --git a/experimental/Intersection/QuadraticImplicit.cpp b/experimental/Intersection/QuadraticImplicit.cpp
index d892ae9..660ffe5 100644
--- a/experimental/Intersection/QuadraticImplicit.cpp
+++ b/experimental/Intersection/QuadraticImplicit.cpp
@@ -5,11 +5,17 @@
 // http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
 
 
+#include "CubicUtilities.h"
 #include "CurveIntersection.h"
 #include "Intersections.h"
 #include "QuadraticParameterization.h"
 #include "QuarticRoot.h"
 #include "QuadraticUtilities.h"
+#include "TSearch.h"
+
+#if SK_DEBUG
+#include "LineUtilities.h"
+#endif
 
 /* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F
  * and given x = at^2 + bt + c  (the parameterized form)
@@ -18,7 +24,8 @@
  * 0 = A(at^2+bt+c)(at^2+bt+c)+B(at^2+bt+c)(dt^2+et+f)+C(dt^2+et+f)(dt^2+et+f)+D(at^2+bt+c)+E(dt^2+et+f)+F
  */
 
-static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double roots[4]) {
+static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double roots[4],
+        bool oneHint) {
     double a, b, c;
     set_abc(&q2[0].x, a, b, c);
     double d, e, f;
@@ -45,7 +52,11 @@
                     +     i.x()  *  c
                     +     i.y()  *  f
                     +     i.c();
-    return quarticRoots(t4, t3, t2, t1, t0, roots);
+    int rootCount = reducedQuarticRoots(t4, t3, t2, t1, t0, oneHint, roots);
+    if (rootCount >= 0) {
+        return rootCount;
+    }
+    return quarticRootsReal(t4, t3, t2, t1, t0, roots);
 }
 
 static void addValidRoots(const double roots[4], const int count, const int side, Intersections& i) {
@@ -83,7 +94,9 @@
         double adj = endPt[1]->x - origX;
         double opp = endPt[1]->y - origY;
         double sign = (q1[oddMan].y - origY) * adj - (q1[oddMan].x - origX) * opp;
-        assert(!approximately_zero(sign));
+        if (approximately_zero(sign)) {
+            goto tryNextHalfPlane;
+        }
         for (int n = 0; n < 3; ++n) {
             double test = (q2[n].y - origY) * adj - (q2[n].x - origX) * opp;
             if (test * sign > 0) {
@@ -97,7 +110,7 @@
                 }
             }
         }
-        assert(i.fUsed < 3);
+        SkASSERT(i.fUsed < 3);
         return true;
 tryNextHalfPlane:
         ;
@@ -105,12 +118,265 @@
     return false;
 }
 
+// http://www.blackpawn.com/texts/pointinpoly/default.html
+static bool pointInTriangle(const _Point& pt, const _Line* testLines[]) {
+    const _Point& A = (*testLines[0])[0];
+    const _Point& B = (*testLines[1])[0];
+    const _Point& C = (*testLines[2])[0];
+
+// Compute vectors
+    _Point v0 = C - A;
+    _Point v1 = B - A;
+    _Point v2 = pt - A;
+
+// Compute dot products
+    double dot00 = v0.dot(v0);
+    double dot01 = v0.dot(v1);
+    double dot02 = v0.dot(v2);
+    double dot11 = v1.dot(v1);
+    double dot12 = v1.dot(v2);
+
+// Compute barycentric coordinates
+    double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+    double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+    double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+// Check if point is in triangle
+    return (u >= 0) && (v >= 0) && (u + v < 1);
+}
+
+// returns false if there's more than one intercept or the intercept doesn't match the point
+// returns true if the intercept was successfully added or if the
+// original quads need to be subdivided
+static bool addIntercept(const Quadratic& q1, const Quadratic& q2, double tMin, double tMax,
+        Intersections& i, bool* subDivide) {
+    double tMid = (tMin + tMax) / 2;
+    _Point mid;
+    xy_at_t(q2, tMid, mid.x, mid.y);
+    _Line line;
+    line[0] = line[1] = mid;
+    _Point dxdy;
+    dxdy_at_t(q2, tMid, dxdy);
+    line[0].x -= dxdy.x;
+    line[0].y -= dxdy.y;
+    line[1].x += dxdy.x;
+    line[1].y += dxdy.y;
+    Intersections rootTs;
+    int roots = intersect(q1, line, rootTs);
+    if (roots == 0) {
+        if (subDivide) {
+            *subDivide = true;
+        }
+        return true;
+    }
+    if (roots == 2) {
+        return false;
+    }
+    _Point pt2;
+    xy_at_t(q1, rootTs.fT[0][0], pt2.x, pt2.y);
+    if (!pt2.approximatelyEqual(mid)) {
+        return false;
+    }
+    i.add(rootTs.fT[0][0], tMid);
+    return true;
+}
+
+static bool isLinearInner(const Quadratic& q1, double t1s, double t1e, const Quadratic& q2,
+        double t2s, double t2e, Intersections& i, bool* subDivide) {
+    Quadratic hull;
+    sub_divide(q1, t1s, t1e, hull);
+    _Line line = {hull[2], hull[0]};
+    const _Line* testLines[] = { &line, (const _Line*) &hull[0], (const _Line*) &hull[1] };
+    size_t testCount = sizeof(testLines) / sizeof(testLines[0]);
+    SkTDArray<double> tsFound;
+    for (size_t index = 0; index < testCount; ++index) {
+        Intersections rootTs;
+        int roots = intersect(q2, *testLines[index], rootTs);
+        for (int idx2 = 0; idx2 < roots; ++idx2) {
+            double t = rootTs.fT[0][idx2];
+#if SK_DEBUG
+        _Point qPt, lPt;
+        xy_at_t(q2, t, qPt.x, qPt.y);
+        xy_at_t(*testLines[index], rootTs.fT[1][idx2], lPt.x, lPt.y);
+        SkASSERT(qPt.approximatelyEqual(lPt));
+#endif
+            if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
+                continue;
+            }
+            *tsFound.append() = rootTs.fT[0][idx2];
+        }
+    }
+    int tCount = tsFound.count();
+    if (!tCount) {
+        return true;
+    }
+    double tMin, tMax;
+    _Point dxy1, dxy2;
+    if (tCount == 1) {
+        tMin = tMax = tsFound[0];
+    } else if (tCount > 1) {
+        QSort<double>(tsFound.begin(), tsFound.end() - 1);
+        tMin = tsFound[0];
+        tMax = tsFound[1];
+    }
+    _Point end;
+    xy_at_t(q2, t2s, end.x, end.y);
+    bool startInTriangle = pointInTriangle(end, testLines);
+    if (startInTriangle) {
+        tMin = t2s;
+    }
+    xy_at_t(q2, t2e, end.x, end.y);
+    bool endInTriangle = pointInTriangle(end, testLines);
+    if (endInTriangle) {
+        tMax = t2e;
+    }
+    int split = 0;
+    if (tMin != tMax || tCount > 2) {
+        dxdy_at_t(q2, tMin, dxy2);
+        for (int index = 1; index < tCount; ++index) {
+            dxy1 = dxy2;
+            dxdy_at_t(q2, tsFound[index], dxy2);
+            double dot = dxy1.dot(dxy2);
+            if (dot < 0) {
+                split = index - 1;
+                break;
+            }
+        }
+
+    }
+    if (split == 0) { // there's one point
+        if (addIntercept(q1, q2, tMin, tMax, i, subDivide)) {
+            return true;
+        }
+        i.swap();
+        return isLinearInner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide);
+    }
+    // At this point, we have two ranges of t values -- treat each separately at the split
+    bool result;
+    if (addIntercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
+        result = true;
+    } else {
+        i.swap();
+        result = isLinearInner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
+    }
+    if (addIntercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
+        result = true;
+    } else {
+        i.swap();
+        result |= isLinearInner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
+    }
+    return result;
+}
+
+static double flatMeasure(const Quadratic& q) {
+    _Point mid = q[1];
+    mid -= q[0];
+    _Point dxy = q[2];
+    dxy -= q[0];
+    double length = dxy.length(); // OPTIMIZE: get rid of sqrt
+    return fabs(mid.cross(dxy) / length);
+}
+
+// FIXME ? should this measure both and then use the quad that is the flattest as the line?
+static bool isLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+    double measure = flatMeasure(q1);
+    // OPTIMIZE: (get rid of sqrt) use approximately_zero
+    if (!approximately_zero_sqrt(measure)) {
+        return false;
+    }
+    return isLinearInner(q1, 0, 1, q2, 0, 1, i, NULL);
+}
+
+// FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
+static void relaxedIsLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+    double m1 = flatMeasure(q1);
+    double m2 = flatMeasure(q2);
+#if SK_DEBUG
+    double min = SkTMin(m1, m2);
+    if (min > 5) {
+        SkDebugf("%s maybe not flat enough.. %1.9g\n", __FUNCTION__, min);
+    }
+#endif
+    i.reset();
+    const Quadratic& rounder = m2 < m1 ? q1 : q2;
+    const Quadratic& flatter = m2 < m1 ? q2 : q1;
+    bool subDivide = false;
+    isLinearInner(flatter, 0, 1, rounder, 0, 1, i, &subDivide);
+    if (subDivide) {
+        QuadraticPair pair;
+        chop_at(flatter, pair, 0.5);
+        Intersections firstI, secondI;
+        relaxedIsLinear(pair.first(), rounder, firstI);
+        for (int index = 0; index < firstI.used(); ++index) {
+            i.insert(firstI.fT[0][index] * 0.5, firstI.fT[1][index]);
+        }
+        relaxedIsLinear(pair.second(), rounder, secondI);
+        for (int index = 0; index < secondI.used(); ++index) {
+            i.insert(0.5 + secondI.fT[0][index] * 0.5, secondI.fT[1][index]);
+        }
+    }
+    if (m2 < m1) {
+        i.swapPts();
+    }
+}
+
+#if 0
+static void unsortableExpanse(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+    const Quadratic* qs[2] = { &q1, &q2 };
+    // need t values for start and end of unsortable expanse on both curves
+    // try projecting lines parallel to the end points
+    i.fT[0][0] = 0;
+    i.fT[0][1] = 1;
+    int flip = -1; // undecided
+    for (int qIdx = 0; qIdx < 2; qIdx++) {
+        for (int t = 0; t < 2; t++) {
+            _Point dxdy;
+            dxdy_at_t(*qs[qIdx], t, dxdy);
+            _Line perp;
+            perp[0] = perp[1] = (*qs[qIdx])[t == 0 ? 0 : 2];
+            perp[0].x += dxdy.y;
+            perp[0].y -= dxdy.x;
+            perp[1].x -= dxdy.y;
+            perp[1].y += dxdy.x;
+            Intersections hitData;
+            int hits = intersectRay(*qs[qIdx ^ 1], perp, hitData);
+            SkASSERT(hits <= 1);
+            if (hits) {
+                if (flip < 0) {
+                    _Point dxdy2;
+                    dxdy_at_t(*qs[qIdx ^ 1], hitData.fT[0][0], dxdy2);
+                    double dot = dxdy.dot(dxdy2);
+                    flip = dot < 0;
+                    i.fT[1][0] = flip;
+                    i.fT[1][1] = !flip;
+                }
+                i.fT[qIdx ^ 1][t ^ flip] = hitData.fT[0][0];
+            }
+        }
+    }
+    i.fUnsortable = true; // failed, probably coincident or near-coincident
+    i.fUsed = 2;
+}
+#endif
+
 bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
     // if the quads share an end point, check to see if they overlap
 
     if (onlyEndPtsInCommon(q1, q2, i)) {
         return i.intersected();
     }
+    if (onlyEndPtsInCommon(q2, q1, i)) {
+        i.swapPts();
+        return i.intersected();
+    }
+    // see if either quad is really a line
+    if (isLinear(q1, q2, i)) {
+        return i.intersected();
+    }
+    if (isLinear(q2, q1, i)) {
+        i.swapPts();
+        return i.intersected();
+    }
     QuadImplicitForm i1(q1);
     QuadImplicitForm i2(q2);
     if (i1.implicit_match(i2)) {
@@ -131,19 +397,16 @@
         if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
             i.addCoincident(1, t);
         }
-        assert(i.fCoincidentUsed <= 2);
+        SkASSERT(i.fCoincidentUsed <= 2);
         return i.fCoincidentUsed > 0;
     }
     double roots1[4], roots2[4];
-    int rootCount = findRoots(i2, q1, roots1);
+    bool useCubic = q1[0] == q2[0] || q1[0] == q2[2] || q1[2] == q2[0];
+    int rootCount = findRoots(i2, q1, roots1, useCubic);
     // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
-#ifndef NDEBUG
-    int rootCount2 =
-#endif
-        findRoots(i1, q2, roots2);
-    assert(rootCount == rootCount2);
+    int rootCount2 = findRoots(i1, q2, roots2, useCubic);
     addValidRoots(roots1, rootCount, 0, i);
-    addValidRoots(roots2, rootCount, 1, i);
+    addValidRoots(roots2, rootCount2, 1, i);
     if (i.insertBalanced() && i.fUsed <= 1) {
         if (i.fUsed == 1) {
             _Point xy1, xy2;
@@ -157,16 +420,13 @@
         return i.intersected();
     }
     _Point pts[4];
-    bool matches[4];
-    int flipCheck[4];
     int closest[4];
     double dist[4];
     int index, ndex2;
-    int flipIndex = 0;
     for (ndex2 = 0; ndex2 < i.fUsed2; ++ndex2) {
         xy_at_t(q2, i.fT[1][ndex2], pts[ndex2].x, pts[ndex2].y);
-        matches[ndex2] = false;
     }
+    bool foundSomething = false;
     for (index = 0; index < i.fUsed; ++index) {
         _Point xy;
         xy_at_t(q1, i.fT[0][index], xy.x, xy.y);
@@ -193,38 +453,39 @@
             }
             dist[index] = distance;
             closest[index] = ndex2;
+            foundSomething = true;
         next:
             ;
         }
     }
-    for (index = 0; index < i.fUsed; ) {
-        for (ndex2 = 0; ndex2 < i.fUsed2; ++ndex2) {
-             if (closest[index] == ndex2) {
-                assert(flipIndex < 4);
-                flipCheck[flipIndex++] = ndex2;
-                matches[ndex2] = true;
-                goto next2;
-             }
-        }
-        if (--i.fUsed > index) {
-            memmove(&i.fT[0][index], &i.fT[0][index + 1], (i.fUsed - index) * sizeof(i.fT[0][0]));
-            memmove(&closest[index], &closest[index + 1], (i.fUsed - index) * sizeof(closest[0]));
-            continue;
-        }
-    next2:
-        ++index;
+    if (i.fUsed && i.fUsed2 && !foundSomething) {
+        relaxedIsLinear(q1, q2, i);
+        return i.intersected();
     }
-    for (ndex2 = 0; ndex2 < i.fUsed2; ) {
-        if (!matches[ndex2]) {
-             if (--i.fUsed2 > ndex2) {
-                memmove(&i.fT[1][ndex2], &i.fT[1][ndex2 + 1], (i.fUsed2 - ndex2) * sizeof(i.fT[1][0]));
-                memmove(&matches[ndex2], &matches[ndex2 + 1], (i.fUsed2 - ndex2) * sizeof(matches[0]));
+    double roots1Copy[4], roots2Copy[4];
+    memcpy(roots1Copy, i.fT[0], i.fUsed * sizeof(double));
+    memcpy(roots2Copy, i.fT[1], i.fUsed2 * sizeof(double));
+    int used = 0;
+    do {
+        double lowest = DBL_MAX;
+        int lowestIndex = -1;
+        for (index = 0; index < i.fUsed; ++index) {
+            if (closest[index] < 0) {
                 continue;
-             }
+            }
+            if (roots1Copy[index] < lowest) {
+                lowestIndex = index;
+                lowest = roots1Copy[index];
+            }
         }
-        ++ndex2;
-    }
-    i.fFlip = i.fUsed >= 2 && flipCheck[0] > flipCheck[1];
-    assert(i.insertBalanced());
+        if (lowestIndex < 0) {
+            break;
+        }
+        i.fT[0][used] = roots1Copy[lowestIndex];
+        i.fT[1][used] = roots2Copy[closest[lowestIndex]];
+        closest[lowestIndex] = -1;
+    } while (++used < i.fUsed);
+    i.fUsed = i.fUsed2 = used;
+    i.fFlip = false;
     return i.intersected();
 }
diff --git a/experimental/Intersection/QuadraticIntersection.cpp b/experimental/Intersection/QuadraticIntersection.cpp
index 800964d..07b8ecf 100644
--- a/experimental/Intersection/QuadraticIntersection.cpp
+++ b/experimental/Intersection/QuadraticIntersection.cpp
@@ -72,22 +72,22 @@
                 largeT = interp(minT2, maxT2, minT);
                 xy_at_t(quad2, largeT, q2pt.x, q2pt.y);
                 xy_at_t(quad1, minT1, q1pt.x, q1pt.y);
-                if (approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y)) {
+                if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) {
                     smallT = minT1;
                 } else {
                     xy_at_t(quad1, maxT1, q1pt.x, q1pt.y); // FIXME: debug code
-                    assert(approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y));
+                    SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y));
                     smallT = maxT1;
                 }
             } else {
                 smallT = interp(minT1, maxT1, minT);
                 xy_at_t(quad1, smallT, q1pt.x, q1pt.y);
                 xy_at_t(quad2, minT2, q2pt.x, q2pt.y);
-                if (approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y)) {
+                if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) {
                     largeT = minT2;
                 } else {
                     xy_at_t(quad2, maxT2, q2pt.x, q2pt.y); // FIXME: debug code
-                    assert(approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y));
+                    SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y));
                     largeT = maxT2;
                 }
             }
@@ -127,9 +127,9 @@
 {
     _Line line1, line2;
     if (intersections.swapped()) {
-        std::swap(treat1AsLine, treat2AsLine);
-        std::swap(minT1, minT2);
-        std::swap(maxT1, maxT2);
+        SkTSwap(treat1AsLine, treat2AsLine);
+        SkTSwap(minT1, minT2);
+        SkTSwap(maxT1, maxT2);
     }
     if (coinMinT1 >= 0) {
         bool earlyExit;
@@ -180,8 +180,8 @@
             xy_at_t(treat1AsLine ? quad2 : quad1, midQuadT, midQuad.x, midQuad.y);
             double lineT = t_at(treat1AsLine ? line1 : line2, midQuad);
             xy_at_t(treat1AsLine ? line1 : line2, lineT, midLine.x, midLine.y);
-            if (approximately_equal(midQuad.x, midLine.x)
-                    && approximately_equal(midQuad.y, midLine.y)) {
+            if (AlmostEqualUlps(midQuad.x, midLine.x)
+                    && AlmostEqualUlps(midQuad.y, midLine.y)) {
                 smallT1 = lq.fT[0][0];
                 largeT1 = lq.fT[1][0];
                 smallT2 = lq.fT[0][1];
@@ -331,7 +331,7 @@
         _Line ends1;
         xy_at_t(q1, start1, ends1[0].x, ends1[0].y);
         xy_at_t(q1, end1, ends1[1].x, ends1[1].y);
-        if (!approximately_equal(ends1[0].x, ends1[1].x) || approximately_equal(ends1[0].y, ends1[1].y)) {
+        if (!AlmostEqualUlps(ends1[0].x, ends1[1].x) || AlmostEqualUlps(ends1[0].y, ends1[1].y)) {
             cIndex += 2;
             continue;
         }
@@ -341,7 +341,7 @@
         xy_at_t(q2, start2, ends2[0].x, ends2[0].y);
         xy_at_t(q2, end2, ends2[1].x, ends2[1].y);
         // again, approximately should be used with T values, not points FIXME
-        if (!approximately_equal(ends2[0].x, ends2[1].x) || approximately_equal(ends2[0].y, ends2[1].y)) {
+        if (!AlmostEqualUlps(ends2[0].x, ends2[1].x) || AlmostEqualUlps(ends2[0].y, ends2[1].y)) {
             cIndex += 2;
             continue;
         }
@@ -388,7 +388,7 @@
         if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
             i.addCoincident(1, t);
         }
-        assert(i.fCoincidentUsed <= 2);
+        SkASSERT(i.fCoincidentUsed <= 2);
         return i.fCoincidentUsed > 0;
     }
     QuadraticIntersections q(q1, q2, i);
diff --git a/experimental/Intersection/QuadraticIntersection_Test.cpp b/experimental/Intersection/QuadraticIntersection_Test.cpp
index bab6c73..41ccb8b 100644
--- a/experimental/Intersection/QuadraticIntersection_Test.cpp
+++ b/experimental/Intersection/QuadraticIntersection_Test.cpp
@@ -10,7 +10,6 @@
 #include "Intersections.h"
 #include "QuadraticIntersection_TestData.h"
 #include "TestUtilities.h"
-#include "SkTypes.h"
 
 const int firstQuadIntersectionTest = 9;
 
@@ -59,9 +58,59 @@
 }
 
 static const Quadratic testSet[] = {
+  {{41.5072916,87.1234036}, {28.2747836,80.9545395}, {23.5780771,69.3344126}},
+  {{72.9633878,95.6593007}, {42.7738746,88.4730382}, {31.1932785,80.2458029}},
+
+  {{31.1663962,54.7302484}, {31.1662882,54.7301074}, {31.1663969,54.7302485}},
+  {{26.0404936,45.4260361}, {27.7887523,33.1863051}, {40.8833242,26.0301855}},
+
+  {{29.9404074,49.1672596}, {44.3131071,45.3915253}, {58.1067559,59.5061814}},
+  {{72.6510251,64.2972928}, {53.6989659,60.1862397}, {35.2053722,44.8391126}},
+
+{{52.14807018377202, 65.012420045148644}, {44.778669050208237, 66.315562705604378}, {51.619118408823567, 63.787827046262684}},
+{{30.004993234763383, 93.921296668202288}, {53.384822003076991, 60.732180341802753}, {58.652998934338584, 43.111073088306185}},
+
+{{80.897794748143198, 49.236332042718459}, {81.082078218891212, 64.066749904488631}, {69.972305057149981, 72.968595519850993}},
+{{72.503745601281395, 32.952320736577882}, {88.030880716061645, 38.137194847810164}, {73.193774825517906, 67.773492479591397}},
+
+{{67.426548091427676, 37.993772624988935}, {51.129513170665035, 57.542281234563646}, {44.594748190899189, 65.644267382683879}},
+{{61.336508189019057, 82.693132843213675}, {54.825078921449354, 71.663932799212432}, {47.727444217558926, 61.4049645128392}},
+
+{{67.4265481,37.9937726}, {51.1295132,57.5422812}, {44.5947482,65.6442674}},
+{{61.3365082,82.6931328}, {54.8250789,71.6639328}, {47.7274442,61.4049645}},
+
+{{53.774852327053594, 53.318060789841951}, {45.787877803416805, 51.393492026284981}, {46.703936967162392, 53.06860709822206}},
+{{46.703936967162392, 53.06860709822206}, {47.619996130907957, 54.74372217015916}, {53.020051653535361, 48.633140968832024}},
+
+{{50.934805397717923, 51.52391952648901}, {56.803308902971423, 44.246234610627596}, {69.776888596721406, 40.166645096692555}},
+{{50.230212796400401, 38.386469101526998}, {49.855620812184917, 38.818990392153609}, {56.356567496227363, 47.229909093319407}},
+
+{{36.148792695174222, 70.336952793070424}, {36.141613037691357, 70.711654739870085}, {36.154708826402597, 71.088492662905836}},
+{{35.216235592661825, 70.580199617313212}, {36.244476835123969, 71.010897787304074}, {37.230244263238326, 71.423156953613102}},
+
+// this pair is nearly coincident, and causes the quartic code to produce bad
+// data. Mathematica doesn't think they touch. Graphically, I can't tell.
+// it may not be so bad to pretend that they don't touch, if I can detect that
+{{369.848602,145.680267}, {382.360413,121.298294}, {406.207703,121.298294}},
+{{369.850525,145.675964}, {382.362915,121.29287}, {406.211273,121.29287}},
+
+{{33.567436351153468, 62.336347586395924}, {35.200980274619084, 65.038561460144479}, {36.479571811084995, 67.632178905412445}},
+{{41.349524945572696, 67.886658677862641}, {39.125562529359087, 67.429772735149214}, {35.600314083992416, 66.705372160552685}},
+
+{{67.25299631583178, 21.109080184767524}, {43.617595267398613, 33.658034168577529}, {33.38371819435676, 44.214192553988745}},
+{{40.476838859398541, 39.543209911285999}, {36.701186108431131, 34.8817994016458}, {30.102144288878023, 26.739063172945315}},
+
+{{25.367434474345036, 50.4712103169743}, {17.865013304933097, 37.356741010559439}, {16.818988838905465, 37.682915484123129}},
+{{16.818988838905465, 37.682915484123129}, {15.772964372877833, 38.009089957686811}, {20.624104547604965, 41.825131596683121}},
+
+{{26.440225044088567, 79.695009812848298}, {26.085525979582247, 83.717928354134784}, {27.075079976297072, 84.820633667838905}},
+{{27.075079976297072, 84.820633667838905}, {28.276546859574015, 85.988574184029034}, {25.649263209500006, 87.166762066617025}},
+
+{{34.879150914024962, 83.862726601601125}, {35.095810134304429, 83.693473210169543}, {35.359284111931586, 83.488069234177502}},
+{{54.503204203015471, 76.094098492518242}, {51.366889541918894, 71.609856061299155}, {46.53086955445437, 69.949863036494207}},
+
 {{0, 0}, {1, 0}, {0, 3}},
 {{1, 0}, {0, 1}, {1, 1}},
-{{369.848602,145.680267}, {382.360413,121.298294}, {406.207703,121.298294}},
 {{369.961151,137.980698}, {383.970093,121.298294}, {406.213287,121.298294}},
 {{353.2948,194.351074}, {353.2948,173.767563}, {364.167572,160.819855}},
 {{360.416077,166.795715}, {370.126831,147.872162}, {388.635406,147.872162}},
@@ -71,7 +120,6 @@
 {{369.8543701171875, 145.66734313964844}, {382.36788940429688, 121.28203582763672}, {406.21844482421875, 121.28203582763672}},
 {{369.96469116210938, 137.96672058105469}, {383.97555541992188, 121.28203582763672}, {406.2218017578125, 121.28203582763672}},
 
-    {{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}},
     {{369.962311, 137.976044}, {383.971893, 121.29287}, {406.216125, 121.29287}},
 
     {{400.121704, 149.468719}, {391.949493, 161.037186}, {391.949493, 181.202423}},
@@ -85,35 +133,47 @@
 
 const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
 
+#define ONE_OFF_DEBUG 0
+
+static void oneOffTest1(size_t outer, size_t inner) {
+    const Quadratic& quad1 = testSet[outer];
+    const Quadratic& quad2 = testSet[inner];
+    Intersections intersections2;
+    intersect2(quad1, quad2, intersections2);
+    if (intersections2.fUnsortable) {
+        SkASSERT(0);
+        return;
+    }
+    for (int pt = 0; pt < intersections2.used(); ++pt) {
+        double tt1 = intersections2.fT[0][pt];
+        double tx1, ty1;
+        xy_at_t(quad1, tt1, tx1, ty1);
+        int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
+        double tt2 = intersections2.fT[1][pt2];
+        double tx2, ty2;
+        xy_at_t(quad2, tt2, tx2, ty2);
+        if (!AlmostEqualUlps(tx1, tx2)) {
+            SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                __FUNCTION__, (int)outer, (int)inner, tt1, tx1, ty1, tt2, tx2, ty2);
+            SkASSERT(0);
+        }
+        if (!AlmostEqualUlps(ty1, ty2)) {
+            SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                __FUNCTION__, (int)outer, (int)inner, tt1, tx1, ty1, tt2, tx2, ty2);
+            SkASSERT(0);
+        }
+#if ONE_OFF_DEBUG
+        SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
+            outer, inner, tt1, tx1, tx2, tt2);
+#endif
+    }
+}
+
 static void oneOffTest() {
+//    oneOffTest1(0, 1);
     for (size_t outer = 0; outer < testSetCount - 1; ++outer) {
         for (size_t inner = outer + 1; inner < testSetCount; ++inner) {
-            const Quadratic& quad1 = testSet[outer];
-            const Quadratic& quad2 = testSet[inner];
-            double tt1, tt2;
-            Intersections intersections2;
-            intersect2(quad1, quad2, intersections2);
-            for (int pt = 0; pt < intersections2.used(); ++pt) {
-                tt1 = intersections2.fT[0][pt];
-                double tx1, ty1;
-                xy_at_t(quad1, tt1, tx1, ty1);
-                int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
-                tt2 = intersections2.fT[1][pt2];
-                double tx2, ty2;
-                xy_at_t(quad2, tt2, tx2, ty2);
-                if (!approximately_equal(tx1, tx2)) {
-                    SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
-                        __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
-                    SkASSERT(0);
-                }
-                if (!approximately_equal(ty1, ty2)) {
-                    SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
-                        __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
-                    SkASSERT(0);
-                }
-                SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
-                    outer, inner, tt1, tx1, tx2, tt2);
-            }
+            oneOffTest1(outer, inner);
         }
     }
 }
diff --git a/experimental/Intersection/QuadraticIntersection_TestData.h b/experimental/Intersection/QuadraticIntersection_TestData.h
index 4c95d5e..2dbf34a 100644
--- a/experimental/Intersection/QuadraticIntersection_TestData.h
+++ b/experimental/Intersection/QuadraticIntersection_TestData.h
@@ -4,11 +4,8 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#if !defined(IN_TEST)
-    #define IN_TEST 1
-#endif
-
 #include "DataTypes.h"
+#include "DataTypes_Test.h"
 
 extern const Quadratic quadraticLines[];
 extern const Quadratic quadraticModEpsilonLines[];
diff --git a/experimental/Intersection/QuadraticParameterization.cpp b/experimental/Intersection/QuadraticParameterization.cpp
index 8e7f1a2..76441aa 100644
--- a/experimental/Intersection/QuadraticParameterization.cpp
+++ b/experimental/Intersection/QuadraticParameterization.cpp
@@ -105,8 +105,7 @@
         if (first == index) {
             continue;
         }
-        if (!approximately_equal(p[index] * p2.p[first],
-                p[first] * p2.p[index])) {
+        if (!AlmostEqualUlps(p[index] * p2.p[first], p[first] * p2.p[index])) {
             return false;
         }
     }
diff --git a/experimental/Intersection/QuadraticParameterization_Test.cpp b/experimental/Intersection/QuadraticParameterization_Test.cpp
index df239dc..eedd6a4 100644
--- a/experimental/Intersection/QuadraticParameterization_Test.cpp
+++ b/experimental/Intersection/QuadraticParameterization_Test.cpp
@@ -7,6 +7,7 @@
 #include "CurveIntersection.h"
 #include "Intersection_Tests.h"
 #include "Parameterization_Test.h"
+#include "QuadraticUtilities.h"
 
 const Quadratic quadratics[] = {
     {{0, 0}, {1, 0}, {1, 1}},
@@ -33,12 +34,12 @@
             for (size_t two = 0; two < quadsCount; ++two) {
                 for (size_t inner = 0; inner < 3; inner += 2) {
                     if (!point_on_parameterized_curve(*quads[one], (*quads[two])[inner])) {
-                            printf("%s %zu [%zu,%zu] %zu parameterization failed\n",
+                            SkDebugf("%s %zu [%zu,%zu] %zu parameterization failed\n",
                                 __FUNCTION__, index, one, two, inner);
                     }
                 }
                 if (!implicit_matches(*quads[one], *quads[two])) {
-                    printf("%s %zu [%zu,%zu] coincidence failed\n", __FUNCTION__,
+                    SkDebugf("%s %zu [%zu,%zu] coincidence failed\n", __FUNCTION__,
                             index, one, two);
                 }
             }
diff --git a/experimental/Intersection/QuadraticReduceOrder.cpp b/experimental/Intersection/QuadraticReduceOrder.cpp
index b68a68b..700d4c5 100644
--- a/experimental/Intersection/QuadraticReduceOrder.cpp
+++ b/experimental/Intersection/QuadraticReduceOrder.cpp
@@ -63,7 +63,7 @@
         --endIndex;
         if (endIndex == 0) {
             printf("%s shouldn't get here if all four points are about equal", __FUNCTION__);
-            assert(0);
+            SkASSERT(0);
         }
     }
     if (!isLinear(quad, startIndex, endIndex)) {
@@ -92,7 +92,7 @@
     if (root) {
         _Point extrema;
         extrema.x = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue);
-        extrema.y = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue);
+        extrema.y = interp_quad_coords(quad[0].y, quad[1].y, quad[2].y, tValue);
         // sameSide > 0 means mid is smaller than either [0] or [2], so replace smaller
         int replace;
         if (useX) {
@@ -148,10 +148,10 @@
         }
     }
     for (index = 0; index < 3; ++index) {
-        if (approximately_equal(quad[index].x, quad[minX].x)) {
+        if (AlmostEqualUlps(quad[index].x, quad[minX].x)) {
             minXSet |= 1 << index;
         }
-        if (approximately_equal(quad[index].y, quad[minY].y)) {
+        if (AlmostEqualUlps(quad[index].y, quad[minY].y)) {
             minYSet |= 1 << index;
         }
     }
diff --git a/experimental/Intersection/QuadraticReduceOrder_Test.cpp b/experimental/Intersection/QuadraticReduceOrder_Test.cpp
index 3f49b95..abf8fa7 100644
--- a/experimental/Intersection/QuadraticReduceOrder_Test.cpp
+++ b/experimental/Intersection/QuadraticReduceOrder_Test.cpp
@@ -8,7 +8,6 @@
 #include "Intersection_Tests.h"
 #include "QuadraticIntersection_TestData.h"
 #include "TestUtilities.h"
-#include "SkTypes.h"
 
 static const Quadratic testSet[] = {
     {{1, 1}, {2, 2}, {1, 1.000003}},
@@ -43,8 +42,8 @@
     run = RunQuadraticLines;
     firstTestIndex = 1;
 #endif
-    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : INT_MAX;
-    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : INT_MAX;
+    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : SK_MaxS32;
+    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : SK_MaxS32;
 
     for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
         const Quadratic& quad = quadraticLines[index];
diff --git a/experimental/Intersection/QuadraticSubDivide.cpp b/experimental/Intersection/QuadraticSubDivide.cpp
index 436b5a9..a889825 100644
--- a/experimental/Intersection/QuadraticSubDivide.cpp
+++ b/experimental/Intersection/QuadraticSubDivide.cpp
@@ -6,6 +6,7 @@
  */
 #include "CurveIntersection.h"
 #include "IntersectionUtilities.h"
+#include "QuadraticUtilities.h"
 
 /*
 Given a quadratic q, t1, and t2, find a small quadratic segment.
diff --git a/experimental/Intersection/QuadraticUtilities.cpp b/experimental/Intersection/QuadraticUtilities.cpp
index 95be90a..32f95ed 100644
--- a/experimental/Intersection/QuadraticUtilities.cpp
+++ b/experimental/Intersection/QuadraticUtilities.cpp
@@ -20,12 +20,37 @@
 
 */
 
+
+int add_valid_ts(double s[], int realRoots, double* t) {
+    int foundRoots = 0;
+    for (int index = 0; index < realRoots; ++index) {
+        double tValue = s[index];
+        if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
+            if (approximately_less_than_zero(tValue)) {
+                tValue = 0;
+            } else if (approximately_greater_than_one(tValue)) {
+                tValue = 1;
+            }
+            for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
+                if (approximately_equal(t[idx2], tValue)) {
+                    goto nextRoot;
+                }
+            }
+            t[foundRoots++] = tValue;
+        }
+nextRoot:
+        ;
+    }
+    return foundRoots;
+}
+
 // note: caller expects multiple results to be sorted smaller first
 // note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
 //  analysis of the quadratic equation, suggesting why the following looks at
 //  the sign of B -- and further suggesting that the greatest loss of precision
 //  is in b squared less two a c
-int quadraticRoots(double A, double B, double C, double t[2]) {
+int quadraticRootsValidT(double A, double B, double C, double t[2]) {
+#if 0
     B *= 2;
     double square = B * B - 4 * A * C;
     if (approximately_negative(square)) {
@@ -61,19 +86,85 @@
             t[0] = ratio;
         }
     }
+#else
+    double s[2];
+    int realRoots = quadraticRootsReal(A, B, C, s);
+    int foundRoots = add_valid_ts(s, realRoots, t);
+#endif
     return foundRoots;
 }
 
-void dxdy_at_t(const Quadratic& quad, double t, double& x, double& y) {
+// unlike quadratic roots, this does not discard real roots <= 0 or >= 1
+int quadraticRootsReal(const double A, const double B, const double C, double s[2]) {
+    const double p = B / (2 * A);
+    const double q = C / A;
+    if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) {
+        if (approximately_zero(B)) {
+            s[0] = 0;
+            return C == 0;
+        }
+        s[0] = -C / B;
+        return 1;
+    }
+    /* normal form: x^2 + px + q = 0 */
+    const double p2 = p * p;
+#if 0
+    double D = AlmostEqualUlps(p2, q) ? 0 : p2 - q;
+    if (D <= 0) {
+        if (D < 0) {
+            return 0;
+        }
+        s[0] = -p;
+        SkDebugf("[%d] %1.9g\n", 1, s[0]);
+        return 1;
+    }
+    double sqrt_D = sqrt(D);
+    s[0] = sqrt_D - p;
+    s[1] = -sqrt_D - p;
+    SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
+    return 2;
+#else
+    if (!AlmostEqualUlps(p2, q) && p2 < q) {
+        return 0;
+    }
+    double sqrt_D = 0;
+    if (p2 > q) {
+        sqrt_D = sqrt(p2 - q);
+    }
+    s[0] = sqrt_D - p;
+    s[1] = -sqrt_D - p;
+#if 0
+    if (AlmostEqualUlps(s[0], s[1])) {
+        SkDebugf("[%d] %1.9g\n", 1, s[0]);
+    } else {
+        SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
+    }
+#endif
+    return 1 + !AlmostEqualUlps(s[0], s[1]);
+#endif
+}
+
+static double derivativeAtT(const double* quad, double t) {
     double a = t - 1;
     double b = 1 - 2 * t;
     double c = t;
-    if (&x) {
-        x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
-    }
-    if (&y) {
-        y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
-    }
+    return a * quad[0] + b * quad[2] + c * quad[4];
+}
+
+double dx_at_t(const Quadratic& quad, double t) {
+    return derivativeAtT(&quad[0].x, t);
+}
+
+double dy_at_t(const Quadratic& quad, double t) {
+    return derivativeAtT(&quad[0].y, t);
+}
+
+void dxdy_at_t(const Quadratic& quad, double t, _Point& dxy) {
+    double a = t - 1;
+    double b = 1 - 2 * t;
+    double c = t;
+    dxy.x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
+    dxy.y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
 }
 
 void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {
diff --git a/experimental/Intersection/QuadraticUtilities.h b/experimental/Intersection/QuadraticUtilities.h
index 5bc15ea..f423dc6 100644
--- a/experimental/Intersection/QuadraticUtilities.h
+++ b/experimental/Intersection/QuadraticUtilities.h
@@ -4,9 +4,17 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
+#if !defined QUADRATIC_UTILITIES_H
+#define QUADRATIC_UTILITIES_H
+
 #include "DataTypes.h"
 
-void dxdy_at_t(const Quadratic& , double t, double& x, double& y);
+int add_valid_ts(double s[], int realRoots, double* t);
+void chop_at(const Quadratic& src, QuadraticPair& dst, double t);
+double dx_at_t(const Quadratic& , double t);
+double dy_at_t(const Quadratic& , double t);
+void dxdy_at_t(const Quadratic& , double t, _Point& xy);
 
 /* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
  *
@@ -23,6 +31,9 @@
     b -= c;          // b =     2*B - 2*C
 }
 
-int quadraticRoots(double A, double B, double C, double t[2]);
-
+int quadraticRootsReal(double A, double B, double C, double t[2]);
+int quadraticRootsValidT(const double A, const double B, const double C, double s[2]);
+void sub_divide(const Quadratic& src, double t1, double t2, Quadratic& dst);
 void xy_at_t(const Quadratic& , double t, double& x, double& y);
+
+#endif
diff --git a/experimental/Intersection/QuarticRoot.cpp b/experimental/Intersection/QuarticRoot.cpp
index 66ce3bf..759e209 100644
--- a/experimental/Intersection/QuarticRoot.cpp
+++ b/experimental/Intersection/QuarticRoot.cpp
@@ -27,209 +27,52 @@
 
 #include    <math.h>
 #include "CubicUtilities.h"
+#include "QuadraticUtilities.h"
 #include "QuarticRoot.h"
 
-const double PI = 4 * atan(1);
-
-// unlike quadraticRoots in QuadraticUtilities.cpp, this does not discard
-// real roots <= 0 or >= 1
-static int quadraticRootsX(const double A, const double B, const double C,
-        double s[2]) {
-    if (approximately_zero(A)) {
-        if (approximately_zero(B)) {
-            s[0] = 0;
-            return C == 0;
-        }
-        s[0] = -C / B;
-        return 1;
-    }
-    /* normal form: x^2 + px + q = 0 */
-    const double p = B / (2 * A);
-    const double q = C / A;
-    double D = p * p - q;
-    if (D < 0) {
-        if (approximately_positive_squared(D)) {
-            D = 0;
-        } else {
-            return 0;
-        }
-    }
-    double sqrt_D = sqrt(D);
-    if (approximately_less_than_zero(sqrt_D)) {
-        s[0] = -p;
-        return 1;
-    }
-    s[0] = sqrt_D - p;
-    s[1] = -sqrt_D - p;
-    return 2;
-}
-
-#define USE_GEMS 0
-#if USE_GEMS
-// unlike cubicRoots in CubicUtilities.cpp, this does not discard
-// real roots <= 0 or >= 1
-static int cubicRootsX(const double A, const double B, const double C,
-        const double D, double s[3]) {
-    int num;
-    /* normal form: x^3 + Ax^2 + Bx + C = 0 */
-    const double invA = 1 / A;
-    const double a = B * invA;
-    const double b = C * invA;
-    const double c = D * invA;
-    /*  substitute x = y - a/3 to eliminate quadric term:
-    x^3 +px + q = 0 */
-    const double a2 = a * a;
-    const double Q = (-a2 + b * 3) / 9;
-    const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
-    /* use Cardano's formula */
-    const double Q3 = Q * Q * Q;
-    const double R2plusQ3 = R * R + Q3;
-    if (approximately_zero(R2plusQ3)) {
-        if (approximately_zero(R)) {/* one triple solution */
-            s[0] = 0;
-            num = 1;
-        } else { /* one single and one double solution */
-
-            double u = cube_root(-R);
-            s[0] = 2 * u;
-            s[1] = -u;
-            num = 2;
-        }
-    }
-    else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */
-        const double theta = acos(-R / sqrt(-Q3)) / 3;
-        const double _2RootQ = 2 * sqrt(-Q);
-        s[0] = _2RootQ * cos(theta);
-        s[1] = -_2RootQ * cos(theta + PI / 3);
-        s[2] = -_2RootQ * cos(theta - PI / 3);
-        num = 3;
-    } else { /* one real solution */
-        const double sqrt_D = sqrt(R2plusQ3);
-        const double u = cube_root(sqrt_D - R);
-        const double v = -cube_root(sqrt_D + R);
-        s[0] = u + v;
-        num = 1;
-    }
-    /* resubstitute */
-    const double sub = a / 3;
-    for (int i = 0; i < num; ++i) {
-        s[i] -= sub;
-    }
-    return num;
-}
-#else
-
-static int cubicRootsX(double A, double B, double C, double D, double s[3]) {
-    if (approximately_zero(A)) {  // we're just a quadratic
-        return quadraticRootsX(B, C, D, s);
-    }
-    if (approximately_zero(D)) { // 0 is one root
-        int num = quadraticRootsX(A, B, C, s);
-        for (int i = 0; i < num; ++i) {
-            if (approximately_zero(s[i])) {
-                return num;
-            }
-        }
-        s[num++] = 0;
-        return num;
-    }
-    if (approximately_zero(A + B + C + D)) { // 1 is one root
-        int num = quadraticRootsX(A, A + B, -D, s);
-        for (int i = 0; i < num; ++i) {
-            if (approximately_equal(s[i], 1)) {
-                return num;
-            }
-        }
-        s[num++] = 1;
-        return num;
-    }
-    double a, b, c;
-    {
-        double invA = 1 / A;
-        a = B * invA;
-        b = C * invA;
-        c = D * invA;
-    }
-    double a2 = a * a;
-    double Q = (a2 - b * 3) / 9;
-    double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
-    double Q3 = Q * Q * Q;
-    double R2MinusQ3 = R * R - Q3;
-    double adiv3 = a / 3;
-    double r;
-    double* roots = s;
-
-    if (approximately_zero_squared(R2MinusQ3)) {
-        if (approximately_zero(R)) {/* one triple solution */
-            *roots++ = -adiv3;
-        } else { /* one single and one double solution */
-
-            double u = cube_root(-R);
-            *roots++ = 2 * u - adiv3;
-            *roots++ = -u - adiv3;
-        }
-    }
-    else if (R2MinusQ3 < 0)   // we have 3 real roots
-    {
-        double theta = acos(R / sqrt(Q3));
-        double neg2RootQ = -2 * sqrt(Q);
-
-        r = neg2RootQ * cos(theta / 3) - adiv3;
-        *roots++ = r;
-
-        r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
-        *roots++ = r;
-
-        r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
-        *roots++ = r;
-    }
-    else                // we have 1 real root
-    {
-        double A = fabs(R) + sqrt(R2MinusQ3);
-        A = cube_root(A);
-        if (R > 0) {
-            A = -A;
-        }
-        if (A != 0) {
-            A += Q / A;
-        }
-        r = A - adiv3;
-        *roots++ = r;
-    }
-    return (int)(roots - s);
-}
+int reducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
+        const double t0, const bool oneHint, double roots[4]) {
+#if SK_DEBUG
+    // create a string mathematica understands
+    // GDB set print repe 15 # if repeated digits is a bother
+    //     set print elements 400 # if line doesn't fit
+    char str[1024];
+    bzero(str, sizeof(str));
+    sprintf(str, "Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
+        t4, t3, t2, t1, t0);
 #endif
+    if (approximately_zero(t4)) {
+        if (approximately_zero(t3)) {
+            return quadraticRootsReal(t2, t1, t0, roots);
+        }
+        return cubicRootsReal(t3, t2, t1, t0, roots);
+    }
+    if (approximately_zero(t0)) { // 0 is one root
+        int num = cubicRootsReal(t4, t3, t2, t1, roots);
+        for (int i = 0; i < num; ++i) {
+            if (approximately_zero(roots[i])) {
+                return num;
+            }
+        }
+        roots[num++] = 0;
+        return num;
+    }
+    if (oneHint) {
+        SkASSERT(approximately_zero(t4 + t3 + t2 + t1 + t0)); // 1 is one root
+        int num = cubicRootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots); // note that -C==A+B+D+E
+        for (int i = 0; i < num; ++i) {
+            if (approximately_equal(roots[i], 1)) {
+                return num;
+            }
+        }
+        roots[num++] = 1;
+        return num;
+    }
+    return -1;
+}
 
-int quarticRoots(const double A, const double B, const double C, const double D,
+int quarticRootsReal(const double A, const double B, const double C, const double D,
         const double E, double s[4]) {
-    if (approximately_zero(A)) {
-        if (approximately_zero(B)) {
-            return quadraticRootsX(C, D, E, s);
-        }
-        return cubicRootsX(B, C, D, E, s);
-    }
-    int num;
-    int i;
-    if (approximately_zero(E)) { // 0 is one root
-        num = cubicRootsX(A, B, C, D, s);
-        for (i = 0; i < num; ++i) {
-            if (approximately_zero(s[i])) {
-                return num;
-            }
-        }
-        s[num++] = 0;
-        return num;
-    }
-    if (approximately_zero_squared(A + B + C + D + E)) { // 1 is one root
-        num = cubicRootsX(A, A + B, -(D + E), -E, s); // note that -C==A+B+D+E
-        for (i = 0; i < num; ++i) {
-            if (approximately_equal(s[i], 1)) {
-                return num;
-            }
-        }
-        s[num++] = 1;
-        return num;
-    }
     double  u, v;
     /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
     const double invA = 1 / A;
@@ -243,39 +86,100 @@
     const double p = -3 * a2 / 8 + b;
     const double q = a2 * a / 8 - a * b / 2 + c;
     const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
+    int num;
     if (approximately_zero(r)) {
     /* no absolute term: y(y^3 + py + q) = 0 */
-        num = cubicRootsX(1, 0, p, q, s);
+        num = cubicRootsReal(1, 0, p, q, s);
         s[num++] = 0;
     } else {
         /* solve the resolvent cubic ... */
-        (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
-        /* ... and take the one real solution ... */
-        const double z = s[0];
-        /* ... to build two quadric equations */
-        u = z * z - r;
-        v = 2 * z - p;
-        if (approximately_zero(u)) {
-            u = 0;
-        } else if (u > 0) {
-            u = sqrt(u);
-        } else {
-            return 0;
+        double cubicRoots[3];
+        int roots = cubicRootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots);
+        int index;
+    #if 0 && SK_DEBUG // enable to verify that any cubic root is as good as any other
+        double tries[3][4];
+        int nums[3];
+        for (index = 0; index < roots; ++index) {
+            /* ... and take one real solution ... */
+            const double z = cubicRoots[index];
+            /* ... to build two quadric equations */
+            u = z * z - r;
+            v = 2 * z - p;
+            if (approximately_zero_squared(u)) {
+                u = 0;
+            } else if (u > 0) {
+                u = sqrt(u);
+            } else {
+                SkDebugf("%s u=%1.9g <0\n", __FUNCTION__, u);
+                continue;
+            }
+            if (approximately_zero_squared(v)) {
+                v = 0;
+            } else if (v > 0) {
+                v = sqrt(v);
+            } else {
+                SkDebugf("%s v=%1.9g <0\n", __FUNCTION__, v);
+                continue;
+            }
+            nums[index] = quadraticRootsReal(1, q < 0 ? -v : v, z - u, tries[index]);
+            nums[index] += quadraticRootsReal(1, q < 0 ? v : -v, z + u, tries[index] + nums[index]);
+            /* resubstitute */
+            const double sub = a / 4;
+            for (int i = 0; i < nums[index]; ++i) {
+                tries[index][i] -= sub;
+            }
         }
-        if (approximately_zero(v)) {
-            v = 0;
-        } else if (v > 0) {
-            v = sqrt(v);
-        } else {
-            return 0;
+        for (index = 0; index < roots; ++index) {
+            SkDebugf("%s", __FUNCTION__);
+            for (int idx2 = 0; idx2 < nums[index]; ++idx2) {
+                SkDebugf(" %1.9g", tries[index][idx2]);
+            }
+            SkDebugf("\n");
         }
-        num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
-        num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
+    #endif
+        /* ... and take one real solution ... */
+        double z;
+        num = 0;
+        int num2 = 0;
+        for (index = 0; index < roots; ++index) {
+            z = cubicRoots[index];
+            /* ... to build two quadric equations */
+            u = z * z - r;
+            v = 2 * z - p;
+            if (approximately_zero_squared(u)) {
+                u = 0;
+            } else if (u > 0) {
+                u = sqrt(u);
+            } else {
+                continue;
+            }
+            if (approximately_zero_squared(v)) {
+                v = 0;
+            } else if (v > 0) {
+                v = sqrt(v);
+            } else {
+                continue;
+            }
+            num = quadraticRootsReal(1, q < 0 ? -v : v, z - u, s);
+            num2 = quadraticRootsReal(1, q < 0 ? v : -v, z + u, s + num);
+            if (!((num | num2) & 1)) {
+                break; // prefer solutions without single quad roots
+            }
+        }
+        num += num2;
+        if (!num) {
+            return 0; // no valid cubic root
+        }
+    }
+    /* resubstitute */
+    const double sub = a / 4;
+    for (int i = 0; i < num; ++i) {
+        s[i] -= sub;
     }
     // eliminate duplicates
-    for (i = 0; i < num - 1; ++i) {
+    for (int i = 0; i < num - 1; ++i) {
         for (int j = i + 1; j < num; ) {
-            if (approximately_equal(s[i], s[j])) {
+            if (AlmostEqualUlps(s[i], s[j])) {
                 if (j < --num) {
                     s[j] = s[num];
                 }
@@ -284,12 +188,5 @@
             }
         }
     }
-    /* resubstitute */
-    const double sub = a / 4;
-    for (i = 0; i < num; ++i) {
-        s[i] -= sub;
-    }
     return num;
 }
-
-
diff --git a/experimental/Intersection/QuarticRoot.h b/experimental/Intersection/QuarticRoot.h
index 8baa842..3692caa 100644
--- a/experimental/Intersection/QuarticRoot.h
+++ b/experimental/Intersection/QuarticRoot.h
@@ -1,2 +1,5 @@
-int quarticRoots(const double A, const double B, const double C, const double D,
+int reducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
+        const double t0, const bool oneHint, double s[4]);
+
+int quarticRootsReal(const double A, const double B, const double C, const double D,
         const double E, double s[4]);
diff --git a/experimental/Intersection/QuarticRoot_Test.cpp b/experimental/Intersection/QuarticRoot_Test.cpp
index 2fe3e72..18a3a8d 100644
--- a/experimental/Intersection/QuarticRoot_Test.cpp
+++ b/experimental/Intersection/QuarticRoot_Test.cpp
@@ -1,13 +1,7 @@
-#include <assert.h>
-#include <math.h>
 #include "CubicUtilities.h"
 #include "Intersection_Tests.h"
-
-namespace QuarticRootTest {
-
-#include "QuarticRoot.cpp"
-
-}
+#include "QuadraticUtilities.h"
+#include "QuarticRoot.h"
 
 double mulA[] = {-3, -1, 1, 3};
 size_t mulACount = sizeof(mulA) / sizeof(mulA[0]);
@@ -20,25 +14,40 @@
 double rootE[] = {-5, -1, 0, 1, 7};
 size_t rootECount = sizeof(rootE) / sizeof(rootE[0]);
 
-static void quadraticTest() {
+
+static void quadraticTest(bool limit) {
     // (x - a)(x - b) == x^2 - (a + b)x + ab
     for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
         for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
             for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
                 const double A = mulA[aIndex];
-                const double B = rootB[bIndex];
-                const double C = rootC[cIndex];
+                double B = rootB[bIndex];
+                double C = rootC[cIndex];
+                if (limit) {
+                    B = (B - 6) / 12;
+                    C = (C - 6) / 12;
+                }
                 const double b = A * (B + C);
                 const double c = A * B * C;
                 double roots[2];
-                const int rootCount = QuarticRootTest::quadraticRootsX(A, b, c, roots);
-                const int expected = 1 + (B != C);
-                assert(rootCount == expected);
-                assert(approximately_equal(roots[0], -B)
+                const int rootCount = limit ? quadraticRootsValidT(A, b, c, roots)
+                    : quadraticRootsReal(A, b, c, roots);
+                int expected;
+                if (limit) {
+                    expected = B <= 0 && B >= -1;
+                    expected += B != C && C <= 0 && C >= -1;
+                } else {
+                    expected = 1 + (B != C);
+                }
+                SkASSERT(rootCount == expected);
+                if (!rootCount) {
+                    continue;
+                }
+                SkASSERT(approximately_equal(roots[0], -B)
                         || approximately_equal(roots[0], -C));
-                if (B != C) {
-                    assert(!approximately_equal(roots[0], roots[1]));
-                    assert(approximately_equal(roots[1], -B)
+                if (expected > 1) {
+                    SkASSERT(!approximately_equal(roots[0], roots[1]));
+                    SkASSERT(approximately_equal(roots[1], -B)
                             || approximately_equal(roots[1], -C));
                 }
             }
@@ -46,45 +55,119 @@
     }
 }
 
-static void cubicTest() {
+static void testOneCubic(bool limit, size_t aIndex, size_t bIndex, size_t cIndex, size_t dIndex) {
+    const double A = mulA[aIndex];
+    double B = rootB[bIndex];
+    double C = rootC[cIndex];
+    double D = rootD[dIndex];
+    if (limit) {
+        B = (B - 6) / 12;
+        C = (C - 6) / 12;
+        D = (C - 2) / 6;
+    }
+    const double b = A * (B + C + D);
+    const double c = A * (B * C + C * D + B * D);
+    const double d = A * B * C * D;
+    double roots[3];
+    const int rootCount = limit ? cubicRootsValidT(A, b, c, d, roots)
+            : cubicRootsReal(A, b, c, d, roots);
+    int expected;
+    if (limit) {
+        expected = B <= 0 && B >= -1;
+        expected += B != C && C <= 0 && C >= -1;
+        expected += B != D && C != D && D <= 0 && D >= -1;
+    } else {
+        expected = 1 + (B != C) + (B != D && C != D);
+    }
+    SkASSERT(rootCount == expected);
+    if (!rootCount) {
+        return;
+    }
+    SkASSERT(approximately_equal(roots[0], -B)
+            || approximately_equal(roots[0], -C)
+            || approximately_equal(roots[0], -D));
+    if (expected <= 1) {
+        return;
+    }
+    SkASSERT(!approximately_equal(roots[0], roots[1]));
+    SkASSERT(approximately_equal(roots[1], -B)
+            || approximately_equal(roots[1], -C)
+            || approximately_equal(roots[1], -D));
+    if (expected <= 2) {
+        return;
+    }
+    SkASSERT(!approximately_equal(roots[0], roots[2])
+            && !approximately_equal(roots[1], roots[2]));
+    SkASSERT(approximately_equal(roots[2], -B)
+            || approximately_equal(roots[2], -C)
+            || approximately_equal(roots[2], -D));
+}
+
+static void cubicTest(bool limit) {
     // (x - a)(x - b)(x - c) == x^3 - (a + b + c)x^2 + (ab + bc + ac)x - abc
     for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
         for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
             for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
                 for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) {
-                    const double A = mulA[aIndex];
-                    const double B = rootB[bIndex];
-                    const double C = rootC[cIndex];
-                    const double D = rootD[dIndex];
-                    const double b = A * (B + C + D);
-                    const double c = A * (B * C + C * D + B * D);
-                    const double d = A * B * C * D;
-                    double roots[3];
-                    const int rootCount = QuarticRootTest::cubicRootsX(A, b, c, d, roots);
-                    const int expected = 1 + (B != C) + (B != D && C != D);
-                    assert(rootCount == expected);
-                    assert(approximately_equal(roots[0], -B)
-                            || approximately_equal(roots[0], -C)
-                            || approximately_equal(roots[0], -D));
-                    if (expected > 1) {
-                        assert(!approximately_equal(roots[0], roots[1]));
-                        assert(approximately_equal(roots[1], -B)
-                                || approximately_equal(roots[1], -C)
-                                || approximately_equal(roots[1], -D));
-                        if (expected > 2) {
-                            assert(!approximately_equal(roots[0], roots[2])
-                                    && !approximately_equal(roots[1], roots[2]));
-                            assert(approximately_equal(roots[2], -B)
-                                    || approximately_equal(roots[2], -C)
-                                    || approximately_equal(roots[2], -D));
-                        }
-                    }
+                    testOneCubic(limit, aIndex, bIndex, cIndex, dIndex);
                 }
             }
         }
     }
 }
 
+static void testOneQuartic(size_t aIndex, size_t bIndex, size_t cIndex, size_t dIndex,
+        size_t eIndex) {
+    const double A = mulA[aIndex];
+    const double B = rootB[bIndex];
+    const double C = rootC[cIndex];
+    const double D = rootD[dIndex];
+    const double E = rootE[eIndex];
+    const double b = A * (B + C + D + E);
+    const double c = A * (B * C + C * D + B * D + B * E + C * E + D * E);
+    const double d = A * (B * C * D + B * C * E + B * D * E + C * D * E);
+    const double e = A * B * C * D * E;
+    double roots[4];
+    bool oneHint = approximately_zero(A + b + c + d + e);
+    int rootCount = reducedQuarticRoots(A, b, c, d, e, oneHint, roots);
+    if (rootCount < 0) {
+        rootCount = quarticRootsReal(A, b, c, d, e, roots);
+    }
+    const int expected = 1 + (B != C) + (B != D && C != D) + (B != E && C != E && D != E);
+    SkASSERT(rootCount == expected);
+    SkASSERT(AlmostEqualUlps(roots[0], -B)
+            || AlmostEqualUlps(roots[0], -C)
+            || AlmostEqualUlps(roots[0], -D)
+            || AlmostEqualUlps(roots[0], -E));
+    if (expected <= 1) {
+        return;
+    }
+    SkASSERT(!AlmostEqualUlps(roots[0], roots[1]));
+    SkASSERT(AlmostEqualUlps(roots[1], -B)
+            || AlmostEqualUlps(roots[1], -C)
+            || AlmostEqualUlps(roots[1], -D)
+            || AlmostEqualUlps(roots[1], -E));
+    if (expected <= 2) {
+        return;
+    }
+    SkASSERT(!AlmostEqualUlps(roots[0], roots[2])
+            && !AlmostEqualUlps(roots[1], roots[2]));
+    SkASSERT(AlmostEqualUlps(roots[2], -B)
+            || AlmostEqualUlps(roots[2], -C)
+            || AlmostEqualUlps(roots[2], -D)
+            || AlmostEqualUlps(roots[2], -E));
+    if (expected <= 3) {
+        return;
+    }
+    SkASSERT(!AlmostEqualUlps(roots[0], roots[3])
+            && !AlmostEqualUlps(roots[1], roots[3])
+            && !AlmostEqualUlps(roots[2], roots[3]));
+    SkASSERT(AlmostEqualUlps(roots[3], -B)
+            || AlmostEqualUlps(roots[3], -C)
+            || AlmostEqualUlps(roots[3], -D)
+            || AlmostEqualUlps(roots[3], -E));
+}
+
 static void quarticTest() {
     // (x - a)(x - b)(x - c)(x - d) == x^4 - (a + b + c + d)x^3
     //   + (ab + bc + cd + ac + bd + cd)x^2 - (abc + bcd + abd + acd) * x + abcd
@@ -93,47 +176,7 @@
             for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
                 for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) {
                     for (size_t eIndex = 0; eIndex < rootECount; ++eIndex) {
-                        const double A = mulA[aIndex];
-                        const double B = rootB[bIndex];
-                        const double C = rootC[cIndex];
-                        const double D = rootD[dIndex];
-                        const double E = rootE[eIndex];
-                        const double b = A * (B + C + D + E);
-                        const double c = A * (B * C + C * D + B * D + B * E + C * E + D * E);
-                        const double d = A * (B * C * D + B * C * E + B * D * E + C * D * E);
-                        const double e = A * B * C * D * E;
-                        double roots[4];
-                        const int rootCount = QuarticRootTest::quarticRoots(A, b, c, d, e, roots);
-                        const int expected = 1 + (B != C) + (B != D && C != D) + (B != E && C != E && D != E);
-                        assert(rootCount == expected);
-                        assert(approximately_equal(roots[0], -B)
-                                || approximately_equal(roots[0], -C)
-                                || approximately_equal(roots[0], -D)
-                                || approximately_equal(roots[0], -E));
-                        if (expected > 1) {
-                            assert(!approximately_equal(roots[0], roots[1]));
-                            assert(approximately_equal(roots[1], -B)
-                                    || approximately_equal(roots[1], -C)
-                                    || approximately_equal(roots[1], -D)
-                                    || approximately_equal(roots[1], -E));
-                            if (expected > 2) {
-                                assert(!approximately_equal(roots[0], roots[2])
-                                        && !approximately_equal(roots[1], roots[2]));
-                                assert(approximately_equal(roots[2], -B)
-                                        || approximately_equal(roots[2], -C)
-                                        || approximately_equal(roots[2], -D)
-                                        || approximately_equal(roots[2], -E));
-                                if (expected > 3) {
-                                    assert(!approximately_equal(roots[0], roots[3])
-                                            && !approximately_equal(roots[1], roots[3])
-                                            && !approximately_equal(roots[2], roots[3]));
-                                    assert(approximately_equal(roots[3], -B)
-                                            || approximately_equal(roots[3], -C)
-                                            || approximately_equal(roots[3], -D)
-                                            || approximately_equal(roots[3], -E));
-                                }
-                            }
-                        }
+                        testOneQuartic(aIndex, bIndex, cIndex, dIndex, eIndex);
                     }
                 }
             }
@@ -142,7 +185,11 @@
 }
 
 void QuarticRoot_Test() {
-    quadraticTest();
-    cubicTest();
+    testOneCubic(false, 0, 5, 5, 4);
+    testOneQuartic(0, 0, 2, 4, 3);
+    quadraticTest(true);
+    quadraticTest(false);
+    cubicTest(true);
+    cubicTest(false);
     quarticTest();
 }
diff --git a/experimental/Intersection/ShapeOpCubic4x4_Test.cpp b/experimental/Intersection/ShapeOpCubic4x4_Test.cpp
new file mode 100644
index 0000000..f4eb031
--- /dev/null
+++ b/experimental/Intersection/ShapeOpCubic4x4_Test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "ShapeOps.h"
+
+// four rects, of four sizes
+// for 3 smaller sizes, tall, wide
+    // top upper mid lower bottom aligned (3 bits, 5 values)
+    // same with x (3 bits, 5 values)
+// not included, square, tall, wide (2 bits)
+// cw or ccw (1 bit)
+
+static void* testShapeOps4x4CubicsMain(void* data)
+{
+    SkASSERT(data);
+    State4& state = *(State4*) data;
+    char pathStr[1024]; // gdb: set print elements 400
+    bzero(pathStr, sizeof(pathStr));
+    do {
+        for (int a = 0 ; a < 6; ++a) {
+        for (int b = a + 1 ; b < 7; ++b)  {
+        for (int c = 0 ; c < 6; ++c)          {
+        for (int d = c + 1 ; d < 7; ++d)           {
+        for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
+        for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f)   {
+            SkPath pathA, pathB;
+            char* str = pathStr;
+            pathA.setFillType((SkPath::FillType) e);
+            str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
+                    e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                    ? "EvenOdd" : "?UNDEFINED");
+            pathA.moveTo(state.a, state.b);
+            str += sprintf(str, "    path.moveTo(%d,%d);\n", state.a, state.b);
+            pathA.cubicTo(state.c, state.d, b, a, d, c);
+            str += sprintf(str, "    path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.c, state.d,
+                    b, a, d, c);
+            pathA.close();
+            str += sprintf(str, "    path.close();\n");
+            pathB.setFillType((SkPath::FillType) f);
+            str += sprintf(str, "    pathB.setFillType(SkPath::k%s_FillType);\n",
+                    f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                    ? "EvenOdd" : "?UNDEFINED");
+            pathB.moveTo(a, b);
+            str += sprintf(str, "    pathB.moveTo(%d,%d);\n", a, b);
+            pathB.cubicTo(c, d, state.b, state.a, state.d, state.c);
+            str += sprintf(str, "    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
+                    state.b, state.a, state.d, state.c);
+            pathB.close();
+            str += sprintf(str, "    pathB.close();\n");
+            for (int op = 0 ; op < kShapeOp_Count; ++op)    {
+                outputProgress(state, pathStr, (ShapeOp) op);
+                testShapeOp(pathA, pathB, (ShapeOp) op);
+                state.testsRun++;
+            }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+    } while (runNextTestSet(state));
+    return NULL;
+}
+
+void ShapeOps4x4CubicsThreaded_Test(int& testsRun)
+{
+    SkDebugf("%s\n", __FUNCTION__);
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = 4;
+    gDebugMaxWindValue = 4;
+#endif
+    const char testLineStr[] = "cubicOp";
+    initializeTests(testLineStr, sizeof(testLineStr));
+    int testsStart = testsRun;
+    for (int a = 0; a < 6; ++a) { // outermost
+        for (int b = a + 1; b < 7; ++b) {
+            for (int c = 0 ; c < 6; ++c) {
+                for (int d = c + 1; d < 7; ++d) {
+                    testsRun += dispatchTest4(testShapeOps4x4CubicsMain, a, b, c, d);
+                }
+                if (!gRunTestsInOneThread) SkDebugf(".");
+            }
+            if (!gRunTestsInOneThread) SkDebugf("%d", b);
+        }
+        if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
+    }
+    testsRun += waitForCompletion();
+    SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
+}
diff --git a/experimental/Intersection/ShapeOpRect4x4_Test.cpp b/experimental/Intersection/ShapeOpRect4x4_Test.cpp
index d149377..e6e44c1 100644
--- a/experimental/Intersection/ShapeOpRect4x4_Test.cpp
+++ b/experimental/Intersection/ShapeOpRect4x4_Test.cpp
@@ -26,7 +26,6 @@
         for (int b = a + 1 ; b < 7; ++b)  {
         for (int c = 0 ; c < 6; ++c)          {
         for (int d = c + 1 ; d < 7; ++d)           {
-        for (int op = 0 ; op < kShapeOp_Count; ++op)    {
         for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
         for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f)   {
             SkPath pathA, pathB;
@@ -53,19 +52,11 @@
             str += sprintf(str, "    pathB.addRect(%d, %d, %d, %d,"
                     " SkPath::kCW_Direction);\n", c, c, d, d);
             pathB.close();
-            outputProgress(state, pathStr, kDifference_Op);
-            testShapeOp(pathA, pathB, kDifference_Op);
-            state.testsRun++;
-            outputProgress(state, pathStr, kIntersect_Op);
-            testShapeOp(pathA, pathB, kIntersect_Op);
-            state.testsRun++;
-            outputProgress(state, pathStr, kUnion_Op);
-            testShapeOp(pathA, pathB, kUnion_Op);
-            state.testsRun++;
-            outputProgress(state, pathStr, kXor_Op);
-            testShapeOp(pathA, pathB, kXor_Op);
-            state.testsRun++;
-                                    }
+            for (int op = 0 ; op < kShapeOp_Count; ++op)    {
+                outputProgress(state, pathStr, (ShapeOp) op);
+                testShapeOp(pathA, pathB, (ShapeOp) op);
+                state.testsRun++;
+            }
                                 }
                             }
                         }
diff --git a/experimental/Intersection/ShapeOps.cpp b/experimental/Intersection/ShapeOps.cpp
index 281cf7f..57aedd6 100644
--- a/experimental/Intersection/ShapeOps.cpp
+++ b/experimental/Intersection/ShapeOps.cpp
@@ -72,11 +72,11 @@
             if (nextIndex == angleCount) {
                 nextIndex = 0;
             }
-            int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
             angle = sorted[nextIndex];
             segment = angle->segment();
             int start = angle->start();
             int end = angle->end();
+            int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
             segment->setUpWindings(start, end, sumMiWinding, sumSuWinding,
                     maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
             if (!segment->done(angle)) {
@@ -139,10 +139,11 @@
     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
         int index, endIndex;
-        Segment* current = findSortableTopNew(contourList, firstContour, index, endIndex, topLeft,
-                topUnsortable);
+        bool done;
+        Segment* current = findSortableTop(contourList, firstContour, index, endIndex, topLeft,
+                topUnsortable, done, true);
         if (!current) {
-            if (topUnsortable) {
+            if (topUnsortable || !done) {
                 topUnsortable = false;
                 SkASSERT(!firstRetry);
                 firstRetry = true;
diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp
index 6c6f5cb..9b4ebc4 100644
--- a/experimental/Intersection/Simplify.cpp
+++ b/experimental/Intersection/Simplify.cpp
@@ -24,13 +24,14 @@
 int gDebugMaxWindSum = SK_MaxS32;
 int gDebugMaxWindValue = SK_MaxS32;
 #endif
-bool gUseOldBridgeWinding = false;
 
 #define PIN_ADD_T 0
 #define TRY_ROTATE 1
+#define ONE_PASS_COINCIDENCE_CHECK 0
+#define APPROXIMATE_CUBICS 1
 
 #define DEBUG_UNUSED 0 // set to expose unused functions
-#define FORCE_RELEASE 0  // set force release to 1 for multiple thread -- no debugging
+#define FORCE_RELEASE 1  // set force release to 1 for multiple thread -- no debugging
 
 #if FORCE_RELEASE || defined SK_RELEASE
 
@@ -49,8 +50,10 @@
 #define DEBUG_PATH_CONSTRUCTION 0
 #define DEBUG_SHOW_WINDING 0
 #define DEBUG_SORT 0
+#define DEBUG_UNSORTABLE 0
 #define DEBUG_WIND_BUMP 0
 #define DEBUG_WINDING 0
+#define DEBUG_WINDING_AT_T 0
 
 #else
 
@@ -69,8 +72,10 @@
 #define DEBUG_PATH_CONSTRUCTION 1
 #define DEBUG_SHOW_WINDING 0
 #define DEBUG_SORT 1
+#define DEBUG_UNSORTABLE 1
 #define DEBUG_WIND_BUMP 0
 #define DEBUG_WINDING 1
+#define DEBUG_WINDING_AT_T 1
 
 #endif
 
@@ -114,7 +119,7 @@
         Intersections& intersections) {
     MAKE_CONST_CUBIC(aCubic, a);
     MAKE_CONST_LINE(bLine, b);
-    return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
+    return intersect(aCubic, bLine, intersections);
 }
 
 static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
@@ -130,11 +135,23 @@
     return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
 }
 
-static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
+#if APPROXIMATE_CUBICS
+static int CubicQuadIntersect(const SkPoint a[4], const SkPoint b[3],
         Intersections& intersections) {
     MAKE_CONST_CUBIC(aCubic, a);
+    MAKE_CONST_QUAD(bQuad, b);
+    return intersect(aCubic, bQuad, intersections);
+}
+#endif
+
+static int CubicIntersect(const SkPoint a[4], const SkPoint b[4], Intersections& intersections) {
+    MAKE_CONST_CUBIC(aCubic, a);
     MAKE_CONST_CUBIC(bCubic, b);
+#if APPROXIMATE_CUBICS
+    intersect2(aCubic, bCubic, intersections);
+#else
     intersect(aCubic, bCubic, intersections);
+#endif
     return intersections.fUsed;
 }
 
@@ -288,15 +305,13 @@
 
 static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
     MAKE_CONST_QUAD(quad, a);
-    double x;
-    dxdy_at_t(quad, t, x, *(double*) 0);
+    double x = dx_at_t(quad, t);
     return SkDoubleToScalar(x);
 }
 
 static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
     MAKE_CONST_CUBIC(cubic, a);
-    double x;
-    dxdy_at_t(cubic, t, x, *(double*) 0);
+    double x = dx_at_t(cubic, t);
     return SkDoubleToScalar(x);
 }
 
@@ -313,15 +328,13 @@
 
 static SkScalar QuadDYAtT(const SkPoint a[3], double t) {
     MAKE_CONST_QUAD(quad, a);
-    double y;
-    dxdy_at_t(quad, t, *(double*) 0, y);
+    double y = dy_at_t(quad, t);
     return SkDoubleToScalar(y);
 }
 
 static SkScalar CubicDYAtT(const SkPoint a[4], double t) {
     MAKE_CONST_CUBIC(cubic, a);
-    double y;
-    dxdy_at_t(cubic, t, *(double*) 0, y);
+    double y = dy_at_t(cubic, t);
     return SkDoubleToScalar(y);
 }
 
@@ -379,34 +392,19 @@
     CubicSubDivide
 };
 
-static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
-        _Line sub) {
+static void LineSubDivideHD(const SkPoint a[2], double startT, double endT, _Line& dst) {
     MAKE_CONST_LINE(aLine, a);
-    _Line dst;
     sub_divide(aLine, startT, endT, dst);
-    sub[0] = dst[0];
-    sub[1] = dst[1];
 }
 
-static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
-        Quadratic sub) {
+static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT, Quadratic& dst) {
     MAKE_CONST_QUAD(aQuad, a);
-    Quadratic dst;
     sub_divide(aQuad, startT, endT, dst);
-    sub[0] = dst[0];
-    sub[1] = dst[1];
-    sub[2] = dst[2];
 }
 
-static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
-        Cubic sub) {
+static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT, Cubic& dst) {
     MAKE_CONST_CUBIC(aCubic, a);
-    Cubic dst;
     sub_divide(aCubic, startT, endT, dst);
-    sub[0] = dst[0];
-    sub[1] = dst[1];
-    sub[2] = dst[2];
-    sub[3] = dst[3];
 }
 
 #if DEBUG_UNUSED
@@ -513,6 +511,34 @@
     return intersectRay(aQuad, bLine, intersections);
 }
 
+static bool LineVertical(const SkPoint a[2], double startT, double endT) {
+    MAKE_CONST_LINE(aLine, a);
+    double x[2];
+    xy_at_t(aLine, startT, x[0], *(double*) 0);
+    xy_at_t(aLine, endT, x[1], *(double*) 0);
+    return AlmostEqualUlps((float) x[0], (float) x[1]);
+}
+
+static bool QuadVertical(const SkPoint a[3], double startT, double endT) {
+    SkPoint dst[3];
+    QuadSubDivide(a, startT, endT, dst);
+    return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX);
+}
+
+static bool CubicVertical(const SkPoint a[4], double startT, double endT) {
+    SkPoint dst[4];
+    CubicSubDivide(a, startT, endT, dst);
+    return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX)
+            && AlmostEqualUlps(dst[2].fX, dst[3].fX);
+}
+
+static bool (* const SegmentVertical[])(const SkPoint [], double , double) = {
+    NULL,
+    LineVertical,
+    QuadVertical,
+    CubicVertical
+};
+
 class Segment;
 
 struct Span {
@@ -575,6 +601,7 @@
             return cmp < 0;
         }
         // at this point, the initial tangent line is coincident
+        // see if edges curl away from each other
         if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
                 || !approximately_zero(rh.fSide))) {
             // FIXME: running demo will trigger this assertion
@@ -591,12 +618,14 @@
             if (longer.lengthen() | rhLonger.lengthen()) {
                 return longer < rhLonger;
             }
+    #if 0
             // what if we extend in the other direction?
             longer = *this;
             rhLonger = rh;
             if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
                 return longer < rhLonger;
             }
+    #endif
         }
         if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
                 || (rh.fVerb == SkPath::kLine_Verb
@@ -722,6 +751,7 @@
         setSpans();
     }
 
+
     void setSpans() {
         double startT = (*fSpans)[fStart].fT;
         double endT = (*fSpans)[fEnd].fT;
@@ -731,41 +761,83 @@
             LineSubDivideHD(fPts, startT, endT, l);
             // OPTIMIZATION: for pure line compares, we never need fTangent1.c
             fTangent1.lineEndPoints(l);
-            fUnsortable = dx() == 0 && dy() == 0;
             fSide = 0;
             break;
         case SkPath::kQuad_Verb:
             QuadSubDivideHD(fPts, startT, endT, fQ);
             fTangent1.quadEndPoints(fQ, 0, 1);
+        #if 1 // FIXME: try enabling this and see if a) it's called and b) does it break anything
+            if (dx() == 0 && dy() == 0) {
+                SkDebugf("*** %s quad is line\n", __FUNCTION__);
+                fTangent1.quadEndPoints(fQ);
+            }
+        #endif
             fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
             break;
-        case SkPath::kCubic_Verb:
+        case SkPath::kCubic_Verb: {
             Cubic c;
+            int nextC = 2;
             CubicSubDivideHD(fPts, startT, endT, c);
             fTangent1.cubicEndPoints(c, 0, 1);
-            fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
-            break;
+            if (dx() == 0 && dy() == 0) {
+                fTangent1.cubicEndPoints(c, 0, 2);
+                nextC = 3;
+        #if 1 // FIXME: try enabling this and see if a) it's called and b) does it break anything
+                if (dx() == 0 && dy() == 0) {
+                    SkDebugf("*** %s cubic is line\n");
+                    fTangent1.cubicEndPoints(c, 0, 3);
+                }
+        #endif
+            }
+            fSide = -fTangent1.pointDistance(c[nextC]); // not normalized -- compare sign only
+            if (nextC == 2 && approximately_zero(fSide)) {
+                fSide = -fTangent1.pointDistance(c[3]);
+            }
+            } break;
         default:
             SkASSERT(0);
         }
+        fUnsortable = dx() == 0 && dy() == 0;
         if (fUnsortable) {
             return;
         }
         SkASSERT(fStart != fEnd);
         int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
         for (int index = fStart; index != fEnd; index += step) {
-            if ((*fSpans)[index].fUnsortableStart) {
-                fUnsortable = true;
-                return;
+#if 1
+            const Span& thisSpan = (*fSpans)[index];
+            const Span& nextSpan = (*fSpans)[index + step];
+            if (thisSpan.fTiny || thisSpan.fT == nextSpan.fT) {
+                continue;
             }
-#if 0
-            if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
-                SkASSERT(0);
+            fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd;
+#if DEBUG_UNSORTABLE
+            if (fUnsortable) {
+                SkPoint iPt, ePt;
+                (*SegmentXYAtT[fVerb])(fPts, thisSpan.fT, &iPt);
+                (*SegmentXYAtT[fVerb])(fPts, nextSpan.fT, &ePt);
+                SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
+                        index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
+            }
+#endif
+            return;
+#else
+            if ((*fSpans)[index].fUnsortableStart) {
                 fUnsortable = true;
                 return;
             }
 #endif
         }
+#if 1
+#if DEBUG_UNSORTABLE
+        SkPoint iPt, ePt;
+        (*SegmentXYAtT[fVerb])(fPts, startT, &iPt);
+        (*SegmentXYAtT[fVerb])(fPts, endT, &ePt);
+        SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
+            fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
+#endif
+        fUnsortable = true;
+#endif
     }
 
     Segment* segment() const {
@@ -951,12 +1023,14 @@
     void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
         lineTo();
         moveTo();
+        fDefer[1] = pt3;
+        nudge();
+        fDefer[0] = fDefer[1];
 #if DEBUG_PATH_CONSTRUCTION
         SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
-                pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
+                pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
 #endif
-        fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
-        fDefer[0] = fDefer[1] = pt3;
+        fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
         fEmpty = false;
     }
 
@@ -1004,6 +1078,7 @@
             return;
         }
         moveTo();
+        nudge();
         fEmpty = false;
 #if DEBUG_PATH_CONSTRUCTION
         SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
@@ -1016,15 +1091,25 @@
         return fPathPtr;
     }
 
+    void nudge() {
+        if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX)
+                || !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) {
+            return;
+        }
+        fDefer[1] = fFirstPt;
+    }
+
     void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
         lineTo();
         moveTo();
+        fDefer[1] = pt2;
+        nudge();
+        fDefer[0] = fDefer[1];
 #if DEBUG_PATH_CONSTRUCTION
         SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
-                pt1.fX, pt1.fY, pt2.fX, pt2.fY);
+                pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
 #endif
-        fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
-        fDefer[0] = fDefer[1] = pt2;
+        fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
         fEmpty = false;
     }
 
@@ -1084,18 +1169,18 @@
         if (activeAngleInner(index, done, angles)) {
             return true;
         }
-        double referenceT = fTs[index].fT;
         int lesser = index;
-        while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
+        while (--lesser >= 0 && equalPoints(index, lesser)) {
             if (activeAngleOther(lesser, done, angles)) {
                 return true;
             }
         }
+        lesser = index;
         do {
             if (activeAngleOther(index, done, angles)) {
                 return true;
             }
-        } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
+        } while (++index < fTs.count() && equalPoints(index, lesser));
         return false;
     }
 
@@ -1144,7 +1229,7 @@
     void activeLeftTop(SkPoint& result) const {
         SkASSERT(!done());
         int count = fTs.count();
-        result.fY = SK_ScalarMax;
+        result.fX = result.fY = SK_ScalarMax;
         bool lastDone = true;
         bool lastUnsortable = false;
         for (int index = 0; index < count; ++index) {
@@ -1166,7 +1251,6 @@
             lastDone = span.fDone;
             lastUnsortable = span.fUnsortableEnd;
         }
-        SkASSERT(result.fY < SK_ScalarMax);
     }
 
     bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
@@ -1229,8 +1313,8 @@
             (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
                     (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
             (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
-            SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
-            SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
+            SkASSERT(AlmostEqualUlps(angle0Pt.fX, newPt.fX));
+            SkASSERT(AlmostEqualUlps(angle0Pt.fY, newPt.fY));
         }
 #endif
         angle->set(fPts, fVerb, this, start, end, fTs);
@@ -1485,41 +1569,45 @@
         }
         span->fUnsortableStart = false;
         span->fUnsortableEnd = false;
-        if (span - fTs.begin() > 0 && !span[-1].fDone
-                && !precisely_negative(newT - span[-1].fT)
- //               && approximately_negative(newT - span[-1].fT)
-                && xyAtT(&span[-1]) == xyAtT(span)) {
-            span[-1].fTiny = true;
-            span[-1].fDone = true;
-            if (approximately_negative(newT - span[-1].fT)) {
+        int less = -1;
+        while (&span[less + 1] - fTs.begin() > 0 && !span[less].fDone
+                && !precisely_negative(newT - span[less].fT)
+ //               && approximately_negative(newT - span[less].fT)
+                && xyAtT(&span[less]) == xyAtT(span)) {
+            span[less].fTiny = true;
+            span[less].fDone = true;
+            if (approximately_negative(newT - span[less].fT)) {
                 if (approximately_greater_than_one(newT)) {
-                    span[-1].fUnsortableStart = true;
-                    span[-2].fUnsortableEnd = true;
+                    span[less].fUnsortableStart = true;
+                    span[less - 1].fUnsortableEnd = true;
                 }
-                if (approximately_less_than_zero(span[-1].fT)) {
-                    span->fUnsortableStart = true;
-                    span[-1].fUnsortableEnd = true;
+                if (approximately_less_than_zero(span[less].fT)) {
+                    span[less + 1].fUnsortableStart = true;
+                    span[less].fUnsortableEnd = true;
                 }
             }
             ++fDoneSpans;
+            --less;
         }
-        if (fTs.end() - span > 1 && !span->fDone
-                && !precisely_negative(span[1].fT - newT)
- //               && approximately_negative(span[1].fT - newT)
-                && xyAtT(&span[1]) == xyAtT(span)) {
-            span->fTiny = true;
-            span->fDone = true;
-            if (approximately_negative(span[1].fT - newT)) {
-                if (approximately_greater_than_one(span[1].fT)) {
-                    span->fUnsortableStart = true;
-                    span[-1].fUnsortableEnd = true;
+        int more = 1;
+        while (fTs.end() - &span[more - 1] > 1 && !span[more - 1].fDone
+                && !precisely_negative(span[more].fT - newT)
+ //               && approximately_negative(span[more].fT - newT)
+                && xyAtT(&span[more]) == xyAtT(span)) {
+            span[more - 1].fTiny = true;
+            span[more - 1].fDone = true;
+            if (approximately_negative(span[more].fT - newT)) {
+                if (approximately_greater_than_one(span[more].fT)) {
+                    span[more + 1].fUnsortableStart = true;
+                    span[more].fUnsortableEnd = true;
                 }
                 if (approximately_less_than_zero(newT)) {
-                    span[1].fUnsortableStart = true;
-                    span->fUnsortableEnd = true;
+                    span[more].fUnsortableStart = true;
+                    span[more - 1].fUnsortableEnd = true;
                 }
             }
             ++fDoneSpans;
+            ++more;
         }
         return insertedAt;
     }
@@ -1606,6 +1694,23 @@
         }
     }
 
+    int addUnsortableT(double newT, Segment* other, bool start) {
+        int result = addT(newT, other);
+        Span* span = &fTs[result];
+        if (start) {
+            if (result > 0) {
+                span[result - 1].fUnsortableEnd = true;
+            }
+            span[result].fUnsortableStart = true;
+        } else {
+            span[result].fUnsortableEnd = true;
+            if (result + 1 < fTs.count()) {
+                span[result + 1].fUnsortableStart = true;
+            }
+        }
+        return result;
+    }
+
     int bumpCoincidentThis(const Span* oTest, bool opp, int index,
             SkTDArray<double>& outsideTs) {
         int oWindValue = oTest->fWindValue;
@@ -1693,7 +1798,7 @@
     }
 
     // FIXME: this doesn't prevent the same span from being added twice
-    // fix in caller, assert here?
+    // fix in caller, SkASSERT here?
     void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
         int tCount = fTs.count();
         for (int tIndex = 0; tIndex < tCount; ++tIndex) {
@@ -1757,6 +1862,13 @@
         return oIndex;
     }
 
+    bool betweenTs(int lesser, double testT, int greater) {
+        if (lesser > greater) {
+            SkTSwap<int>(lesser, greater);
+        }
+        return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
+    }
+
     const Bounds& bounds() const {
         return fBounds;
     }
@@ -1893,108 +2005,96 @@
         return windSum(minIndex);
     }
 
-    int crossedSpanX(const SkPoint& basePt, SkScalar& bestX, double& hitT, bool opp) const {
-        int bestT = -1;
-        SkScalar left = bounds().fLeft;
-        SkScalar right = bounds().fRight;
-        int end = 0;
-        do {
-            int start = end;
-            end = nextSpan(start, 1);
-            if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
+    int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool& hitSomething,
+            double mid, bool opp, bool current) const {
+        SkScalar bottom = fBounds.fBottom;
+        int bestTIndex = -1;
+        if (bottom <= bestY) {
+            return bestTIndex;
+        }
+        SkScalar top = fBounds.fTop;
+        if (top >= basePt.fY) {
+            return bestTIndex;
+        }
+        if (fBounds.fLeft > basePt.fX) {
+            return bestTIndex;
+        }
+        if (fBounds.fRight < basePt.fX) {
+            return bestTIndex;
+        }
+        if (fBounds.fLeft == fBounds.fRight) {
+            // if vertical, and directly above test point, wait for another one
+            return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
+        }
+        // intersect ray starting at basePt with edge
+        Intersections intersections;
+        // OPTIMIZE: use specialty function that intersects ray with curve,
+        // returning t values only for curve (we don't care about t on ray)
+        int pts = (*VSegmentIntersect[fVerb])(fPts, top, bottom, basePt.fX, false, intersections);
+        if (pts == 0 || (current && pts == 1)) {
+            return bestTIndex;
+        }
+        if (current) {
+            SkASSERT(pts > 1);
+            int closestIdx = 0;
+            double closest = fabs(intersections.fT[0][0] - mid);
+            for (int idx = 1; idx < pts; ++idx) {
+                double test = fabs(intersections.fT[0][idx] - mid);
+                if (closest > test) {
+                    closestIdx = idx;
+                    closest = test;
+                }
+            }
+            if (closestIdx < pts - 1) {
+                intersections.fT[0][closestIdx] = intersections.fT[0][pts - 1];
+            }
+            --pts;
+        }
+        double bestT = -1;
+        for (int index = 0; index < pts; ++index) {
+            double foundT = intersections.fT[0][index];
+            if (approximately_less_than_zero(foundT)
+                        || approximately_greater_than_one(foundT)) {
                 continue;
             }
-            SkPoint edge[4];
-            double startT = fTs[start].fT;
-            double endT = fTs[end].fT;
-            (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
-            // intersect ray starting at basePt with edge
-            Intersections intersections;
-            // FIXME: always use original and limit results to T values within
-            // start t and end t.
-            // OPTIMIZE: use specialty function that intersects ray with curve,
-            // returning t values only for curve (we don't care about t on ray)
-            int pts = (*HSegmentIntersect[fVerb])(edge, left, right, basePt.fY,
-                    false, intersections);
-            if (pts == 0) {
+            SkScalar testY = (*SegmentYAtT[fVerb])(fPts, foundT);
+            if (approximately_negative(testY - bestY)
+                    || approximately_negative(basePt.fY - testY)) {
                 continue;
             }
             if (pts > 1 && fVerb == SkPath::kLine_Verb) {
-            // if the intersection is edge on, wait for another one
-                continue;
+                return SK_MinS32; // if the intersection is edge on, wait for another one
             }
-            for (int index = 0; index < pts; ++index) {
-                double foundT = intersections.fT[0][index];
-                double testT = startT + (endT - startT) * foundT;
-                SkScalar testX = (*SegmentXAtT[fVerb])(fPts, testT);
-                if (bestX < testX && testX < basePt.fX) {
-                    if (fVerb > SkPath::kLine_Verb
-                            && !approximately_less_than_zero(foundT)
-                            && !approximately_greater_than_one(foundT)) {
-                        SkScalar dy = (*SegmentDYAtT[fVerb])(fPts, testT);
-                        if (approximately_zero(dy)) {
-                            continue;
-                        }
-                    }
-                    bestX = testX;
-                    bestT = foundT < 1 ? start : end;
-                    hitT = testT;
+            if (fVerb > SkPath::kLine_Verb) {
+                SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, foundT);
+                if (approximately_zero(dx)) {
+                    return SK_MinS32; // hit vertical, wait for another one
                 }
             }
-        } while (fTs[end].fT != 1);
-        return bestT;
-    }
-
-    int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool opp) const {
-        int bestT = -1;
-        SkScalar top = bounds().fTop;
-        SkScalar bottom = bounds().fBottom;
+            bestY = testY;
+            bestT = foundT;
+        }
+        if (bestT < 0) {
+            return bestTIndex;
+        }
+        SkASSERT(bestT >= 0);
+        SkASSERT(bestT <= 1);
+        int start;
         int end = 0;
         do {
-            int start = end;
+            start = end;
             end = nextSpan(start, 1);
-            if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
-                continue;
-            }
-            SkPoint edge[4];
-            double startT = fTs[start].fT;
-            double endT = fTs[end].fT;
-            (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
-            // intersect ray starting at basePt with edge
-            Intersections intersections;
-            // FIXME: always use original and limit results to T values within
-            // start t and end t.
-            // OPTIMIZE: use specialty function that intersects ray with curve,
-            // returning t values only for curve (we don't care about t on ray)
-            int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
-                    false, intersections);
-            if (pts == 0) {
-                continue;
-            }
-            if (pts > 1 && fVerb == SkPath::kLine_Verb) {
-            // if the intersection is edge on, wait for another one
-                continue;
-            }
-            for (int index = 0; index < pts; ++index) {
-                double foundT = intersections.fT[0][index];
-                double testT = startT + (endT - startT) * foundT;
-                SkScalar testY = (*SegmentYAtT[fVerb])(fPts, testT);
-                if (bestY < testY && testY < basePt.fY) {
-                    if (fVerb > SkPath::kLine_Verb
-                            && !approximately_less_than_zero(foundT)
-                            && !approximately_greater_than_one(foundT)) {
-                        SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
-                        if (approximately_zero(dx)) {
-                            continue;
-                        }
-                    }
-                    bestY = testY;
-                    bestT = foundT < 1 ? start : end;
-                    hitT = testT;
-                }
-            }
-        } while (fTs[end].fT != 1);
-        return bestT;
+        } while (fTs[end].fT < bestT);
+        // FIXME: see next candidate for a better pattern to find the next start/end pair
+        while (start + 1 < end && fTs[start].fDone) {
+            ++start;
+        }
+        if (!isCanceled(start)) {
+            hitT = bestT;
+            bestTIndex = start;
+            hitSomething = true;
+        }
+        return bestTIndex;
     }
 
     void decrementSpan(Span* span) {
@@ -2050,6 +2150,19 @@
         return done(SkMin32(angle->start(), angle->end()));
     }
 
+    bool equalPoints(int greaterTIndex, int lesserTIndex) {
+        SkASSERT(greaterTIndex >= lesserTIndex);
+        double greaterT = fTs[greaterTIndex].fT;
+        double lesserT = fTs[lesserTIndex].fT;
+        if (greaterT == lesserT) {
+            return true;
+        }
+        if (!approximately_negative(greaterT - lesserT)) {
+            return false;
+        }
+        return xyAtT(greaterTIndex) == xyAtT(lesserTIndex);
+    }
+
     /*
      The M and S variable name parts stand for the operators.
        Mi stands for Minuend (see wiki subtraction, analogous to difference)
@@ -2172,217 +2285,6 @@
         return nextSegment;
     }
 
-    // so the span needs to contain the pairing info found here
-    // this should include the winding computed for the edge, and
-    //  what edge it connects to, and whether it is discarded
-    //  (maybe discarded == abs(winding) > 1) ?
-    // only need derivatives for duration of sorting, add a new struct
-    // for pairings, remove extra spans that have zero length and
-    //  reference an unused other
-    // for coincident, the last span on the other may be marked done
-    //  (always?)
-
-    // if loop is exhausted, contour may be closed.
-    // FIXME: pass in close point so we can check for closure
-
-    // given a segment, and a sense of where 'inside' is, return the next
-    // segment. If this segment has an intersection, or ends in multiple
-    // segments, find the mate that continues the outside.
-    // note that if there are multiples, but no coincidence, we can limit
-    // choices to connections in the correct direction
-
-    // mark found segments as done
-
-    // start is the index of the beginning T of this edge
-    // it is guaranteed to have an end which describes a non-zero length (?)
-    // winding -1 means ccw, 1 means cw
-    Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
-            int& nextStart, int& nextEnd, int& winding, int& spanWinding,
-            bool& unsortable) {
-        const int startIndex = nextStart;
-        const int endIndex = nextEnd;
-        int outerWinding = winding;
-        int innerWinding = winding + spanWinding;
-    #if DEBUG_WINDING
-        SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
-                __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
-    #endif
-        if (useInnerWinding(outerWinding, innerWinding)) {
-            outerWinding = innerWinding;
-        }
-        SkASSERT(startIndex != endIndex);
-        int count = fTs.count();
-        SkASSERT(startIndex < endIndex ? startIndex < count - 1
-                : startIndex > 0);
-        int step = SkSign32(endIndex - startIndex);
-        int end = nextExactSpan(startIndex, step);
-        SkASSERT(end >= 0);
-        Span* endSpan = &fTs[end];
-        Segment* other;
-        if (isSimple(end)) {
-        // mark the smaller of startIndex, endIndex done, and all adjacent
-        // spans with the same T value (but not 'other' spans)
-    #if DEBUG_WINDING
-            SkDebugf("%s simple\n", __FUNCTION__);
-    #endif
-            int min = SkMin32(startIndex, endIndex);
-            if (fTs[min].fDone) {
-                return NULL;
-            }
-            markDone(min, outerWinding);
-            other = endSpan->fOther;
-            nextStart = endSpan->fOtherIndex;
-            double startT = other->fTs[nextStart].fT;
-            nextEnd = nextStart;
-            do {
-                nextEnd += step;
-            }
-             while (precisely_zero(startT - other->fTs[nextEnd].fT));
-            SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
-            return other;
-        }
-        // more than one viable candidate -- measure angles to find best
-        SkTDArray<Angle> angles;
-        SkASSERT(startIndex - endIndex != 0);
-        SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
-        addTwoAngles(startIndex, end, angles);
-        buildAngles(end, angles, false);
-        SkTDArray<Angle*> sorted;
-        bool sortable = SortAngles(angles, sorted);
-        int angleCount = angles.count();
-        int firstIndex = findStartingEdge(sorted, startIndex, end);
-        SkASSERT(firstIndex >= 0);
-    #if DEBUG_SORT
-        debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
-    #endif
-        if (!sortable) {
-            unsortable = true;
-            return NULL;
-        }
-        SkASSERT(sorted[firstIndex]->segment() == this);
-    #if DEBUG_WINDING
-        SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
-    #endif
-        int sumWinding = winding - spanSign(sorted[firstIndex]);
-        int nextIndex = firstIndex + 1;
-        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
-        const Angle* foundAngle = NULL;
-        // FIXME: found done logic probably fails if there are more than 4
-        // sorted angles. It should bias towards the first and last undone
-        // edges -- but not sure that it won't choose a middle (incorrect)
-        // edge if one is undone
-        bool foundDone = false;
-        bool foundDone2 = false;
-        // iterate through the angle, and compute everyone's winding
-        bool altFlipped = false;
-        bool foundFlipped = false;
-        int foundSum = SK_MinS32;
-        Segment* nextSegment;
-        int lastNonZeroSum = winding;
-        do {
-            if (nextIndex == angleCount) {
-                nextIndex = 0;
-            }
-            const Angle* nextAngle = sorted[nextIndex];
-            int maxWinding = sumWinding;
-            if (sumWinding) {
-                lastNonZeroSum = sumWinding;
-            }
-            nextSegment = nextAngle->segment();
-            bool nextDone = nextSegment->done(nextAngle);
-            bool nextTiny = nextSegment->tiny(nextAngle);
-            sumWinding -= nextSegment->spanSign(nextAngle);
-            altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
-    #if 0 && DEBUG_WINDING
-            SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
-            SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
-                    nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
-    #endif
-           if (!sumWinding) {
-                if (!active) {
-                    // FIXME: shouldn't this call mark and chase done ?
-                    markDone(SkMin32(startIndex, endIndex), outerWinding);
-                    // FIXME: shouldn't this call mark and chase winding ?
-                    nextSegment->markWinding(SkMin32(nextAngle->start(),
-                                nextAngle->end()), maxWinding);
-    #if DEBUG_WINDING
-                    SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
-    #endif
-                    return NULL;
-                }
-                if (!foundAngle || foundDone) {
-                    foundAngle = nextAngle;
-                    foundDone = nextDone && !nextTiny;
-                    foundFlipped = altFlipped;
-                }
-                continue;
-            }
-
-            if (!maxWinding && (!foundAngle || foundDone2)) {
-        #if DEBUG_WINDING
-                if (foundAngle && foundDone2) {
-                    SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
-                }
-        #endif
-                foundAngle = nextAngle;
-                foundDone2 = nextDone && !nextTiny;
-                foundFlipped = altFlipped;
-                foundSum = sumWinding;
-            }
-            if (nextSegment->done()) {
-                continue;
-            }
-            // if the winding is non-zero, nextAngle does not connect to
-            // current chain. If we haven't done so already, mark the angle
-            // as done, record the winding value, and mark connected unambiguous
-            // segments as well.
-            if (nextSegment->windSum(nextAngle) == SK_MinS32) {
-                if (useInnerWinding(maxWinding, sumWinding)) {
-                    maxWinding = sumWinding;
-                }
-                Span* last;
-                if (foundAngle) {
-                    last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
-                } else {
-                    last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
-                }
-                if (last) {
-                    *chase.append() = last;
-    #if DEBUG_WINDING
-                    SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
-                            last->fOther->fTs[last->fOtherIndex].fOther->debugID());
-    #endif
-                }
-            }
-        } while (++nextIndex != lastIndex);
-        markDone(SkMin32(startIndex, endIndex), outerWinding);
-        if (!foundAngle) {
-            return NULL;
-        }
-        nextStart = foundAngle->start();
-        nextEnd = foundAngle->end();
-        nextSegment = foundAngle->segment();
-        int flipped = foundFlipped ? -1 : 1;
-        spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
-                SkMin32(nextStart, nextEnd));
-        if (winding) {
-    #if DEBUG_WINDING
-            SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
-            if (foundSum == SK_MinS32) {
-                SkDebugf("?");
-            } else {
-                SkDebugf("%d", foundSum);
-            }
-            SkDebugf("\n");
-     #endif
-            winding = foundSum;
-        }
-    #if DEBUG_WINDING
-        SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
-    #endif
-        return nextSegment;
-    }
-
     Segment* findNextWinding(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
             bool& unsortable) {
         const int startIndex = nextStart;
@@ -2441,13 +2343,13 @@
                 sorted[firstIndex]->sign());
     #endif
         int sumWinding = updateWinding(endIndex, startIndex);
-        int outside = sumWinding & 1; // associate pairs together to avoid figure eights
         int nextIndex = firstIndex + 1;
         int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
         const Angle* foundAngle = NULL;
         bool foundDone = false;
         // iterate through the angle, and compute everyone's winding
         Segment* nextSegment;
+        int activeCount = 0;
         do {
             SkASSERT(nextIndex != firstIndex);
             if (nextIndex == angleCount) {
@@ -2458,9 +2360,16 @@
             int maxWinding;
             bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
                     maxWinding, sumWinding);
-            if (activeAngle && (!foundAngle || foundDone) && outside != (sumWinding & 1)) {
-                foundAngle = nextAngle;
-                foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
+            if (activeAngle) {
+                ++activeCount;
+                if (!foundAngle || (foundDone && activeCount & 1)) {
+                    if (nextSegment->tiny(nextAngle)) {
+                        unsortable = true;
+                        return NULL;
+                    }
+                    foundAngle = nextAngle;
+                    foundDone = nextSegment->done(nextAngle);
+                }
             }
             if (nextSegment->done()) {
                 continue;
@@ -2484,7 +2393,6 @@
         nextStart = foundAngle->start();
         nextEnd = foundAngle->end();
         nextSegment = foundAngle->segment();
-
     #if DEBUG_WINDING
         SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
                 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
@@ -2516,6 +2424,7 @@
             other = endSpan->fOther;
             nextStart = endSpan->fOtherIndex;
             double startT = other->fTs[nextStart].fT;
+        #if 01 // FIXME: I don't know why the logic here is difference from the winding case
             SkDEBUGCODE(bool firstLoop = true;)
             if ((approximately_less_than_zero(startT) && step < 0)
                     || (approximately_greater_than_one(startT) && step > 0)) {
@@ -2523,11 +2432,13 @@
                 SkDEBUGCODE(firstLoop = false;)
             }
             do {
+        #endif
                 nextEnd = nextStart;
                 do {
                     nextEnd += step;
                 }
                  while (precisely_zero(startT - other->fTs[nextEnd].fT));
+        #if 01
                 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
                     break;
                 }
@@ -2537,6 +2448,7 @@
                 SkDEBUGCODE(firstLoop = false;)
                 step = -step;
             } while (true);
+        #endif
             SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
             return other;
         }
@@ -2549,6 +2461,9 @@
         bool sortable = SortAngles(angles, sorted);
         if (!sortable) {
             unsortable = true;
+    #if DEBUG_SORT
+            debugShowSort(__FUNCTION__, sorted, findStartingEdge(sorted, startIndex, end), 0, 0);
+    #endif
             return NULL;
         }
         int angleCount = angles.count();
@@ -2560,27 +2475,41 @@
         SkASSERT(sorted[firstIndex]->segment() == this);
         int nextIndex = firstIndex + 1;
         int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
-        const Angle* nextAngle;
+        const Angle* foundAngle = NULL;
+        bool foundDone = false;
         Segment* nextSegment;
-        bool foundAngle = false;
+        int activeCount = 0;
         do {
+            SkASSERT(nextIndex != firstIndex);
             if (nextIndex == angleCount) {
                 nextIndex = 0;
             }
-            nextAngle = sorted[nextIndex];
+            const Angle* nextAngle = sorted[nextIndex];
             nextSegment = nextAngle->segment();
-            if (!nextSegment->done(nextAngle) || nextSegment->tiny(nextAngle)) {
-                foundAngle = true;
-                break;
+            ++activeCount;
+            if (!foundAngle || (foundDone && activeCount & 1)) {
+                if (nextSegment->tiny(nextAngle)) {
+                    unsortable = true;
+                    return NULL;
+                }
+                foundAngle = nextAngle;
+                foundDone = nextSegment->done(nextAngle);
+            }
+            if (nextSegment->done()) {
+                continue;
             }
         } while (++nextIndex != lastIndex);
         markDone(SkMin32(startIndex, endIndex), 1);
         if (!foundAngle) {
-            nextIndex = firstIndex + 1 == angleCount ? 0 : firstIndex + 1;
-            nextAngle = sorted[nextIndex];
+            return NULL;
         }
-        nextStart = nextAngle->start();
-        nextEnd = nextAngle->end();
+        nextStart = foundAngle->start();
+        nextEnd = foundAngle->end();
+        nextSegment = foundAngle->segment();
+    #if DEBUG_WINDING
+        SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
+                __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
+     #endif
         return nextSegment;
     }
 
@@ -2599,6 +2528,7 @@
     }
 
     // FIXME: this is tricky code; needs its own unit test
+    // note that fOtherIndex isn't computed yet, so it can't be used here
     void findTooCloseToCall() {
         int count = fTs.count();
         if (count < 3) { // require t=0, x, 1 at minimum
@@ -2665,7 +2595,7 @@
                     continue;
                 }
                 if (moSpan.fOther == tOther) {
-                    if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
+                    if (tOther->windValueAt(moSpan.fOtherT) == 0) {
                         moStart = -1;
                         break;
                     }
@@ -2697,7 +2627,7 @@
                     continue;
                 }
                 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
-                    if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
+                    if (mOther->windValueAt(toSpan.fOtherT) == 0) {
                         moStart = -1;
                         break;
                     }
@@ -2833,22 +2763,78 @@
         fVerb = verb;
     }
 
+    void initWinding(int start, int end) {
+        int local = spanSign(start, end);
+        int oppLocal = oppSign(start, end);
+        (void) markAndChaseWinding(start, end, local, oppLocal);
+        // OPTIMIZATION: the reverse mark and chase could skip the first marking
+        (void) markAndChaseWinding(end, start, local, oppLocal);
+    }
+
     void initWinding(int start, int end, int winding, int oppWinding) {
         int local = spanSign(start, end);
         if (local * winding >= 0) {
             winding += local;
         }
-        local = oppSign(start, end);
-        if (local * oppWinding >= 0) {
-            oppWinding += local;
+        int oppLocal = oppSign(start, end);
+        if (oppLocal * oppWinding >= 0) {
+            oppWinding += oppLocal;
         }
         (void) markAndChaseWinding(start, end, winding, oppWinding);
     }
 
+/*
+when we start with a vertical intersect, we try to use the dx to determine if the edge is to
+the left or the right of vertical. This determines if we need to add the span's
+sign or not. However, this isn't enough.
+If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
+If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
+from has the same x direction as this span, the winding should change. If the dx is opposite, then
+the same winding is shared by both.
+*/
+    void initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
+            SkScalar hitOppDx) {
+        SkASSERT(hitDx || !winding);
+        SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
+        SkASSERT(dx);
+        int windVal = windValue(SkMin32(start, end));
+    #if DEBUG_WINDING_AT_T
+        SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding,
+                hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
+    #endif
+        if (!winding) {
+            winding = dx < 0 ? windVal : -windVal;
+        } else if (winding * dx < 0) {
+            int sideWind = winding + (dx < 0 ? windVal : -windVal);
+            if (abs(winding) < abs(sideWind)) {
+                winding = sideWind;
+            }
+        }
+    #if DEBUG_WINDING_AT_T
+        SkDebugf(" winding=%d\n", winding);
+    #endif
+        int oppLocal = oppSign(start, end);
+        SkASSERT(hitOppDx || !oppWind || !oppLocal);
+        int oppWindVal = oppValue(SkMin32(start, end));
+        if (!oppWind) {
+            oppWind = dx < 0 ? oppWindVal : -oppWindVal;
+        } else if (hitOppDx * dx >= 0) {
+            int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
+            if (abs(oppWind) < abs(oppSideWind)) {
+                oppWind = oppSideWind;
+            }
+        }
+        (void) markAndChaseWinding(start, end, winding, oppWind);
+    }
+
     bool intersected() const {
         return fTs.count() > 0;
     }
 
+    bool isCanceled(int tIndex) const {
+        return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
+    }
+
     bool isConnected(int startIndex, int endIndex) const {
         return fTs[startIndex].fWindSum != SK_MinS32
                 || fTs[endIndex].fWindSum != SK_MinS32;
@@ -2905,6 +2891,10 @@
         return fBounds.fLeft == fBounds.fRight;
     }
 
+    bool isVertical(int start, int end) const {
+        return (*SegmentVertical[fVerb])(fPts, start, end);
+    }
+
     SkScalar leftMost(int start, int end) const {
         return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
     }
@@ -2959,6 +2949,21 @@
         return last;
     }
 
+    Span* markAndChaseDoneUnary(int index, int endIndex) {
+        int step = SkSign32(endIndex - index);
+        int min = SkMin32(index, endIndex);
+        markDoneUnary(min);
+        Span* last;
+        Segment* other = this;
+        while ((other = other->nextChase(index, step, min, last))) {
+            if (other->done()) {
+                return NULL;
+            }
+            other->markDoneUnary(min);
+        }
+        return last;
+    }
+
     Span* markAndChaseDoneUnary(const Angle* angle, int winding) {
         int index = angle->start();
         int endIndex = angle->end();
@@ -3213,12 +3218,21 @@
     // note that just because a span has one end that is unsortable, that's
     // not enough to mark it done. The other end may be sortable, allowing the
     // span to be added.
+    // FIXME: if abs(start - end) > 1, mark intermediates as unsortable on both ends
     void markUnsortable(int start, int end) {
         Span* span = &fTs[start];
         if (start < end) {
+#if DEBUG_UNSORTABLE
+            SkDebugf("%s start id=%d [%d] (%1.9g,%1.9g)\n", __FUNCTION__, fID, start,
+                    xAtT(start), yAtT(start));
+#endif
             span->fUnsortableStart = true;
         } else {
             --span;
+#if DEBUG_UNSORTABLE
+            SkDebugf("%s end id=%d [%d] (%1.9g,%1.9g) next:(%1.9g,%1.9g)\n", __FUNCTION__, fID,
+                start - 1, xAtT(start - 1), yAtT(start - 1), xAtT(start), yAtT(start));
+#endif
             span->fUnsortableEnd = true;
         }
         if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
@@ -3287,6 +3301,26 @@
         }
     }
 
+    bool moreHorizontal(int index, int endIndex, bool& unsortable) const {
+        // find bounds
+        Bounds bounds;
+        bounds.setPoint(xyAtT(index));
+        bounds.add(xyAtT(endIndex));
+        SkScalar width = bounds.width();
+        SkScalar height = bounds.height();
+        if (width > height) {
+            if (approximately_negative(width)) {
+                unsortable = true; // edge is too small to resolve meaningfully
+            }
+            return false;
+        } else {
+            if (approximately_negative(height)) {
+                unsortable = true; // edge is too small to resolve meaningfully
+            }
+            return true;
+        }
+    }
+
     // return span if when chasing, two or more radiating spans are not done
     // OPTIMIZATION: ? multiple spans is detected when there is only one valid
     // candidate and the remaining spans have windValue == 0 (canceled by
@@ -3296,6 +3330,18 @@
         return end > 0 && end < fTs.count() - 1;
     }
 
+    bool nextCandidate(int& start, int& end) const {
+        while (fTs[end].fDone) {
+            if (fTs[end].fT == 1) {
+                return false;
+            }
+            ++end;
+        }
+        start = end;
+        end = nextExactSpan(start, 1);
+        return true;
+    }
+
     Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
         int end = nextExactSpan(index, step);
         SkASSERT(end >= 0);
@@ -3306,7 +3352,9 @@
         const Span& endSpan = fTs[end];
         Segment* other = endSpan.fOther;
         index = endSpan.fOtherIndex;
+        SkASSERT(index >= 0);
         int otherEnd = other->nextExactSpan(index, step);
+        SkASSERT(otherEnd >= 0);
         min = SkMin32(index, otherEnd);
         return other;
     }
@@ -3480,6 +3528,10 @@
         return fTs[tIndex].fT;
     }
 
+    double tAtMid(int start, int end, double mid) const {
+        return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
+    }
+
     bool tiny(const Angle* angle) const {
         int start = angle->start();
         int end = angle->end();
@@ -3569,32 +3621,33 @@
         return fVerb;
     }
 
-    int windingAtT(double tHit, int tIndex, bool crossOpp) const {
+    int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar& dx) const {
         if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
             return SK_MinS32;
         }
         int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
         SkASSERT(winding != SK_MinS32);
         int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
-    #if DEBUG_WINDING
-        SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
-                windVal);
+    #if DEBUG_WINDING_AT_T
+        SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
     #endif
         // see if a + change in T results in a +/- change in X (compute x'(T))
-        SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
+        dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
         if (fVerb > SkPath::kLine_Verb && approximately_zero(dx)) {
             dx = fPts[2].fX - fPts[1].fX - dx;
         }
-    #if DEBUG_WINDING
-        SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
+        if (dx == 0) {
+    #if DEBUG_WINDING_AT_T
+            SkDebugf(" dx=0 winding=SK_MinS32\n");
     #endif
-        SkASSERT(dx != 0);
+            return SK_MinS32;
+        }
         if (winding * dx > 0) { // if same signs, result is negative
             winding += dx > 0 ? -windVal : windVal;
-    #if DEBUG_WINDING
-            SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
-    #endif
         }
+    #if DEBUG_WINDING_AT_T
+        SkDebugf(" dx=%c winding=%d\n", dx > 0 ? '+' : '-', winding);
+    #endif
         return winding;
     }
 
@@ -3620,6 +3673,21 @@
         return windValue(index);
     }
 
+    int windValueAt(double t) const {
+        int count = fTs.count();
+        for (int index = 0; index < count; ++index) {
+            if (fTs[index].fT == t) {
+                return fTs[index].fWindValue;
+            }
+        }
+        SkASSERT(0);
+        return 0;
+    }
+
+    SkScalar xAtT(int index) const {
+        return xAtT(&fTs[index]);
+    }
+
     SkScalar xAtT(const Span* span) const {
         return xyAtT(span).fX;
     }
@@ -3641,6 +3709,11 @@
         return span->fPt;
     }
 
+    // used only by right angle winding finding
+    void xyAtT(double mid, SkPoint& pt) const {
+        (*SegmentXYAtT[fVerb])(fPts, mid, &pt);
+    }
+
     SkScalar yAtT(int index) const {
         return yAtT(&fTs[index]);
     }
@@ -3700,7 +3773,7 @@
 #endif
 
 #if DEBUG_CONCIDENT
-    // assert if pair has not already been added
+    // SkASSERT if pair has not already been added
      void debugAddTPair(double t, const Segment& other, double otherT) const {
         for (int i = 0; i < fTs.count(); ++i) {
             if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
@@ -4114,6 +4187,10 @@
         return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
     }
 
+    int addUnsortableT(int segIndex, double newT, Contour* other, int otherIndex, bool start) {
+        return fSegments[segIndex].addUnsortableT(newT, &other->fSegments[otherIndex], start);
+    }
+
     const Bounds& bounds() const {
         return fBounds;
     }
@@ -4127,72 +4204,6 @@
         fContainsIntercepts = true;
     }
 
-    const Segment* crossedSegmentX(const SkPoint& basePt, SkScalar& bestX,
-            int& tIndex, double& hitT, bool opp) {
-        int segmentCount = fSegments.count();
-        const Segment* bestSegment = NULL;
-        for (int test = 0; test < segmentCount; ++test) {
-            Segment* testSegment = &fSegments[test];
-            const SkRect& bounds = testSegment->bounds();
-            if (bounds.fRight <= bestX) {
-                continue;
-            }
-            if (bounds.fLeft >= basePt.fX) {
-                continue;
-            }
-            if (bounds.fTop > basePt.fY) {
-                continue;
-            }
-            if (bounds.fBottom < basePt.fY) {
-                continue;
-            }
-            if (bounds.fTop == bounds.fBottom) {
-                continue;
-            }
-            double testHitT;
-            int testT = testSegment->crossedSpanX(basePt, bestX, testHitT, opp);
-            if (testT >= 0) {
-                bestSegment = testSegment;
-                tIndex = testT;
-                hitT = testHitT;
-            }
-        }
-        return bestSegment;
-    }
-
-    const Segment* crossedSegmentY(const SkPoint& basePt, SkScalar& bestY,
-            int &tIndex, double& hitT, bool opp) {
-        int segmentCount = fSegments.count();
-        const Segment* bestSegment = NULL;
-        for (int test = 0; test < segmentCount; ++test) {
-            Segment* testSegment = &fSegments[test];
-            const SkRect& bounds = testSegment->bounds();
-            if (bounds.fBottom <= bestY) {
-                continue;
-            }
-            if (bounds.fTop >= basePt.fY) {
-                continue;
-            }
-            if (bounds.fLeft > basePt.fX) {
-                continue;
-            }
-            if (bounds.fRight < basePt.fX) {
-                continue;
-            }
-            if (bounds.fLeft == bounds.fRight) {
-                continue;
-            }
-            double testHitT;
-            int testT = testSegment->crossedSpanY(basePt, bestY, testHitT, opp);
-            if (testT >= 0) {
-                bestSegment = testSegment;
-                tIndex = testT;
-                hitT = testHitT;
-            }
-        }
-        return bestSegment;
-    }
-
     bool crosses(const Contour* crosser) const {
         for (int index = 0; index < fCrosses.count(); ++index) {
             if (fCrosses[index] == crosser) {
@@ -4202,6 +4213,10 @@
         return false;
     }
 
+    bool done() const {
+        return fDone;
+    }
+
     const SkPoint& end() const {
         const Segment& segment = fSegments.back();
         return segment.pts()[segment.verb()];
@@ -4221,6 +4236,24 @@
         }
     }
 
+    Segment* nonVerticalSegment(int& start, int& end) {
+        int segmentCount = fSortedSegments.count();
+        SkASSERT(segmentCount > 0);
+        for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
+            Segment* testSegment = fSortedSegments[sortedIndex];
+            if (testSegment->done()) {
+                continue;
+            }
+            start = end = 0;
+            while (testSegment->nextCandidate(start, end)) {
+                if (!testSegment->isVertical(start, end)) {
+                    return testSegment;
+                }
+            }
+        }
+        return NULL;
+    }
+
     bool operand() const {
         return fOperand;
     }
@@ -4228,7 +4261,7 @@
     void reset() {
         fSegments.reset();
         fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
-        fContainsCurves = fContainsIntercepts = false;
+        fContainsCurves = fContainsIntercepts = fDone = false;
     }
 
     void resolveCoincidence(SkTDArray<Contour*>& contourList) {
@@ -4300,7 +4333,117 @@
         }
     }
 
-    const SkTArray<Segment>& segments() {
+    // first pass, add missing T values
+    // second pass, determine winding values of overlaps
+    void addCoincidentPoints() {
+        int count = fCoincidences.count();
+        for (int index = 0; index < count; ++index) {
+            Coincidence& coincidence = fCoincidences[index];
+            SkASSERT(coincidence.fContours[0] == this);
+            int thisIndex = coincidence.fSegments[0];
+            Segment& thisOne = fSegments[thisIndex];
+            Contour* otherContour = coincidence.fContours[1];
+            int otherIndex = coincidence.fSegments[1];
+            Segment& other = otherContour->fSegments[otherIndex];
+            if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
+                // OPTIMIZATION: remove from array
+                continue;
+            }
+        #if DEBUG_CONCIDENT
+            thisOne.debugShowTs();
+            other.debugShowTs();
+        #endif
+            double startT = coincidence.fTs[0][0];
+            double endT = coincidence.fTs[0][1];
+            bool cancelers;
+            if ((cancelers = startT > endT)) {
+                SkTSwap<double>(startT, endT);
+            }
+            SkASSERT(!approximately_negative(endT - startT));
+            double oStartT = coincidence.fTs[1][0];
+            double oEndT = coincidence.fTs[1][1];
+            if (oStartT > oEndT) {
+                SkTSwap<double>(oStartT, oEndT);
+                cancelers ^= true;
+            }
+            SkASSERT(!approximately_negative(oEndT - oStartT));
+            bool opp = fOperand ^ otherContour->fOperand;
+            if (cancelers && !opp) {
+                // make sure startT and endT have t entries
+                if (startT > 0 || oEndT < 1
+                        || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
+                    thisOne.addTPair(startT, other, oEndT, true);
+                }
+                if (oStartT > 0 || endT < 1
+                        || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
+                    other.addTPair(oStartT, thisOne, endT, true);
+                }
+            } else {
+                if (startT > 0 || oStartT > 0
+                        || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
+                    thisOne.addTPair(startT, other, oStartT, true);
+                }
+                if (endT < 1 || oEndT < 1
+                        || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
+                    other.addTPair(oEndT, thisOne, endT, true);
+                }
+            }
+        #if DEBUG_CONCIDENT
+            thisOne.debugShowTs();
+            other.debugShowTs();
+        #endif
+        }
+    }
+
+    void calcCoincidentWinding() {
+        int count = fCoincidences.count();
+        for (int index = 0; index < count; ++index) {
+            Coincidence& coincidence = fCoincidences[index];
+            SkASSERT(coincidence.fContours[0] == this);
+            int thisIndex = coincidence.fSegments[0];
+            Segment& thisOne = fSegments[thisIndex];
+            if (thisOne.done()) {
+                continue;
+            }
+            Contour* otherContour = coincidence.fContours[1];
+            int otherIndex = coincidence.fSegments[1];
+            Segment& other = otherContour->fSegments[otherIndex];
+            if (other.done()) {
+                continue;
+            }
+            double startT = coincidence.fTs[0][0];
+            double endT = coincidence.fTs[0][1];
+            bool cancelers;
+            if ((cancelers = startT > endT)) {
+                SkTSwap<double>(startT, endT);
+            }
+            SkASSERT(!approximately_negative(endT - startT));
+            double oStartT = coincidence.fTs[1][0];
+            double oEndT = coincidence.fTs[1][1];
+            if (oStartT > oEndT) {
+                SkTSwap<double>(oStartT, oEndT);
+                cancelers ^= true;
+            }
+            SkASSERT(!approximately_negative(oEndT - oStartT));
+            bool opp = fOperand ^ otherContour->fOperand;
+            if (cancelers && !opp) {
+                // make sure startT and endT have t entries
+                if (!thisOne.done() && !other.done()) {
+                    thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
+                }
+            } else {
+                if (!thisOne.done() && !other.done()) {
+                    thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
+                }
+            }
+        #if DEBUG_CONCIDENT
+            thisOne.debugShowTs();
+            other.debugShowTs();
+        #endif
+        }
+    }
+
+    SkTArray<Segment>& segments() {
         return fSegments;
     }
 
@@ -4358,53 +4501,11 @@
         }
     }
 
-#if 0 // FIXME: obsolete, remove
-    // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
-    // we need to sort and walk edges in y, but that on the surface opens the
-    // same can of worms as before. But then, this is a rough sort based on
-    // segments' top, and not a true sort, so it could be ameniable to regular
-    // sorting instead of linear searching. Still feel like I'm missing something
-    Segment* topSegment(SkScalar& bestY) {
-        int segmentCount = fSegments.count();
-        SkASSERT(segmentCount > 0);
-        int best = -1;
-        Segment* bestSegment = NULL;
-        while (++best < segmentCount) {
-            Segment* testSegment = &fSegments[best];
-            if (testSegment->done()) {
-                continue;
-            }
-            bestSegment = testSegment;
-            break;
-        }
-        if (!bestSegment) {
-            return NULL;
-        }
-        SkScalar bestTop = bestSegment->activeTop();
-        for (int test = best + 1; test < segmentCount; ++test) {
-            Segment* testSegment = &fSegments[test];
-            if (testSegment->done()) {
-                continue;
-            }
-            if (testSegment->bounds().fTop > bestTop) {
-                continue;
-            }
-            SkScalar testTop = testSegment->activeTop();
-            if (bestTop > testTop) {
-                bestTop = testTop;
-                bestSegment = testSegment;
-            }
-        }
-        bestY = bestTop;
-        return bestSegment;
-    }
-#endif
-
-    Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
+    void topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY, Segment*& topStart) {
         int segmentCount = fSortedSegments.count();
         SkASSERT(segmentCount > 0);
-        Segment* bestSegment = NULL;
         int sortedIndex = fFirstSorted;
+        fDone = true; // may be cleared below
         for ( ; sortedIndex < segmentCount; ++sortedIndex) {
             Segment* testSegment = fSortedSegments[sortedIndex];
             if (testSegment->done()) {
@@ -4413,24 +4514,26 @@
                 }
                 continue;
             }
+            fDone = false;
             SkPoint testXY;
             testSegment->activeLeftTop(testXY);
-            if (testXY.fY < topLeft.fY) {
-                continue;
+            if (topStart) {
+                if (testXY.fY < topLeft.fY) {
+                    continue;
+                }
+                if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
+                    continue;
+                }
+                if (bestXY.fY < testXY.fY) {
+                    continue;
+                }
+                if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
+                    continue;
+                }
             }
-            if (testXY.fY == topLeft.fY && testXY.fX <= topLeft.fX) {
-                continue;
-            }
-            if (bestXY.fY < testXY.fY) {
-                continue;
-            }
-            if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
-                continue;
-            }
-            bestSegment = testSegment;
+            topStart = testSegment;
             bestXY = testXY;
         }
-        return bestSegment;
     }
 
     Segment* undoneSegment(int& start, int& end) {
@@ -4546,6 +4649,7 @@
     Bounds fBounds;
     bool fContainsIntercepts;
     bool fContainsCurves;
+    bool fDone;
     bool fOperand; // true for the second argument to a binary operator
     bool fXor;
     bool fOppXor;
@@ -4769,6 +4873,10 @@
         return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
     }
 
+    int addUnsortableT(double newT, const Work& other, bool start) {
+        return fContour->addUnsortableT(fIndex, newT, other.fContour, other.fIndex, start);
+    }
+
     bool advance() {
         return ++fIndex < fLast;
     }
@@ -4781,9 +4889,11 @@
         return fContour->segments()[fIndex].bounds();
     }
 
+#if !APPROXIMATE_CUBICS
     const SkPoint* cubic() const {
         return fCubic;
     }
+#endif
 
     void init(Contour* contour) {
         fContour = contour;
@@ -4804,6 +4914,7 @@
         return bounds().fLeft;
     }
 
+#if !APPROXIMATE_CUBICS
     void promoteToCubic() {
         fCubic[0] = pts()[0];
         fCubic[2] = pts()[1];
@@ -4813,6 +4924,7 @@
         fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
         fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
     }
+#endif
 
     const SkPoint* pts() const {
         return fContour->segments()[fIndex].pts();
@@ -4872,7 +4984,9 @@
 
 protected:
     Contour* fContour;
+#if !APPROXIMATE_CUBICS
     SkPoint fCubic[4];
+#endif
     int fIndex;
     int fLast;
 };
@@ -4939,6 +5053,7 @@
     SkDebugf("\n");
 }
 
+// FIXME: show more than two intersection points
 static void debugShowQuadIntersection(int pts, const Work& wt,
         const Work& wn, const double wtTs[2], const double wnTs[2]) {
     if (!pts) {
@@ -4970,6 +5085,105 @@
     }
     SkDebugf("\n");
 }
+
+static void debugShowCubicLineIntersection(int pts, const Work& wt,
+        const Work& wn, const double wtTs[2], const double wnTs[2]) {
+    if (!pts) {
+        SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+                " (%1.9g,%1.9g %1.9g,%1.9g)\n",
+                __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
+                wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
+                wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
+        return;
+    }
+    SkPoint wtOutPt, wnOutPt;
+    CubicXYAtT(wt.pts(), wtTs[0], &wtOutPt);
+    LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
+    SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            __FUNCTION__,
+            wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
+            wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
+            wtOutPt.fX, wtOutPt.fY);
+    if (pts == 2) {
+        CubicXYAtT(wt.pts(), wtTs[1], &wtOutPt);
+        SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
+    }
+    SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            wtTs[0], wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
+            wnOutPt.fX, wnOutPt.fY);
+    if (pts == 2) {
+        LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
+        SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
+    }
+    SkDebugf("\n");
+}
+
+// FIXME: show more than two intersection points
+static void debugShowCubicQuadIntersection(int pts, const Work& wt,
+        const Work& wn, const double wtTs[2], const double wnTs[2]) {
+    if (!pts) {
+        SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+                " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
+                __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
+                wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
+                wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
+                wn.pts()[2].fX, wn.pts()[2].fY );
+        return;
+    }
+    SkPoint wtOutPt, wnOutPt;
+    CubicXYAtT(wt.pts(), wtTs[0], &wtOutPt);
+    QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
+    SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            __FUNCTION__,
+            wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
+            wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
+            wtOutPt.fX, wtOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
+    }
+    SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
+            wn.pts()[2].fX, wn.pts()[2].fY,
+            wnOutPt.fX, wnOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
+    }
+    SkDebugf("\n");
+}
+
+// FIXME: show more than two intersection points
+static void debugShowCubicIntersection(int pts, const Work& wt,
+        const Work& wn, const double wtTs[2], const double wnTs[2]) {
+    if (!pts) {
+        SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+                " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
+                __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
+                wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
+                wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
+                wn.pts()[2].fX, wn.pts()[2].fY, wn.pts()[3].fX, wn.pts()[3].fY );
+        return;
+    }
+    SkPoint wtOutPt, wnOutPt;
+    CubicXYAtT(wt.pts(), wtTs[0], &wtOutPt);
+    CubicXYAtT(wn.pts(), wnTs[0], &wnOutPt);
+    SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            __FUNCTION__,
+            wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY, wt.pts()[1].fX, wt.pts()[1].fY,
+            wt.pts()[2].fX, wt.pts()[2].fY, wt.pts()[3].fX, wt.pts()[3].fY,
+            wtOutPt.fX, wtOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
+    }
+    SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
+            wn.pts()[2].fX, wn.pts()[2].fY, wn.pts()[3].fX, wn.pts()[3].fY,
+            wnOutPt.fX, wnOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
+    }
+    SkDebugf("\n");
+}
+
 #else
 static void debugShowLineIntersection(int , const Work& ,
         const Work& , const double [2], const double [2]) {
@@ -4982,6 +5196,18 @@
 static void debugShowQuadIntersection(int , const Work& ,
         const Work& , const double [2], const double [2]) {
 }
+
+static void debugShowCubicLineIntersection(int , const Work& ,
+        const Work& , const double [2], const double [2]) {
+}
+
+static void debugShowCubicQuadIntersection(int , const Work& ,
+        const Work& , const double [2], const double [2]) {
+}
+
+static void debugShowCubicIntersection(int , const Work& ,
+        const Work& , const double [2], const double [2]) {
+}
 #endif
 
 static bool addIntersectTs(Contour* test, Contour* next) {
@@ -5031,6 +5257,7 @@
                         case Work::kCubic_Segment: {
                             pts = HCubicIntersect(wn.pts(), wt.left(),
                                     wt.right(), wt.y(), wt.xFlipped(), ts);
+                            debugShowCubicLineIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
                             break;
                         }
                         default:
@@ -5057,6 +5284,7 @@
                         case Work::kCubic_Segment: {
                             pts = VCubicIntersect(wn.pts(), wt.top(),
                                     wt.bottom(), wt.x(), wt.yFlipped(), ts);
+                            debugShowCubicLineIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
                             break;
                         }
                         default:
@@ -5093,6 +5321,7 @@
                         case Work::kCubic_Segment: {
                             swap = true;
                             pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
+                            debugShowCubicLineIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
                             break;
                         }
                         default:
@@ -5122,8 +5351,15 @@
                             break;
                         }
                         case Work::kCubic_Segment: {
+                    #if APPROXIMATE_CUBICS
+                            swap = true;
+                            pts = CubicQuadIntersect(wn.pts(), wt.pts(), ts);
+                            debugShowCubicQuadIntersection(pts, wn, wt, ts.fT[0], ts.fT[1]);
+                    #else
                             wt.promoteToCubic();
                             pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
+                            debugShowCubicIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
+                    #endif
                             break;
                         }
                         default:
@@ -5139,18 +5375,27 @@
                         case Work::kVerticalLine_Segment:
                             pts = VCubicIntersect(wt.pts(), wn.top(),
                                     wn.bottom(), wn.x(), wn.yFlipped(), ts);
+                            debugShowCubicLineIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
                             break;
                         case Work::kLine_Segment: {
                             pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
+                            debugShowCubicLineIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
                             break;
                         }
                         case Work::kQuad_Segment: {
+                    #if APPROXIMATE_CUBICS
+                            pts = CubicQuadIntersect(wt.pts(), wn.pts(), ts);
+                            debugShowCubicQuadIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
+                    #else
                             wn.promoteToCubic();
                             pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
+                            debugShowCubicIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
+                    #endif
                             break;
                         }
                         case Work::kCubic_Segment: {
                             pts = CubicIntersect(wt.pts(), wn.pts(), ts);
+                            debugShowCubicIntersection(pts, wt, wn, ts.fT[0], ts.fT[1]);
                             break;
                         }
                         default:
@@ -5166,6 +5411,19 @@
                 foundCommonContour = true;
             }
             // in addition to recording T values, record matching segment
+            if (ts.unsortable()) {
+                bool start = true;
+                for (int pt = 0; pt < ts.used(); ++pt) {
+                    // FIXME: if unsortable, the other points to the original. This logic is
+                    // untested downstream.
+                    int testTAt = wt.addUnsortableT(ts.fT[swap][pt], wt, start);
+                    wt.addOtherT(testTAt, ts.fT[swap][pt], testTAt);
+                    testTAt = wn.addUnsortableT(ts.fT[!swap][pt], wn, start ^ ts.fFlip);
+                    wn.addOtherT(testTAt, ts.fT[!swap][pt], testTAt);
+                    start ^= true;
+                }
+                continue;
+            }
             if (pts == 2) {
                 if (wn.segmentType() <= Work::kLine_Segment
                         && wt.segmentType() <= Work::kLine_Segment) {
@@ -5197,55 +5455,38 @@
 // see if coincidence is formed by clipping non-concident segments
 static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
     int contourCount = contourList.count();
+#if ONE_PASS_COINCIDENCE_CHECK
     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
         Contour* contour = contourList[cIndex];
         contour->resolveCoincidence(contourList);
     }
+#else
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        Contour* contour = contourList[cIndex];
+        contour->addCoincidentPoints();
+    }
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        Contour* contour = contourList[cIndex];
+        contour->calcCoincidentWinding();
+    }
+#endif
     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
         Contour* contour = contourList[cIndex];
         contour->findTooCloseToCall();
     }
 }
 
-static int contourRangeCheckX(SkTDArray<Contour*>& contourList, double mid,
-        const Segment* current, int index, int endIndex, bool opp) {
-    const SkPoint& basePt = current->xyAtT(endIndex);
-    int contourCount = contourList.count();
-    SkScalar bestX = SK_ScalarMin;
-    const Segment* test = NULL;
-    int tIndex;
-    double tHit;
-    bool crossOpp;
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        Contour* contour = contourList[cTest];
-        bool testOpp = contour->operand() ^ current->operand() ^ opp;
-        if (basePt.fX < contour->bounds().fLeft) {
-            continue;
-        }
-        if (bestX > contour->bounds().fRight) {
-            continue;
-        }
-        const Segment* next = contour->crossedSegmentX(basePt, bestX, tIndex, tHit, testOpp);
-        if (next) {
-            test = next;
-            crossOpp = testOpp;
-        }
-    }
-    if (!test) {
-        return 0;
-    }
-    return test->windingAtT(tHit, tIndex, crossOpp);
-}
-
-static int contourRangeCheckY(SkTDArray<Contour*>& contourList, double mid,
-        const Segment* current, int index, int endIndex, bool opp) {
-    const SkPoint& basePt = current->xyAtT(endIndex);
+static int contourRangeCheckY(SkTDArray<Contour*>& contourList,  Segment*& current, int& index,
+        int& endIndex, double& bestHit, SkScalar& bestDx, bool& tryAgain, double& mid, bool opp) {
+    SkPoint basePt;
+    double tAtMid = current->tAtMid(index, endIndex, mid);
+    current->xyAtT(tAtMid, basePt);
     int contourCount = contourList.count();
     SkScalar bestY = SK_ScalarMin;
-    const Segment* test = NULL;
-    int tIndex;
-    double tHit;
-    bool crossOpp;
+    Segment* bestSeg = NULL;
+    int bestTIndex;
+    bool bestOpp;
+    bool hitSomething = false;
     for (int cTest = 0; cTest < contourCount; ++cTest) {
         Contour* contour = contourList[cTest];
         bool testOpp = contour->operand() ^ current->operand() ^ opp;
@@ -5255,181 +5496,69 @@
         if (bestY > contour->bounds().fBottom) {
             continue;
         }
-        const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
-        if (next) {
-            test = next;
-            crossOpp = testOpp;
-        }
-    }
-    if (!test) {
-        return 0;
-    }
-    return test->windingAtT(tHit, tIndex, crossOpp);
-}
-
-// project a ray from the top of the contour up and see if it hits anything
-// note: when we compute line intersections, we keep track of whether
-// two contours touch, so we need only look at contours not touching this one.
-// OPTIMIZATION: sort contourList vertically to avoid linear walk
-static int innerContourCheck(SkTDArray<Contour*>& contourList,
-        const Segment* current, int index, int endIndex, bool opp) {
-    const SkPoint& basePt = current->xyAtT(endIndex);
-    int contourCount = contourList.count();
-    SkScalar bestY = SK_ScalarMin;
-    const Segment* test = NULL;
-    int tIndex;
-    double tHit;
-    bool crossOpp;
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        Contour* contour = contourList[cTest];
-        bool testOpp = contour->operand() ^ current->operand() ^ opp;
-        if (basePt.fY < contour->bounds().fTop) {
-            continue;
-        }
-        if (bestY > contour->bounds().fBottom) {
-            continue;
-        }
-        const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
-        if (next) {
-            test = next;
-            crossOpp = testOpp;
-        }
-    }
-    if (!test) {
-        return 0;
-    }
-    int winding, windValue;
-    // If the ray hit the end of a span, we need to construct the wheel of
-    // angles to find the span closest to the ray -- even if there are just
-    // two spokes on the wheel.
-    const Angle* angle = NULL;
-    if (approximately_zero(tHit - test->t(tIndex))) {
-        SkTDArray<Angle> angles;
-        int end = test->nextSpan(tIndex, 1);
-        if (end < 0) {
-            end = test->nextSpan(tIndex, -1);
-        }
-        test->addTwoAngles(end, tIndex, angles);
-        SkASSERT(angles.count() > 0);
-        if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
-#if DEBUG_SORT
-            SkDebugf("%s early return\n", __FUNCTION__);
-#endif
-            return SK_MinS32;
-        }
-        test->buildAngles(tIndex, angles, false);
-        SkTDArray<Angle*> sorted;
-        // OPTIMIZATION: call a sort that, if base point is the leftmost,
-        // returns the first counterclockwise hour before 6 o'clock,
-        // or if the base point is rightmost, returns the first clockwise
-        // hour after 6 o'clock
-        bool sortable = Segment::SortAngles(angles, sorted);
-        if (!sortable) {
-            return SK_MinS32;
-        }
-#if DEBUG_SORT
-        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
-#endif
-        // walk the sorted angle fan to find the lowest angle
-        // above the base point. Currently, the first angle in the sorted array
-        // is 12 noon or an earlier hour (the next counterclockwise)
-        int count = sorted.count();
-        int left = -1;
-        int mid = -1;
-        int right = -1;
-        bool baseMatches = test->yAtT(tIndex) == basePt.fY;
-        for (int index = 0; index < count; ++index) {
-            angle = sorted[index];
-            if (angle->unsortable()) {
+        int segmentCount = contour->segments().count();
+        for (int test = 0; test < segmentCount; ++test) {
+            Segment* testSeg = &contour->segments()[test];
+            SkScalar testY = bestY;
+            double testHit;
+            int testTIndex = testSeg->crossedSpanY(basePt, testY, testHit, hitSomething, tAtMid,
+                    testOpp, testSeg == current);
+            if (testTIndex < 0) {
+                if (testTIndex == SK_MinS32) {
+                    hitSomething = true;
+                    bestSeg = NULL;
+                    goto abortContours; // vertical encountered, return and try different point
+                }
                 continue;
             }
-            if (baseMatches && angle->isHorizontal()) {
-                continue;
-            }
-            double indexDx = angle->dx();
-            test = angle->segment();
-            if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
-                const SkPoint* pts = test->pts();
-                indexDx = pts[2].fX - pts[1].fX - indexDx;
-            }
-            if (indexDx < 0) {
-                left = index;
-            } else if (indexDx > 0) {
-                right = index;
-                int previous = index - 1;
-                if (previous < 0) {
-                    previous = count - 1;
-                }
-                const Angle* prev = sorted[previous];
-                if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
-#if DEBUG_SORT
-                    SkDebugf("%s use prev\n", __FUNCTION__);
+            if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
+                double baseT = current->t(index);
+                double endT = current->t(endIndex);
+                double newMid = (testHit - baseT) / (endT - baseT);
+#if DEBUG_WINDING
+                SkPoint midXY, newXY;
+                double midT = current->tAtMid(index, endIndex, mid);
+                current->xyAtT(midT, midXY);
+                double newMidT = current->tAtMid(index, endIndex, newMid);
+                current->xyAtT(newMidT, newXY);
+                SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
+                        " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
+                        current->debugID(), mid, newMid,
+                        baseT, current->xAtT(index), current->yAtT(index),
+                        baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
+                        baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
+                        endT, current->xAtT(endIndex), current->yAtT(endIndex));
 #endif
-                    right = previous;
-                }
-                break;
-            } else {
-                mid = index;
-            }
-        }
-        if ((left < 0 || right < 0) && mid >= 0) {
-            angle = sorted[mid];
-            Segment* midSeg = angle->segment();
-            int end = angle->end();
-            if (midSeg->unsortable(end)) {
+                mid = newMid * 2; // calling loop with divide by 2 before continuing
                 return SK_MinS32;
             }
+            bestSeg = testSeg;
+            bestHit = testHit;
+            bestOpp = testOpp;
+            bestTIndex = testTIndex;
+            bestY = testY;
         }
-        if (left < 0 && right < 0) {
-            left = mid;
-        }
-        if (left < 0 && right < 0) {
-            SkASSERT(0);
-            return SK_MinS32; // unsortable
-        }
-        if (left < 0) {
-            left = right;
-        } else if (left >= 0 && mid >= 0 && right >= 0
-                && sorted[mid]->sign() == sorted[right]->sign()) {
-            left = right;
-        }
-        angle = sorted[left];
-        test = angle->segment();
-        winding = crossOpp ? test->oppSum(angle) : test->windSum(angle);
-        SkASSERT(winding != SK_MinS32);
-        windValue = crossOpp ? test->oppValue(angle) : test->windValue(angle);
-#if DEBUG_WINDING
-        SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
-                windValue, angle->sign());
-#endif
+    }
+abortContours:
+    int result;
+    if (!bestSeg) {
+        result = hitSomething ? SK_MinS32 : 0;
     } else {
-        winding = crossOpp ? test->oppSum(tIndex) : test->windSum(tIndex);
-        if (winding == SK_MinS32) {
-            return SK_MinS32; // unsortable
+        if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
+            current = bestSeg;
+            index = bestTIndex;
+            endIndex = bestSeg->nextSpan(bestTIndex, 1);
+            SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
+            tryAgain = true;
+            return 0;
         }
-        windValue = crossOpp ? test->oppValue(tIndex) : test->windValue(tIndex);
-#if DEBUG_WINDING
-        SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
-                windValue);
-#endif
+        result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, bestDx);
+        SkASSERT(bestDx);
     }
-    // see if a + change in T results in a +/- change in X (compute x'(T))
-    SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
-    if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
-        const SkPoint* pts = test->pts();
-        dx = pts[2].fX - pts[1].fX - dx;
-    }
-#if DEBUG_WINDING
-    SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
-#endif
-    SkASSERT(dx != 0);
-    if (winding * dx > 0) { // if same signs, result is negative
-        winding += dx > 0 ? -windValue : windValue;
-#if DEBUG_WINDING
-        SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
-#endif
-    }
-    return winding;
+    double baseT = current->t(index);
+    double endT = current->t(endIndex);
+    bestHit = baseT + mid * (endT - baseT);
+    return result;
 }
 
 static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
@@ -5445,7 +5574,7 @@
     return NULL;
 }
 
-
+#define OLD_FIND_CHASE 1
 
 static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
     while (chase.count()) {
@@ -5472,6 +5601,7 @@
         }
         SkTDArray<Angle*> sorted;
         bool sortable = Segment::SortAngles(angles, sorted);
+        int angleCount = sorted.count();
 #if DEBUG_SORT
         sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
 #endif
@@ -5481,6 +5611,7 @@
         // find first angle, initialize winding to computed fWindSum
         int firstIndex = -1;
         const Angle* angle;
+#if OLD_FIND_CHASE
         int winding;
         do {
             angle = sorted[++firstIndex];
@@ -5505,10 +5636,22 @@
         // advance to first undone angle, then return it and winding
         // (to set whether edges are active or not)
         int nextIndex = firstIndex + 1;
-        int angleCount = sorted.count();
         int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
         angle = sorted[firstIndex];
         winding -= angle->segment()->spanSign(angle);
+#else
+        do {
+            angle = sorted[++firstIndex];
+            segment = angle->segment();
+        } while (segment->windSum(angle) == SK_MinS32);
+    #if DEBUG_SORT
+        segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
+    #endif
+        int sumWinding = segment->updateWindingReverse(angle);
+        int nextIndex = firstIndex + 1;
+        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+        Segment* first = NULL;
+#endif
         do {
             SkASSERT(nextIndex != firstIndex);
             if (nextIndex == angleCount) {
@@ -5516,6 +5659,7 @@
             }
             angle = sorted[nextIndex];
             segment = angle->segment();
+#if OLD_FIND_CHASE
             int maxWinding = winding;
             winding -= segment->spanSign(angle);
     #if DEBUG_SORT
@@ -5528,16 +5672,30 @@
             const Span& nextSpan = segment->span(lesser);
             if (!nextSpan.fDone) {
 #if 1
-            // FIXME: this be wrong. assign startWinding if edge is in
+            // FIXME: this be wrong? assign startWinding if edge is in
             // same direction. If the direction is opposite, winding to
             // assign is flipped sign or +/- 1?
                 if (useInnerWinding(maxWinding, winding)) {
                     maxWinding = winding;
                 }
-                segment->markWinding(lesser, maxWinding);
+                segment->markAndChaseWinding(angle, maxWinding, 0);
 #endif
                 break;
             }
+#else
+            int start = angle->start();
+            int end = angle->end();
+            int maxWinding;
+            segment->setUpWinding(start, end, maxWinding, sumWinding);
+            if (!segment->done(angle)) {
+                if (!first) {
+                    first = segment;
+                    tIndex = start;
+                    endIndex = end;
+                }
+                (void) segment->markAngle(maxWinding, sumWinding, true, angle);
+            }
+#endif
         } while (++nextIndex != lastIndex);
    #if TRY_ROTATE
         *chase.insert(0) = span;
@@ -5561,31 +5719,31 @@
 }
 #endif
 
-static bool windingIsActive(int winding, int spanWinding) {
-    // FIXME: !spanWinding test must be superflorous, true?
-    return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
-            && (!winding || !spanWinding || winding == -spanWinding);
-}
-
 static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
-        int& endIndex, SkPoint& topLeft, bool& unsortable, bool onlySortable) {
+        int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool onlySortable) {
     Segment* result;
     do {
         SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
         int contourCount = contourList.count();
         Segment* topStart = NULL;
+        done = true;
         for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
             Contour* contour = contourList[cIndex];
+            if (contour->done()) {
+                continue;
+            }
             const Bounds& bounds = contour->bounds();
             if (bounds.fBottom < topLeft.fY) {
+                done = false;
                 continue;
             }
             if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
+                done = false;
                 continue;
             }
-            Segment* test = contour->topSortableSegment(topLeft, bestXY);
-            if (test) {
-                topStart = test;
+            contour->topSortableSegment(topLeft, bestXY, topStart);
+            if (!contour->done()) {
+                done = false;
             }
         }
         if (!topStart) {
@@ -5597,35 +5755,15 @@
     return result;
 }
 
-static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
-    int lesser = SkMin32(index, endIndex);
-    spanWinding = current->spanSign(index, endIndex);
-    int winding = current->windSum(lesser);
-    bool inner = useInnerWinding(winding - spanWinding, winding);
-#if DEBUG_WINDING
-    SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
-            " inner=%d result=%d\n",
-            __FUNCTION__, current->debugID(), current->t(lesser),
-            spanWinding, winding, SkSign32(index - endIndex),
-            useInnerWinding(winding - spanWinding, winding),
-            inner ? winding - spanWinding : winding);
-#endif
-    if (inner) {
-        winding -= spanWinding;
-    }
-    return winding;
-}
-
-typedef int (*RangeChecker)(SkTDArray<Contour*>& contourList, double mid,
-        const Segment* current, int index, int endIndex, bool opp);
-
-static int rightAngleWinding(RangeChecker rangeChecker, SkTDArray<Contour*>& contourList,
-        Segment* current, int index, int endIndex, bool opp) {
+static int rightAngleWinding(SkTDArray<Contour*>& contourList,
+        Segment*& current, int& index, int& endIndex, double& tHit, SkScalar& hitDx, bool& tryAgain,
+        bool opp) {
     double test = 0.9;
     int contourWinding;
     do {
-        contourWinding = (*rangeChecker)(contourList, test, current, index, endIndex, opp);
-        if (contourWinding != SK_MinS32) {
+        contourWinding = contourRangeCheckY(contourList, current, index, endIndex, tHit, hitDx,
+                tryAgain, test, opp);
+        if (contourWinding != SK_MinS32 || tryAgain) {
             return contourWinding;
         }
         test /= 2;
@@ -5634,249 +5772,91 @@
     return contourWinding;
 }
 
-static Segment* tryRightAngleRay(SkTDArray<Contour*>& contourList, int& index,
-        int& endIndex, SkPoint& topLeft, bool& unsortable, RangeChecker& rangeChecker) {
-    // the simple upward projection of the unresolved points hit unsortable angles
-    // shoot rays at right angles to the segment to find its winding, ignoring angle cases
-    topLeft.fX = topLeft.fY = SK_ScalarMin;
-    Segment* current;
-    do {
-        current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, false);
-        SkASSERT(current); // FIXME: return to caller that path cannot be simplified (yet)
-        // find bounds
-        Bounds bounds;
-        bounds.setPoint(current->xyAtT(index));
-        bounds.add(current->xyAtT(endIndex));
-        SkScalar width = bounds.width();
-        SkScalar height = bounds.height();
-        if (width > height) {
-            if (approximately_negative(width)) {
-                continue; // edge is too small to resolve meaningfully
-            }
-            rangeChecker = contourRangeCheckY;
-        } else {
-            if (approximately_negative(height)) {
-                continue; // edge is too small to resolve meaningfully
-            }
-            rangeChecker = contourRangeCheckX;
-        }
-    } while (!current);
-    return current;
-}
-
-static Segment* findSortableTopOld(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
-        int& endIndex, SkPoint& topLeft, int& contourWinding, bool& unsortable) {
-    Segment* current;
-    do {
-        current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, true);
-        if (!current) {
-            break;
-        }
-        if (firstContour) {
-            contourWinding = 0;
-            firstContour = false;
-            break;
-        }
-        int sumWinding = current->windSum(SkMin32(index, endIndex));
-        // FIXME: don't I have to adjust windSum to get contourWinding?
-        if (sumWinding == SK_MinS32) {
-            sumWinding = current->computeSum(index, endIndex, false);
-        }
-        if (sumWinding == SK_MinS32) {
-            contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
-        } else {
-            contourWinding = sumWinding;
-            int spanWinding = current->spanSign(index, endIndex);
-            bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
-            if (inner) {
-                contourWinding -= spanWinding;
-            }
-#if DEBUG_WINDING
-            SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
-                    __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
-                    inner, contourWinding);
-#endif
-        }
-    } while (contourWinding == SK_MinS32);
-    if (contourWinding != SK_MinS32) {
-#if DEBUG_WINDING
-        SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
-#endif
-        return current;
+static void skipVertical(SkTDArray<Contour*>& contourList,
+        Segment*& current, int& index, int& endIndex) {
+    if (!current->isVertical(index, endIndex)) {
+        return;
     }
-    RangeChecker rangeChecker = NULL;
-    current = tryRightAngleRay(contourList, index, endIndex, topLeft, unsortable, rangeChecker);
-    contourWinding = rightAngleWinding(rangeChecker, contourList, current, index, endIndex, false);
-    return current;
-}
-
-// Each segment may have an inside or an outside. Segments contained within
-// winding may have insides on either side, and form a contour that should be
-// ignored. Segments that are coincident with opposing direction segments may
-// have outsides on either side, and should also disappear.
-// 'Normal' segments will have one inside and one outside. Subsequent connections
-// when winding should follow the intersection direction. If more than one edge
-// is an option, choose first edge that continues the inside.
-    // since we start with leftmost top edge, we'll traverse through a
-    // smaller angle counterclockwise to get to the next edge.
-// returns true if all edges were processed
-static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
-    bool firstContour = true;
-    bool unsortable = false;
-    bool topUnsortable = false;
-    bool firstRetry = false;
-    bool closable = true;
-    SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
-    do {
-        int index, endIndex;
-        // iterates while top is unsortable
-        int contourWinding;
-        Segment* current = findSortableTopOld(contourList, firstContour, index, endIndex, topLeft,
-                contourWinding, topUnsortable);
-        if (!current) {
-            if (topUnsortable) {
-                topUnsortable = false;
-                SkASSERT(!firstRetry);
-                firstRetry = true;
-                topLeft.fX = topLeft.fY = SK_ScalarMin;
-                continue;
-            }
-            break;
-        }
-        int winding = contourWinding;
-        int spanWinding = current->spanSign(index, endIndex);
-        // FIXME: needs work. While it works in limited situations, it does
-        // not always compute winding correctly. Active should be removed and instead
-        // the initial winding should be correctly passed in so that if the
-        // inner contour is wound the same way, it never finds an accumulated
-        // winding of zero. Inside 'find next', we need to look for transitions
-        // other than zero when resolving sorted angles.
-        SkTDArray<Span*> chaseArray;
-        do {
-            bool active = windingIsActive(winding, spanWinding);
-        #if DEBUG_WINDING
-            SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
-                    __FUNCTION__, active ? "true" : "false",
-                    winding, spanWinding);
-        #endif
-            do {
-                SkASSERT(unsortable || !current->done());
-                int nextStart = index;
-                int nextEnd = endIndex;
-                Segment* next = current->findNextWinding(chaseArray, active,
-                        nextStart, nextEnd, winding, spanWinding, unsortable);
-                if (!next) {
-                    if (active && !unsortable && simple.hasMove()
-                            && current->verb() != SkPath::kLine_Verb
-                            && !simple.isClosed()) {
-                        current->addCurveTo(index, endIndex, simple, true);
-                        SkASSERT(simple.isClosed());
-                    }
-                    break;
-                }
-        #if DEBUG_FLOW
-                SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
-                        current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
-                        current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
-        #endif
-                current->addCurveTo(index, endIndex, simple, active);
-                current = next;
-                index = nextStart;
-                endIndex = nextEnd;
-            } while (!simple.isClosed()
-                    && ((active && !unsortable) || !current->done()));
-            if (active) {
-                if (!simple.isClosed()) {
-                    SkASSERT(unsortable);
-                    int min = SkMin32(index, endIndex);
-                    if (!current->done(min)) {
-                        current->addCurveTo(index, endIndex, simple, true);
-                        current->markDone(min, winding ? winding : spanWinding);
-                    }
-                    closable = false;
-                }
-                simple.close();
-            }
-            current = findChase(chaseArray, index, endIndex);
-        #if DEBUG_ACTIVE_SPANS
-            debugShowActiveSpans(contourList);
-        #endif
-            if (!current) {
-                break;
-            }
-            winding = updateWindings(current, index, endIndex, spanWinding);
-        } while (true);
-    } while (true);
-    return closable;
-}
-
-static Segment* findSortableTopNew(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
-        int& endIndex, SkPoint& topLeft, bool& unsortable) {
-    Segment* current;
-    bool first = true;
-    int contourWinding, oppContourWinding;
-    do {
-        current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, true);
-        if (!current) {
-            if (first) {
-                return NULL;
-            }
-            break;
-        }
-        first = false;
-        if (firstContour) {
-            current->initWinding(index, endIndex, 0, 0);
-            firstContour = false;
-            return current;
-        }
-        int minIndex = SkMin32(index, endIndex);
-        int sumWinding = current->windSum(minIndex);
-        if (sumWinding == SK_MinS32) {
-            sumWinding = current->computeSum(index, endIndex, true);
-            if (sumWinding != SK_MinS32) {
-                return current;
-            }
-        }
-        contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
-        if (contourWinding == SK_MinS32) {
+    int contourCount = contourList.count();
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        Contour* contour = contourList[cIndex];
+        if (contour->done()) {
             continue;
         }
-        oppContourWinding = innerContourCheck(contourList, current, index, endIndex, true);
-        if (oppContourWinding != SK_MinS32) {
+        current = contour->nonVerticalSegment(index, endIndex);
+        if (current) {
+            return;
+        }
+    }
+}
+
+static Segment* findSortableTop(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
+        int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool binary) {
+    Segment* current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, done,
+            true);
+    if (!current) {
+        return NULL;
+    }
+    if (firstContour) {
+        current->initWinding(index, endIndex);
+        firstContour = false;
+        return current;
+    }
+    int minIndex = SkMin32(index, endIndex);
+    int sumWinding = current->windSum(minIndex);
+    if (sumWinding != SK_MinS32) {
+        return current;
+    }
+    sumWinding = current->computeSum(index, endIndex, binary);
+    if (sumWinding != SK_MinS32) {
+        return current;
+    }
+    int contourWinding;
+    int oppContourWinding = 0;
+    // the simple upward projection of the unresolved points hit unsortable angles
+    // shoot rays at right angles to the segment to find its winding, ignoring angle cases
+    bool tryAgain;
+    double tHit;
+    SkScalar hitDx = 0;
+    SkScalar hitOppDx = 0;
+    do {
+        // if current is vertical, find another candidate which is not
+        // if only remaining candidates are vertical, then they can be marked done
+        SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
+        skipVertical(contourList, current, index, endIndex);
+        SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
+        tryAgain = false;
+        contourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitDx,
+                tryAgain, false);
+        if (tryAgain) {
+            continue;
+        }
+        if (!binary) {
             break;
         }
-        current->initWinding(index, endIndex, contourWinding, oppContourWinding);
-        return current;
-    } while (true);
-    if (!current) {
-        // the simple upward projection of the unresolved points hit unsortable angles
-        // shoot rays at right angles to the segment to find its winding, ignoring angle cases
-        int (*checker)(SkTDArray<Contour*>& contourList, double mid,
-                const Segment* current, int index, int endIndex, bool opp);
-        current = tryRightAngleRay(contourList, index, endIndex, topLeft, unsortable, checker);
-        contourWinding = rightAngleWinding(checker, contourList, current, index, endIndex, false);
-        oppContourWinding = rightAngleWinding(checker, contourList, current, index, endIndex, true);
-    }
-    current->initWinding(index, endIndex, contourWinding, oppContourWinding);
+        oppContourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitOppDx,
+                tryAgain, true);
+    } while (tryAgain);
+
+    current->initWinding(index, endIndex, tHit, contourWinding, hitDx, oppContourWinding, hitOppDx);
     return current;
 }
 
 // rewrite that abandons keeping local track of winding
-static bool bridgeWindingX(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
+static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
     bool firstContour = true;
     bool unsortable = false;
     bool topUnsortable = false;
-    bool firstRetry = false;
     SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
         int index, endIndex;
-        Segment* current = findSortableTopNew(contourList, firstContour, index, endIndex, topLeft,
-                topUnsortable);
+        bool topDone;
+        Segment* current = findSortableTop(contourList, firstContour, index, endIndex, topLeft,
+                topUnsortable, topDone, false);
         if (!current) {
-            if (topUnsortable) {
+            if (topUnsortable || !topDone) {
                 topUnsortable = false;
-                SkASSERT(!firstRetry);
-                firstRetry = true;
+                SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
                 topLeft.fX = topLeft.fY = SK_ScalarMin;
                 continue;
             }
@@ -5905,11 +5885,17 @@
                         }
                         break;
                     }
+        #if DEBUG_FLOW
+            SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
+                    current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
+                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
+        #endif
                     current->addCurveTo(index, endIndex, simple, true);
                     current = next;
                     index = nextStart;
                     endIndex = nextEnd;
-                } while (!simple.isClosed() && ((!unsortable) || !current->done()));
+                } while (!simple.isClosed() && (!unsortable
+                        || !current->done(SkMin32(index, endIndex))));
                 if (current->activeWinding(index, endIndex) && !simple.isClosed()) {
                     SkASSERT(unsortable);
                     int min = SkMin32(index, endIndex);
@@ -5920,7 +5906,7 @@
                 }
                 simple.close();
             } else {
-                Span* last = current->markAndChaseDoneBinary(index, endIndex);
+                Span* last = current->markAndChaseDoneUnary(index, endIndex);
                 if (last) {
                     *chaseArray.append() = last;
                 }
@@ -5945,6 +5931,11 @@
     bool closable = true;
     while ((current = findUndone(contourList, start, end))) {
         do {
+    #if DEBUG_ACTIVE_SPANS
+            if (!unsortable && current->done()) {
+                debugShowActiveSpans(contourList);
+            }
+    #endif
             SkASSERT(unsortable || !current->done());
             int nextStart = start;
             int nextEnd = end;
@@ -5967,7 +5958,7 @@
             current = next;
             start = nextStart;
             end = nextEnd;
-        } while (!simple.isClosed() && (!unsortable || !current->done()));
+        } while (!simple.isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
         if (!simple.isClosed()) {
             SkASSERT(unsortable);
             int min = SkMin32(start, end);
@@ -6019,6 +6010,9 @@
     return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
 }
 
+static bool lessThan(SkTDArray<double>& distances, const int one, const int two) {
+    return distances[one] < distances[two];
+}
     /*
         check start and end of each contour
         if not the same, record them
@@ -6063,67 +6057,64 @@
     SkTDArray<int> sLink, eLink;
     sLink.setCount(count);
     eLink.setCount(count);
-    SkTDArray<double> sBest, eBest;
-    sBest.setCount(count);
-    eBest.setCount(count);
-    int rIndex;
+    int rIndex, iIndex;
     for (rIndex = 0; rIndex < count; ++rIndex) {
-        outer = runs[rIndex];
-        const Contour& oContour = contours[outer];
-        const SkPoint& oStart = oContour.start();
-        const SkPoint& oEnd = oContour.end();
-        double dx = oEnd.fX - oStart.fX;
-        double dy = oEnd.fY - oStart.fY;
-        double dist = dx * dx + dy * dy;
-        sBest[rIndex] = eBest[rIndex] = dist;
-        sLink[rIndex] = eLink[rIndex] = rIndex;
+        sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
     }
-    for (rIndex = 0; rIndex < count - 1; ++rIndex) {
-        outer = runs[rIndex];
+    SkTDArray<double> distances;
+    const int ends = count * 2; // all starts and ends
+    const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
+    distances.setCount(entries);
+    for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
+        outer = runs[rIndex >> 1];
         const Contour& oContour = contours[outer];
-        const SkPoint& oStart = oContour.start();
-        const SkPoint& oEnd = oContour.end();
-        double bestStartDist = sBest[rIndex];
-        double bestEndDist = eBest[rIndex];
-        for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
-            int inner = runs[iIndex];
+        const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
+        const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
+                * ends - rIndex - 1;
+        for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
+            int inner = runs[iIndex >> 1];
             const Contour& iContour = contours[inner];
-            const SkPoint& iStart = iContour.start();
-            const SkPoint& iEnd = iContour.end();
-            double dx = iStart.fX - oStart.fX;
-            double dy = iStart.fY - oStart.fY;
+            const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
+            double dx = iPt.fX - oPt.fX;
+            double dy = iPt.fY - oPt.fY;
             double dist = dx * dx + dy * dy;
-            if (bestStartDist > dist && sBest[iIndex] > dist) {
-                sBest[iIndex] = bestStartDist = dist;
-                sLink[rIndex] = ~iIndex;
-                sLink[iIndex] = ~rIndex;
-            }
-            dx = iEnd.fX - oStart.fX;
-            dy = iEnd.fY - oStart.fY;
-            dist = dx * dx + dy * dy;
-            if (bestStartDist > dist && eBest[iIndex] > dist) {
-                eBest[iIndex] = bestStartDist = dist;
-                sLink[rIndex] = iIndex;
-                eLink[iIndex] = rIndex;
-            }
-            dx = iStart.fX - oEnd.fX;
-            dy = iStart.fY - oEnd.fY;
-            dist = dx * dx + dy * dy;
-            if (bestEndDist > dist && sBest[iIndex] > dist) {
-                sBest[iIndex] = bestEndDist = dist;
-                sLink[iIndex] = rIndex;
-                eLink[rIndex] = iIndex;
-            }
-            dx = iEnd.fX - oEnd.fX;
-            dy = iEnd.fY - oEnd.fY;
-            dist = dx * dx + dy * dy;
-            if (bestEndDist > dist && eBest[iIndex] > dist) {
-                eBest[iIndex] = bestEndDist = dist;
-                eLink[iIndex] = ~rIndex;
-                eLink[rIndex] = ~iIndex;
-            }
-       }
+            distances[row + iIndex] = dist; // oStart distance from iStart
+        }
     }
+    SkTDArray<int> sortedDist;
+    sortedDist.setCount(entries);
+    for (rIndex = 0; rIndex < entries; ++rIndex) {
+        sortedDist[rIndex] = rIndex;
+    }
+    QSort<SkTDArray<double>, int>(distances, sortedDist.begin(), sortedDist.end() - 1, lessThan);
+    int remaining = count; // number of start/end pairs
+    for (rIndex = 0; rIndex < entries; ++rIndex) {
+        int pair = sortedDist[rIndex];
+        int row = pair / ends;
+        int col = pair - row * ends;
+        int thingOne = row < col ? row : ends - row - 2;
+        int ndxOne = thingOne >> 1;
+        bool endOne = thingOne & 1;
+        int* linkOne = endOne ? eLink.begin() : sLink.begin();
+        if (linkOne[ndxOne] != SK_MaxS32) {
+            continue;
+        }
+        int thingTwo = row < col ? col : ends - row + col - 1;
+        int ndxTwo = thingTwo >> 1;
+        bool endTwo = thingTwo & 1;
+        int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
+        if (linkTwo[ndxTwo] != SK_MaxS32) {
+            continue;
+        }
+        SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
+        bool flip = endOne == endTwo;
+        linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
+        linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
+        if (!--remaining) {
+            break;
+        }
+    }
+    SkASSERT(!remaining);
 #if DEBUG_ASSEMBLE
     for (rIndex = 0; rIndex < count; ++rIndex) {
         int s = sLink[rIndex];
@@ -6137,17 +6128,17 @@
         bool forward = true;
         bool first = true;
         int sIndex = sLink[rIndex];
-        SkASSERT(sIndex != INT_MAX);
-        sLink[rIndex] = INT_MAX;
+        SkASSERT(sIndex != SK_MaxS32);
+        sLink[rIndex] = SK_MaxS32;
         int eIndex;
         if (sIndex < 0) {
             eIndex = sLink[~sIndex];
-            sLink[~sIndex] = INT_MAX;
+            sLink[~sIndex] = SK_MaxS32;
         } else {
             eIndex = eLink[sIndex];
-            eLink[sIndex] = INT_MAX;
+            eLink[sIndex] = SK_MaxS32;
         }
-        SkASSERT(eIndex != INT_MAX);
+        SkASSERT(eIndex != SK_MaxS32);
 #if DEBUG_ASSEMBLE
         SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
                     sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
@@ -6177,25 +6168,25 @@
             }
             if (forward) {
                 eIndex = eLink[rIndex];
-                SkASSERT(eIndex != INT_MAX);
-                eLink[rIndex] = INT_MAX;
+                SkASSERT(eIndex != SK_MaxS32);
+                eLink[rIndex] = SK_MaxS32;
                 if (eIndex >= 0) {
                     SkASSERT(sLink[eIndex] == rIndex);
-                    sLink[eIndex] = INT_MAX;
+                    sLink[eIndex] = SK_MaxS32;
                 } else {
                     SkASSERT(eLink[~eIndex] == ~rIndex);
-                    eLink[~eIndex] = INT_MAX;
+                    eLink[~eIndex] = SK_MaxS32;
                 }
             } else {
                 eIndex = sLink[rIndex];
-                SkASSERT(eIndex != INT_MAX);
-                sLink[rIndex] = INT_MAX;
+                SkASSERT(eIndex != SK_MaxS32);
+                sLink[rIndex] = SK_MaxS32;
                 if (eIndex >= 0) {
                     SkASSERT(eLink[eIndex] == rIndex);
-                    eLink[eIndex] = INT_MAX;
+                    eLink[eIndex] = SK_MaxS32;
                 } else {
                     SkASSERT(sLink[~eIndex] == ~rIndex);
-                    sLink[~eIndex] = INT_MAX;
+                    sLink[~eIndex] = SK_MaxS32;
                 }
             }
             rIndex = eIndex;
@@ -6205,15 +6196,15 @@
             }
         } while (true);
         for (rIndex = 0; rIndex < count; ++rIndex) {
-            if (sLink[rIndex] != INT_MAX) {
+            if (sLink[rIndex] != SK_MaxS32) {
                 break;
             }
         }
     } while (rIndex < count);
 #if DEBUG_ASSEMBLE
     for (rIndex = 0; rIndex < count; ++rIndex) {
-       SkASSERT(sLink[rIndex] == INT_MAX);
-       SkASSERT(eLink[rIndex] == INT_MAX);
+       SkASSERT(sLink[rIndex] == SK_MaxS32);
+       SkASSERT(eLink[rIndex] == SK_MaxS32);
     }
 #endif
 }
@@ -6253,9 +6244,7 @@
     debugShowActiveSpans(contourList);
 #endif
     // construct closed contours
-    if (builder.xorMask() == kWinding_Mask
-                ? gUseOldBridgeWinding ? !bridgeWinding(contourList, simple)
-                : bridgeWindingX(contourList, simple)
+    if (builder.xorMask() == kWinding_Mask ? bridgeWinding(contourList, simple)
                 : !bridgeXor(contourList, simple))
     { // if some edges could not be resolved, assemble remaining fragments
         SkPath temp;
@@ -6265,4 +6254,3 @@
         result = *assembled.nativePath();
     }
 }
-
diff --git a/experimental/Intersection/Simplify.h b/experimental/Intersection/Simplify.h
index a0b936a..d2e238f 100644
--- a/experimental/Intersection/Simplify.h
+++ b/experimental/Intersection/Simplify.h
@@ -15,5 +15,3 @@
 #include "SkTDArray.h"
 #include "ShapeOps.h"
 #include "TSearch.h"
-#include <algorithm> // used for std::min
-
diff --git a/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp b/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
index ca96ea6..89046a8 100644
--- a/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
+++ b/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
@@ -133,4 +133,3 @@
         }
     }
 }
-
diff --git a/experimental/Intersection/SimplifyFindNext_Test.cpp b/experimental/Intersection/SimplifyFindNext_Test.cpp
index b01b951..0a04747 100644
--- a/experimental/Intersection/SimplifyFindNext_Test.cpp
+++ b/experimental/Intersection/SimplifyFindNext_Test.cpp
@@ -37,8 +37,7 @@
     SkTDArray<SimplifyFindNextTest::Span*> chaseArray;
     bool unsortable = false;
     SimplifyFindNextTest::Segment* next = segment.findNextWinding(chaseArray,
-            true, nextStart, nextEnd, contourWinding, spanWinding,
-            unsortable);
+            nextStart, nextEnd, unsortable);
     pts[1] = next->xyAtT(&next->span(nextStart));
     SkASSERT(pts[0] == pts[1]);
     return next;
diff --git a/experimental/Intersection/SimplifyFindTop_Test.cpp b/experimental/Intersection/SimplifyFindTop_Test.cpp
index d29325c..5f26750 100644
--- a/experimental/Intersection/SimplifyFindTop_Test.cpp
+++ b/experimental/Intersection/SimplifyFindTop_Test.cpp
@@ -33,9 +33,9 @@
             end);
 #else
     SkPoint bestXY = {SK_ScalarMin, SK_ScalarMin};
-    bool unsortable = false;
+    bool done, unsortable = false;
     const SimplifyFindTopTest::Segment* topSegment =
-            findSortableTop(contourList, index, end, bestXY, unsortable, true);
+            findSortableTop(contourList, index, end, bestXY, unsortable, done, true);
 #endif
     return topSegment;
 }
diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp
index 7a1fc12..aa0a45b 100644
--- a/experimental/Intersection/SimplifyNew_Test.cpp
+++ b/experimental/Intersection/SimplifyNew_Test.cpp
@@ -2893,7 +2893,7 @@
 }
 
 static void testQuadratic59x() {
-    SkPath path, pathB;
+    SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
@@ -2907,7 +2907,7 @@
 }
 
 static void testQuadratic59() {
-    SkPath path, pathB;
+    SkPath path;
     path.setFillType(SkPath::kWinding_FillType);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
@@ -2921,7 +2921,7 @@
 }
 
 static void testQuadratic63() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
     path.lineTo(3, 2);
@@ -2934,7 +2934,7 @@
 }
 
 static void testQuadratic64() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
     path.lineTo(2, 3);
@@ -2947,7 +2947,7 @@
 }
 
 static void testQuadratic65() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
     path.lineTo(3, 2);
@@ -2960,7 +2960,7 @@
 }
 
 static void testQuadratic67x() {
-    SkPath path, pathB;
+    SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 2, 1);
@@ -2974,7 +2974,7 @@
 }
 
 static void testQuadratic68() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(1, 0, 0, 1);
     path.lineTo(1, 2);
@@ -2987,7 +2987,7 @@
 }
 
 static void testQuadratic69() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 1);
     path.lineTo(3, 2);
@@ -3000,7 +3000,7 @@
 }
 
 static void testQuadratic70x() {
-    SkPath path, pathB;
+    SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(0, 0);
     path.quadTo(1, 0, 0, 1);
@@ -3014,7 +3014,7 @@
 }
 
 static void testQuadratic71() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(1, 0, 1, 1);
     path.lineTo(3, 2);
@@ -3027,7 +3027,7 @@
 }
 
 static void testQuadratic72() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(1, 0, 1, 2);
     path.lineTo(1, 2);
@@ -3040,7 +3040,7 @@
 }
 
 static void testQuadratic73() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(1, 0, 0, 3);
     path.lineTo(0, 3);
@@ -3053,7 +3053,7 @@
 }
 
 static void testQuadratic74() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(1, 0, 1, 3);
     path.lineTo(1, 3);
@@ -3066,7 +3066,7 @@
 }
 
 static void testQuadratic75() {
-    SkPath path, pathB;
+    SkPath path;
     path.moveTo(0, 0);
     path.quadTo(1, 0, 1, 3);
     path.lineTo(2, 3);
@@ -3078,13 +3078,543 @@
     testSimplifyx(path);
 }
 
-static void (*firstTest)() = testQuadratic63;
+static void testQuadratic76() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 2);
+    path.quadTo(1, 2, 2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic77() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(3, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 3, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic78() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic79() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 3, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight1() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(2, 2);
+    path.lineTo(0, 2);
+    path.lineTo(2, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight2() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight3() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 2);
+    path.lineTo(2, 0);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight4() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(2, 2);
+    path.lineTo(2, 0);
+    path.lineTo(0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight5() {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.lineTo(1, 2);
+    path.lineTo(0, 2);
+    path.lineTo(2, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight6() {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.lineTo(0, 2);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight7() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(2, 1);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight8() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(2, 2);
+    path.lineTo(2, 1);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight9() {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.lineTo(1, 2);
+    path.lineTo(2, 1);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testEight10() {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic80() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(3, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic81() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic82() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 1, 1);
+    path.lineTo(0, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic83() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 2, 0);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(0, 2);
+    path.quadTo(2, 2, 1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic84() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 1, 1);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.quadTo(0, 1, 2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic85() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(3, 0, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(3, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic86() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 1, 1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic87() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 3, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic88() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic89x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(3, 1, 2, 2);
+    path.lineTo(0, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.quadTo(3, 1, 3, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic90x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(3, 0, 2, 2);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.quadTo(3, 2, 2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic91() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(3, 2, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(2, 1, 2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic92x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(1, 0);
+    path.quadTo(3, 0, 2, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(0, 1);
+    path.quadTo(3, 2, 2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine82() {
+    SkPath path;
+    path.addRect(20, 0, 40, 40, SkPath::kCCW_Direction);
+    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
+    path.addRect(24, 32, 33, 36, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82a() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82b() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82c() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82d() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82e() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82f() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82g() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine82h() {
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine83() {
+    SkPath path;
+path.addRect(10, 30, 30, 40, SkPath::kCCW_Direction);
+path.addRect(0, 12, 12, 18, SkPath::kCCW_Direction);
+path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine84() {
+    SkPath path;
+    path.addRect(0, 12, 60, 30, SkPath::kCCW_Direction);
+    path.addRect(10, 20, 40, 30, SkPath::kCW_Direction);
+    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
+    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine84x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 12, 60, 30, SkPath::kCCW_Direction);
+    path.addRect(10, 20, 40, 30, SkPath::kCCW_Direction);
+    path.addRect(0, 12, 12, 12, SkPath::kCCW_Direction);
+    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testLine85() {
+    SkPath path;
+    path.addRect(36, 0, 66, 60, SkPath::kCCW_Direction);
+    path.addRect(20, 0, 40, 40, SkPath::kCCW_Direction);
+    path.addRect(12, 0, 24, 24, SkPath::kCCW_Direction);
+    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+}
+
+static void testQuadralateral1() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.lineTo(2, 2);
+    path.lineTo(2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testCubic1() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.cubicTo(0, 1, 1, 1, 1, 0);
+    path.close();
+    path.moveTo(1, 0);
+    path.cubicTo(0, 0, 0, 1, 1, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+#if 0
+static void testQuadratic93() {
+    SkPath path;
+    path.moveTo(3, 0);
+    path.quadTo(0, 1, 3, 2);
+    path.lineTo(0, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.quadTo(1, 1, 2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+#endif
+
+static void cubicOp1d() {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(0,2, 1,0, 1,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,1);
+    pathB.cubicTo(0,1, 1,0, 2,0);
+    pathB.close();
+    testShapeOp(path, pathB, kDifference_Op);
+}
+
+static void (*firstTest)() = 0;
 
 static struct {
     void (*fun)();
     const char* str;
 } tests[] = {
-//    TEST(testQuadratic75),
+    TEST(cubicOp1d),
+ //   TEST(testQuadratic93),    // FIXME: gets stuck in a loop because top is unsortable
+    TEST(testCubic1),
+    TEST(testQuadralateral1),
+    TEST(testLine85),
+    TEST(testLine84),
+    TEST(testLine84x),
+    TEST(testLine83),
+    TEST(testLine82h),
+    TEST(testLine82g),
+    TEST(testLine82f),
+    TEST(testLine82e),
+    TEST(testLine82d),
+    TEST(testLine82c),
+    TEST(testLine82b),
+    TEST(testLine82a),
+    TEST(testLine82),
+    TEST(testQuadratic92x),
+    TEST(testQuadratic91),
+    TEST(testQuadratic90x),
+    TEST(testQuadratic89x),
+    TEST(testQuadratic88),
+    TEST(testQuadratic87),
+    TEST(testQuadratic86),
+    TEST(testQuadratic85),
+    TEST(testQuadratic84),
+    TEST(testQuadratic83),
+    TEST(testQuadratic82),
+    TEST(testQuadratic81),
+    TEST(testQuadratic80),
+    TEST(testEight1),
+    TEST(testEight2),
+    TEST(testEight3),
+    TEST(testEight4),
+    TEST(testEight5),
+    TEST(testEight6),
+    TEST(testEight7),
+    TEST(testEight8),
+    TEST(testEight9),
+    TEST(testEight10),
+    TEST(testQuadratic79),
+    TEST(testQuadratic78),
+    TEST(testQuadratic77),
+    TEST(testQuadratic76),
+    TEST(testQuadratic75),
     TEST(testQuadratic74),
     TEST(testQuadratic73),
     TEST(testQuadratic72),
@@ -3370,6 +3900,8 @@
     TEST(testLine1),
 };
 
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
 static void testIntersect1() {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
@@ -3525,12 +4057,20 @@
     testShapeOp(path, pathB, kUnion_Op);
 }
 
-static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+static void testOp8d() {
+    SkPath path, pathB;
+    path.addRect(0, 0, 640, 480);
+    pathB.moveTo(577330, 1971.72f);
+    pathB.cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
+    pathB.close();
+    testShapeOp(path, pathB, kDifference_Op);
+}
 
 static struct {
     void (*fun)();
     const char* str;
 } subTests[] = {
+    TEST(testOp8d),
     TEST(testDiff1),
     TEST(testIntersect1),
     TEST(testUnion1),
@@ -3552,11 +4092,12 @@
 
 static const size_t subTestCount = sizeof(subTests) / sizeof(subTests[0]);
 
-static void (*firstBinaryTest)() = 0;
+static void (*firstBinaryTest)() = testOp8d;
 
 static bool skipAll = false;
 static bool runBinaryTestsFirst = false;
 static bool runReverse = false;
+static void (*stopTest)() = 0;
 
 void SimplifyNew_Test() {
     if (skipAll) {
@@ -3597,6 +4138,9 @@
         SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
         (*tests[index].fun)();
         firstTestComplete = true;
+        if (tests[index].fun == stopTest) {
+            SkDebugf("lastTest\n");
+        }
         if (index == last) {
             break;
         }
diff --git a/experimental/Intersection/SimplifyRect4x4_Test.cpp b/experimental/Intersection/SimplifyRect4x4_Test.cpp
index 5d857e9..caa4597 100644
--- a/experimental/Intersection/SimplifyRect4x4_Test.cpp
+++ b/experimental/Intersection/SimplifyRect4x4_Test.cpp
@@ -65,7 +65,7 @@
                 }
                 path.addRect(l, t, r, b, aCW);
                 str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sWDirection);\n", l, t, r, b, aCW ? "C" : "");
+                        " SkPath::kC%sW_Direction);\n", l, t, r, b, aCW ? "C" : "");
             } else {
                 aXAlign = 5;
                 aYAlign = 5;
@@ -93,7 +93,7 @@
                 }
                 path.addRect(l, t, r, b, bCW);
                 str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sWDirection);\n", l, t, r, b, bCW ? "C" : "");
+                        " SkPath::kC%sW_Direction);\n", l, t, r, b, bCW ? "C" : "");
             } else {
                 bXAlign = 5;
                 bYAlign = 5;
@@ -121,7 +121,7 @@
                 }
                 path.addRect(l, t, r, b, cCW);
                 str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sWDirection);\n", l, t, r, b, cCW ? "C" : "");
+                        " SkPath::kC%sW_Direction);\n", l, t, r, b, cCW ? "C" : "");
             } else {
                 cXAlign = 5;
                 cYAlign = 5;
@@ -149,7 +149,7 @@
                 }
                 path.addRect(l, t, r, b, dCW);
                 str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sWDirection);\n", l, t, r, b, dCW ? "C" : "");
+                        " SkPath::kC%sW_Direction);\n", l, t, r, b, dCW ? "C" : "");
             } else {
                 dXAlign = 5;
                 dYAlign = 5;
@@ -198,4 +198,3 @@
     testsRun += waitForCompletion();
     SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
 }
-
diff --git a/experimental/Intersection/SkAntiEdge.cpp b/experimental/Intersection/SkAntiEdge.cpp
index 41887fe..9a61aa4 100644
--- a/experimental/Intersection/SkAntiEdge.cpp
+++ b/experimental/Intersection/SkAntiEdge.cpp
@@ -1079,4 +1079,3 @@
     test_horz();
     test_vert();
 }
-
diff --git a/experimental/Intersection/TSearch.h b/experimental/Intersection/TSearch.h
index 010e69f..2635470 100644
--- a/experimental/Intersection/TSearch.h
+++ b/experimental/Intersection/TSearch.h
@@ -7,11 +7,38 @@
 #ifndef TSearch_DEFINED
 #define TSearch_DEFINED
 
-#include "SkTypes.h"
-
 // FIXME: Move this templated version into SKTSearch.h
 
 template <typename T>
+static T* QSort_Partition(T* left, T* right, T* pivot)
+{
+    T pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T* newPivot = left;
+    while (left < right) {
+        if (*left < pivotValue) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
+        }
+        left += 1;
+    }
+    SkTSwap(*newPivot, *right);
+    return newPivot;
+}
+
+template <typename T>
+void QSort(T* left, T* right)
+{
+    if (left >= right) {
+        return;
+    }
+    T* pivot = left + (right - left >> 1);
+    pivot = QSort_Partition(left, right, pivot);
+    QSort(left, pivot - 1);
+    QSort(pivot + 1, right);
+}
+
+template <typename T>
 static T** QSort_Partition(T** left, T** right, T** pivot)
 {
     T* pivotValue = *pivot;
diff --git a/experimental/Intersection/TestUtilities.cpp b/experimental/Intersection/TestUtilities.cpp
index 95ed1e0..3ae10d2 100644
--- a/experimental/Intersection/TestUtilities.cpp
+++ b/experimental/Intersection/TestUtilities.cpp
@@ -4,6 +4,8 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
+#include "CubicUtilities.h"
 #include "CurveIntersection.h"
 #include "TestUtilities.h"
 
@@ -61,4 +63,3 @@
      && ((cubic[0].y <= cubic[1].y && cubic[0].y <= cubic[2].y && cubic[1].y <= cubic[3].y && cubic[2].y <= cubic[3].y)
      ||  (cubic[0].y >= cubic[1].y && cubic[0].y >= cubic[2].y && cubic[1].y >= cubic[3].y && cubic[2].x >= cubic[3].y));
 }
-
diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm
index e251a8e..94d8adf 100644
--- a/experimental/Intersection/op.htm
+++ b/experimental/Intersection/op.htm
@@ -2794,6 +2794,16 @@
 path.close();
 </div>
 
+<div id="testQuadratic59">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.quadTo(3, 1, 1, 2);
+</div>
+
 <div id="testQuadratic59o">
 path.moveTo(369.863983, 145.645813);
 path.quadTo(382.380371, 121.254936, 406.236359, 121.254936);
@@ -3003,11 +3013,370 @@
     path.close();
 </div>
 
+<div id="testQuadratic76">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 2);
+    path.quadTo(1, 2, 2, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic77">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(3, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 3, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic78">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic79">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 3, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic80">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(3, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic81">
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic82">
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 1, 1);
+    path.lineTo(0, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic83">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 2, 0);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(0, 2);
+    path.quadTo(2, 2, 1, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic84">
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 1, 1);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.quadTo(0, 1, 2, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic85">
+    path.moveTo(0, 0);
+    path.quadTo(3, 0, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(3, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic86">
+    path.moveTo(0, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 1, 1, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic87">
+    path.moveTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 3, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic88">
+    path.moveTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 2, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic89x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(3, 1, 2, 2);
+    path.lineTo(0, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.quadTo(3, 1, 3, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic90x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(3, 0, 2, 2);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.quadTo(3, 2, 2, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic91">
+    path.moveTo(0, 0);
+    path.quadTo(3, 2, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(2, 1, 2, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic92x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(1, 0);
+    path.quadTo(3, 0, 2, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(0, 1);
+    path.quadTo(3, 2, 2, 3);
+    path.close();
+</div>
+
+<div id="testLine82">
+    path.addRect(20, 0, 40, 40, SkPath::kCCWDirection);
+    path.addRect(24, 20, 36, 30, SkPath::kCCWDirection);
+    path.addRect(24, 32, 33, 36, SkPath::kCCWDirection);
+</div>
+
+<div id="testLine82a">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine82b">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine82c">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+</div>
+
+
+<div id="testLine82d">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine82e">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine82f">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine82g">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine82h">
+    SkPath path;
+    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
+    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
+    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine83">
+path.addRect(10, 30, 30, 40, SkPath::kCCW_Direction);
+path.addRect(0, 12, 12, 18, SkPath::kCCW_Direction);
+path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction);
+</div>
+
+<div id="testLine84x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 12, 60, 30, SkPath::kCCW_Direction);
+    path.addRect(10, 20, 40, 30, SkPath::kCCW_Direction);
+    path.addRect(0, 12, 12, 12, SkPath::kCCW_Direction);
+    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+</div>
+
+<div id="testLine85">
+    path.addRect(36, 0, 66, 60, SkPath::kCCW_Direction);
+    path.addRect(20, 0, 40, 40, SkPath::kCCW_Direction);
+    path.addRect(12, 0, 24, 24, SkPath::kCCW_Direction);
+    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+</div>
+
+<div id="testQuadralateral1">
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.lineTo(2, 2);
+    path.lineTo(2, 3);
+    path.close();
+</div>
+
+<div id="testCubic1">
+    path.moveTo(0, 0);
+    path.cubicTo(0, 1, 1, 1, 1, 0);
+    path.close();
+    path.moveTo(1, 0);
+    path.cubicTo(0, 0, 0, 1, 1, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic93">
+    path.moveTo(3, 0);
+    path.quadTo(0, 1, 3, 2);
+    path.lineTo(0, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.quadTo(1, 1, 2, 2);
+    path.close();
+</div>
+
+<div id="cubicOp1d">
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(0,2, 1,0, 1,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,1);
+    pathB.cubicTo(0,1, 1,0, 2,0);
+    pathB.close();
+</div>
+
 </div>
 
 <script type="text/javascript">
 
 var testDivs = [
+    cubicOp1d,
+    testQuadratic93,
+    testCubic1,
+    testQuadralateral1,
+    testLine85,
+    testLine84x,
+    testLine83,
+    testLine82h,
+    testLine82g,
+    testLine82f,
+    testLine82e,
+    testLine82d,
+    testLine82c,
+    testLine82b,
+    testLine82a,
+    testLine82,
+    testQuadratic92x,
+    testQuadratic91,
+    testQuadratic90x,
+    testQuadratic89x,
+    testQuadratic88,
+    testQuadratic87,
+    testQuadratic86,
+    testQuadratic85,
+    testQuadratic84,
+    testQuadratic83,
+    testQuadratic82,
+    testQuadratic81,
+    testQuadratic80,
+    testQuadratic79,
+    testQuadratic78,
+    testQuadratic77,
+    testQuadratic76,
     testQuadratic75,
     testQuadratic74,
     testQuadratic73,
@@ -3026,6 +3395,7 @@
     testLine81,
     testQuadratic61,
     testQuadratic60,
+    testQuadratic59,
     testQuadratic59o,
     testQuadratic59s,
     testQuadratic58o,
@@ -3441,9 +3811,8 @@
         }
         ctx.closePath();
     }
-    if (hasXor) {
-        ctx.fillType=xor; // how is this done?
-    }
+  // uncomment if ever part of the standard
+  //  ctx.fillRule=hasXor ? evenodd : nonzero;
     ctx.stroke();
     ctx.fillStyle="rgba(192,192,255, 0.3)";
     ctx.fill();
diff --git a/experimental/Intersection/qc.htm b/experimental/Intersection/qc.htm
new file mode 100644
index 0000000..2053b9b
--- /dev/null
+++ b/experimental/Intersection/qc.htm
@@ -0,0 +1,2316 @@
+<html>
+<head>
+<div style="height:0">
+
+<div id="cubic1">
+$1 = (Cubic &) @0x297c40: {{x = 60.776536520932126, y = 71.249307306133829}, {x = 87.107894191103014, y = 22.377669868235323}, {x = 1.4974754310666936, y = 68.069569937917208}, {x = 45.261946574441133, y = 17.536076632112298}}
+$3 = {{{x = 60.776536520932126, y = 71.249307306133829}, {x = 66.996745328074098, y = 59.419614231505768}, {x = 65.760655441289899, y = 53.975522936482086}}, {{x = 65.760655441289899, y = 53.975522936482086}, {x = 64.524565554505699, y = 48.531431641458411}, {x = 59.040356119613065, y = 46.936502854722001}}, {{x = 59.040356119613065, y = 46.936502854722001}, {x = 53.556146684720431, y = 45.341574067985597}, {x = 47.031996847537108, y = 45.059368518219323}}, {{x = 47.031996847537108, y = 45.059368518219323}, {x = 40.29980253329046, y = 44.781843489000011}, {x = 35.915024002796116, y = 43.168182836391942}}, {{x = 35.915024002796116, y = 43.168182836391942}, {x = 31.530245472301775, y = 41.554522183783902}, {x = 32.992157437282373, y = 35.838141687728616}}, {{x = 32.992157437282373, y = 35.838141687728616}, {x = 34.454069402262967, y = 30.121761191673329}, {x = 45.261946574441133, y = 17.536076632112298}}}
+</div>
+
+<div id="cubic2">
+$1 = {{x = 73.565270739405079, y = 11.505317181118446}, {x = 69.865863057722279, y = 35.56041113825534}, {x = 63.830000657509075, y = 90.821050755130614}, {x = 29.400041480269302, y = 26.497158886164968}}
+</div>
+
+<div id="cubic3">
+$3 = {{x = 69.729201388419241, y = 38.687735162064307}, {x = 24.764868814854356, y = 23.150171257159752}, {x = 84.928319083959011, y = 90.258844099128083}, {x = 80.39277404565027, y = 61.35338524419506}}
+</div>
+
+<div id="cubic1x0">
+{{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139}, {8.03767257, 89.1628526}},
+{{14.5975863, 41.632436}, {8.03767257, 89.1628526}, {8.03767257, 89.1628526}},
+</div>
+
+<div id="cubic1x0x">
+{{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139}, {8.03767257, 89.1628526}},
+{{14.5975863, 41.632436}, {8.03767257, 89.1628526}, {8.03767257, 89.1628526}},
+</div>
+
+<div id="cubic1x1">
+{{32.0437334, 59.0267425}, {62.4615541, 91.340573}, {61.0102145, 98.6747985}, {27.3387826, 82.9194526}},
+{{32.0437334, 59.0267425}, {75.9425223, 105.66184}, {27.3387826, 82.9194526}},
+</div>
+
+<div id="cubic1x1x">
+{{32.0437334, 59.0267425}, {62.4615541, 91.340573}, {61.0102145, 98.6747985}, {27.3387826, 82.9194526}},
+{{32.0437334, 59.0267425}, {77.7581975, 107.02498}, {27.3387826, 82.9194526}},
+</div>
+
+<div id="cubic1x2">
+{{57.8949944, 41.1707465}, {56.7368674, 76.5309905}, {56.356649, 86.19953}, {55.5002867, 93.318629}},
+{{57.8949944, 41.1707465}, {55.5002867, 93.318629}, {55.5002867, 93.318629}},
+</div>
+
+<div id="cubic1x2x">
+{{57.8949944, 41.1707465}, {56.7368674, 76.5309905}, {56.356649, 86.19953}, {55.5002867, 93.318629}},
+{{57.8949944, 41.1707465}, {55.5002867, 93.318629}, {55.5002867, 93.318629}},
+</div>
+
+<div id="cubic1x3">
+{{41.1844187, 86.52533}, {31.2211043, 33.1005529}, {20.992908, 27.8044979}, {10.1965708, 73.7658388}},
+{{41.1844187, 86.52533}, {26.1439273, 5.87597256}, {10.1965708, 73.7658388}},
+</div>
+
+<div id="cubic1x3x">
+{{41.1844187, 86.52533}, {31.2211043, 33.1005529}, {20.992908, 27.8044979}, {10.1965708, 73.7658388}},
+{{41.1844187, 86.52533}, {26.3152619, 5.60599593}, {10.1965708, 73.7658388}},
+</div>
+
+<div id="cubic1x4">
+{{51.1608132, 59.7881237}, {58.9955693, 38.5338731}, {38.8048957, 93.8817224}, {70.1083283, 10.6741861}},
+{{51.1608132, 59.7881237}, {70.1083283, 10.6741861}, {70.1083283, 10.6741861}},
+</div>
+
+<div id="cubic1x4x">
+{{51.1608132, 59.7881237}, {58.9955693, 38.5338731}, {38.8048957, 93.8817224}, {70.1083283, 10.6741861}},
+{{51.1608132, 59.7881237}, {70.1083283, 10.6741861}, {70.1083283, 10.6741861}},
+</div>
+
+<div id="cubic1x5">
+{{9.45225228, 64.0040808}, {16.5855418, 53.2003115}, {37.6356814, 42.8968969}, {68.1461999, 33.1817941}},
+{{9.45225228, 64.0040808}, {19.5957859, 48.641127}, {68.1461999, 33.1817941}},
+</div>
+
+<div id="cubic1x5x">
+{{9.45225228, 64.0040808}, {16.5855418, 53.2003115}, {37.6356814, 42.8968969}, {68.1461999, 33.1817941}},
+{{9.45225228, 64.0040808}, {21.2663043, 47.7764376}, {68.1461999, 33.1817941}},
+</div>
+
+<div id="cubic1x6">
+{{96.2293269, 26.2973682}, {79.8675829, 34.4649929}, {53.1353046, 45.0651411}, {9.82676512, 58.4413866}},
+{{96.2293269, 26.2973682}, {67.554114, 40.6117577}, {9.82676512, 58.4413866}},
+</div>
+
+<div id="cubic1x6x">
+{{96.2293269, 26.2973682}, {79.8675829, 34.4649929}, {53.1353046, 45.0651411}, {9.82676512, 58.4413866}},
+{{96.2293269, 26.2973682}, {73.2381426, 38.4629118}, {9.82676512, 58.4413866}},
+</div>
+
+<div id="cubic1x7">
+{{77.9926032, 21.6823036}, {14.4765247, 6.95017395}, {11.5735665, 16.9314125}, {66.2493838, 53.3932579}},
+{{77.9926032, 21.6823036}, {-12.9236155, 0.594894684}, {66.2493838, 53.3932579}},
+</div>
+
+<div id="cubic1x7x">
+{{77.9926032, 21.6823036}, {14.4765247, 6.95017395}, {11.5735665, 16.9314125}, {66.2493838, 53.3932579}},
+{{77.9926032, 21.6823036}, {-16.5229284, -0.857700571}, {66.2493838, 53.3932579}},
+</div>
+
+<div id="cubic1x8">
+{{56.479229, 46.4012343}, {65.5444116, 4.92526628}, {78.9504195, 19.6997536}, {93.7579262, 89.4649302}},
+{{56.479229, 46.4012343}, {70.7547833, -18.9137695}, {93.7579262, 89.4649302}},
+</div>
+
+<div id="cubic1x8x">
+{{56.479229, 46.4012343}, {65.5444116, 4.92526628}, {78.9504195, 19.6997536}, {93.7579262, 89.4649302}},
+{{56.479229, 46.4012343}, {70.8118345, -15.4977762}, {93.7579262, 89.4649302}},
+</div>
+
+<div id="cubic1x9">
+{{14.1826743, 68.2075081}, {63.5890486, 41.1398453}, {37.3805687, 55.2173676}, {38.296851, 55.1751163}},
+{{14.1826743, 68.2075081}, {38.296851, 55.1751163}, {38.296851, 55.1751163}},
+</div>
+
+<div id="cubic1x9x">
+{{14.1826743, 68.2075081}, {63.5890486, 41.1398453}, {37.3805687, 55.2173676}, {38.296851, 55.1751163}},
+{{14.1826743, 68.2075081}, {38.296851, 55.1751163}, {38.296851, 55.1751163}},
+</div>
+
+<div id="cubic2x0">
+{{27.9052884, 4.18132628}, {75.550717, 80.9000193}, {86.6244633, 97.3541595}, {31.358766, 46.7795742}},
+{{27.9052884, 4.18132628}, {66.5480157, 66.4038653}, {68.2236994, 73.2154296}},
+{{68.2236994, 73.2154296}, {70.5412458, 82.636132}, {31.358766, 46.7795742}},
+</div>
+
+<div id="cubic2x0x">
+{{27.9052884, 4.18132628}, {75.550717, 80.9000193}, {86.6244633, 97.3541595}, {31.358766, 46.7795742}},
+{{27.9052884, 4.18132628}, {64.5696024, 61.9317264}, {68.2236994, 73.2154296}},
+{{68.2236994, 73.2154296}, {71.8777964, 84.4991328}, {31.358766, 46.7795742}},
+</div>
+
+<div id="cubic2x1">
+{{55.6607299, 89.8878963}, {45.872586, 80.5522712}, {42.0218181, 60.6961261}, {19.7918636, 41.8513322}},
+{{55.6607299, 89.8878963}, {52.7119153, 87.0754092}, {42.3919757, 69.4355526}},
+{{42.3919757, 69.4355526}, {32.6126647, 52.7197915}, {19.7918636, 41.8513322}},
+</div>
+
+<div id="cubic2x1x">
+{{55.6607299, 89.8878963}, {45.872586, 80.5522712}, {42.0218181, 60.6961261}, {19.7918636, 41.8513322}},
+{{55.6607299, 89.8878963}, {49.0795145, 82.5258065}, {42.3919757, 69.4355526}},
+{{42.3919757, 69.4355526}, {35.7044369, 56.3452986}, {19.7918636, 41.8513322}},
+</div>
+
+<div id="cubic2x2">
+{{80.5982112, 14.1354079}, {73.8005055, 65.0951435}, {54.0762929, 60.254824}, {2.82780649, 26.9437232}},
+{{80.5982112, 14.1354079}, {75.7174786, 50.7243473}, {58.3820516, 52.1411292}},
+{{58.3820516, 52.1411292}, {43.4686812, 53.3599624}, {2.82780649, 26.9437232}},
+</div>
+
+<div id="cubic2x2x">
+{{80.5982112, 14.1354079}, {73.8005055, 65.0951435}, {54.0762929, 60.254824}, {2.82780649, 26.9437232}},
+{{80.5982112, 14.1354079}, {76.0811121, 51.5011698}, {58.3820516, 52.1411292}},
+{{58.3820516, 52.1411292}, {40.6829911, 52.7810886}, {2.82780649, 26.9437232}},
+</div>
+
+<div id="cubic2x3">
+{{1.6014867, 16.1869736}, {54.4660745, 11.3148647}, {68.9317074, 35.2054791}, {98.4868263, 68.0902175}},
+{{1.6014867, 16.1869736}, {36.7068582, 12.9515906}, {58.7852073, 27.9797778}},
+{{58.7852073, 27.9797778}, {68.1927213, 34.3832403}, {98.4868263, 68.0902175}},
+</div>
+
+<div id="cubic2x3x">
+{{1.6014867, 16.1869736}, {54.4660745, 11.3148647}, {68.9317074, 35.2054791}, {98.4868263, 68.0902175}},
+{{1.6014867, 16.1869736}, {39.5784138, 13.1506607}, {58.7852073, 27.9797778}},
+{{58.7852073, 27.9797778}, {77.9920009, 42.808895}, {98.4868263, 68.0902175}},
+</div>
+
+<div id="cubic2x4">
+{{23.0453529, 23.2462522}, {99.7603064, 71.4695575}, {88.8529841, 52.1034408}, {2.52897437, 4.4722111}},
+{{23.0453529, 23.2462522}, {83.1995453, 61.0594014}, {73.9267748, 49.8046823}},
+{{73.9267748, 49.8046823}, {64.9578598, 38.9187642}, {2.52897437, 4.4722111}},
+</div>
+
+<div id="cubic2x4x">
+{{23.0453529, 23.2462522}, {99.7603064, 71.4695575}, {88.8529841, 52.1034408}, {2.52897437, 4.4722111}},
+{{23.0453529, 23.2462522}, {80.2001434, 58.1848465}, {73.9267748, 49.8046823}},
+{{73.9267748, 49.8046823}, {67.6534063, 41.424518}, {2.52897437, 4.4722111}},
+</div>
+
+<div id="cubic2x5">
+{{64.4519328, 43.6345262}, {65.4821636, 58.7228333}, {54.6599207, 69.286817}, {3.532848, 76.5762786}},
+{{64.4519328, 43.6345262}, {65.2901892, 55.9112614}, {53.5513792, 63.0299695}},
+{{53.5513792, 63.0299695}, {39.721686, 71.4166414}, {3.532848, 76.5762786}},
+</div>
+
+<div id="cubic2x5x">
+{{64.4519328, 43.6345262}, {65.4821636, 58.7228333}, {54.6599207, 69.286817}, {3.532848, 76.5762786}},
+{{64.4519328, 43.6345262}, {66.113742, 54.9117003}, {53.5513792, 63.0299695}},
+{{53.5513792, 63.0299695}, {40.9890164, 71.1482387}, {3.532848, 76.5762786}},
+</div>
+
+<div id="cubic2x6">
+{{82.5366784, 93.9543251}, {90.3418213, 74.9907304}, {69.20575, 41.039441}, {49.884656, 11.4126389}},
+{{82.5366784, 93.9543251}, {87.7472455, 81.2945847}, {76.383006, 56.6821848}},
+{{76.383006, 56.6821848}, {69.0501277, 40.8008111}, {49.884656, 11.4126389}},
+</div>
+
+<div id="cubic2x6x">
+{{82.5366784, 93.9543251}, {90.3418213, 74.9907304}, {69.20575, 41.039441}, {49.884656, 11.4126389}},
+{{82.5366784, 93.9543251}, {87.4294046, 79.1281234}, {76.383006, 56.6821848}},
+{{76.383006, 56.6821848}, {65.3366074, 34.2362462}, {49.884656, 11.4126389}},
+</div>
+
+<div id="cubic2x7">
+{{81.6027334, 97.1400425}, {32.694003, 88.1076582}, {25.4108981, 80.9641684}, {64.7788314, 37.8185316}},
+{{81.6027334, 97.1400425}, {43.6639346, 90.1335672}, {40.0870335, 80.2717567}},
+{{40.0870335, 80.2717567}, {36.0922592, 69.2578356}, {64.7788314, 37.8185316}},
+</div>
+
+<div id="cubic2x7x">
+{{81.6027334, 97.1400425}, {32.694003, 88.1076582}, {25.4108981, 80.9641684}, {64.7788314, 37.8185316}},
+{{81.6027334, 97.1400425}, {44.7641414, 91.5498493}, {40.0870335, 80.2717567}},
+{{40.0870335, 80.2717567}, {35.4099255, 68.9936642}, {64.7788314, 37.8185316}},
+</div>
+
+<div id="cubic2x8">
+{{43.6332675, 44.3267048}, {98.9277035, 77.9134953}, {92.1152147, 80.4133992}, {7.99971512, 51.2120151}},
+{{43.6332675, 44.3267048}, {85.4039037, 69.6989069}, {78.0952172, 71.3149254}},
+{{78.0952172, 71.3149254}, {70.649222, 72.9613041}, {7.99971512, 51.2120151}},
+</div>
+
+<div id="cubic2x8x">
+{{43.6332675, 44.3267048}, {98.9277035, 77.9134953}, {92.1152147, 80.4133992}, {7.99971512, 51.2120151}},
+{{43.6332675, 44.3267048}, {85.5789722, 69.5359977}, {78.0952172, 71.3149254}},
+{{78.0952172, 71.3149254}, {70.6114621, 73.0938531}, {7.99971512, 51.2120151}},
+</div>
+
+<div id="cubic2x9">
+{{3.42763756, 8.30440876}, {72.1979502, 30.9497829}, {73.001545, 36.9676506}, {15.3033876, 4.03527813}},
+{{3.42763756, 8.30440876}, {62.7221525, 27.8294978}, {56.7911889, 27.0114984}},
+{{56.7911889, 27.0114984}, {55.1652933, 26.7872547}, {15.3033876, 4.03527813}},
+</div>
+
+<div id="cubic2x9x">
+{{3.42763756, 8.30440876}, {72.1979502, 30.9497829}, {73.001545, 36.9676506}, {15.3033876, 4.03527813}},
+{{3.42763756, 8.30440876}, {54.7095919, 25.9860248}, {56.7911889, 27.0114984}},
+{{56.7911889, 27.0114984}, {58.8727859, 28.036972}, {15.3033876, 4.03527813}},
+</div>
+
+<div id="cubic3x0">
+{{37.7493998, 54.1620116}, {0.928181503, 99.9465276}, {1.29019157, 84.2497321}, {85.2470221, 46.7010984}},
+{{37.7493998, 54.1620116}, {30.0262679, 63.7651662}, {19.9157535, 75.222785}},
+{{19.9157535, 75.222785}, {12.0739437, 84.1094218}, {23.0870945, 77.9306985}},
+{{23.0870945, 77.9306985}, {53.2236264, 61.0231583}, {85.2470221, 46.7010984}},
+</div>
+
+<div id="cubic3x0x">
+{{37.7493998, 54.1620116}, {0.928181503, 99.9465276}, {1.29019157, 84.2497321}, {85.2470221, 46.7010984}},
+{{37.7493998, 54.1620116}, {26.0576358, 68.4753229}, {19.9157535, 75.222785}},
+{{19.9157535, 75.222785}, {7.36020346, 87.8757618}, {23.0870945, 77.9306985}},
+{{23.0870945, 77.9306985}, {37.855802, 68.7339725}, {85.2470221, 46.7010984}},
+</div>
+
+<div id="cubic3x1">
+{{77.853445, 82.8493315}, {48.7140421, 36.904878}, {60.2845497, 2.42643608}, {81.1111786, 35.5792593}},
+{{77.853445, 82.8493315}, {64.326138, 61.5206609}, {61.1190571, 42.8070764}},
+{{61.1190571, 42.8070764}, {58.2548088, 26.0939491}, {64.5348786, 22.8899965}},
+{{64.5348786, 22.8899965}, {71.0512995, 19.5654629}, {81.1111786, 35.5792593}},
+</div>
+
+<div id="cubic3x1x">
+{{77.853445, 82.8493315}, {48.7140421, 36.904878}, {60.2845497, 2.42643608}, {81.1111786, 35.5792593}},
+{{77.853445, 82.8493315}, {63.5749823, 59.3570561}, {61.1190571, 42.8070764}},
+{{61.1190571, 42.8070764}, {58.6631319, 26.2570968}, {64.5348786, 22.8899965}},
+{{64.5348786, 22.8899965}, {70.4066254, 19.5228963}, {81.1111786, 35.5792593}},
+</div>
+
+<div id="cubic3x2">
+{{38.2012882, 49.0499648}, {82.7576585, 7.96646616}, {92.3967278, 11.8042378}, {93.8251679, 19.597347}},
+{{38.2012882, 49.0499648}, {58.8845939, 29.9787846}, {72.1076941, 21.4229661}},
+{{72.1076941, 21.4229661}, {83.1166319, 14.2997899}, {88.6707154, 14.6399404}},
+{{88.6707154, 14.6399404}, {92.9647013, 14.9029184}, {93.8251679, 19.597347}},
+</div>
+
+<div id="cubic3x2x">
+{{38.2012882, 49.0499648}, {82.7576585, 7.96646616}, {92.3967278, 11.8042378}, {93.8251679, 19.597347}},
+{{38.2012882, 49.0499648}, {60.2321893, 28.8875296}, {72.1076941, 21.4229661}},
+{{72.1076941, 21.4229661}, {83.983199, 13.9584026}, {88.6707154, 14.6399404}},
+{{88.6707154, 14.6399404}, {93.3582319, 15.3214782}, {93.8251679, 19.597347}},
+</div>
+
+<div id="cubic3x3">
+{{52.7120295, 31.0801866}, {64.6964272, 52.8517052}, {78.6098203, 95.2490945}, {51.5310243, 81.9254304}},
+{{52.7120295, 31.0801866}, {59.5211432, 43.4499986}, {63.7497522, 56.8993316}},
+{{63.7497522, 56.8993316}, {68.6258107, 72.4079153}, {66.5354307, 79.5030739}},
+{{66.5354307, 79.5030739}, {64.0124137, 88.0666872}, {51.5310243, 81.9254304}},
+</div>
+
+<div id="cubic3x3x">
+{{52.7120295, 31.0801866}, {64.6964272, 52.8517052}, {78.6098203, 95.2490945}, {51.5310243, 81.9254304}},
+{{52.7120295, 31.0801866}, {59.1016467, 42.6728619}, {63.7497522, 56.8993316}},
+{{63.7497522, 56.8993316}, {68.3978576, 71.1258013}, {66.5354307, 79.5030739}},
+{{66.5354307, 79.5030739}, {64.6730039, 87.8803465}, {51.5310243, 81.9254304}},
+</div>
+
+<div id="cubic3x4">
+{{20.7082833, 44.1170772}, {75.7169666, 75.0570675}, {84.1330966, 24.9551825}, {21.7528516, 0.176163297}},
+{{20.7082833, 44.1170772}, {46.6273271, 58.6954113}, {59.2896776, 51.9825439}},
+{{59.2896776, 51.9825439}, {71.1540672, 45.6927106}, {61.4307428, 29.4567029}},
+{{61.4307428, 29.4567029}, {50.807026, 11.71722}, {21.7528516, 0.176163297}},
+</div>
+
+<div id="cubic3x4x">
+{{20.7082833, 44.1170772}, {75.7169666, 75.0570675}, {84.1330966, 24.9551825}, {21.7528516, 0.176163297}},
+{{20.7082833, 44.1170772}, {48.4367344, 58.6022136}, {59.2896776, 51.9825439}},
+{{59.2896776, 51.9825439}, {70.1426209, 45.3628742}, {61.4307428, 29.4567029}},
+{{61.4307428, 29.4567029}, {52.7188646, 13.5505316}, {21.7528516, 0.176163297}},
+</div>
+
+<div id="cubic3x5">
+{{20.8291142, 74.9221559}, {16.6750469, 57.513008}, {21.1249099, 46.360262}, {76.9233116, 50.0985771}},
+{{20.8291142, 74.9221559}, {18.4755741, 65.0587801}, {21.1261573, 59.9182775}},
+{{21.1261573, 59.9182775}, {24.474036, 53.4254499}, {36.6579558, 51.0041468}},
+{{36.6579558, 51.0041468}, {50.2178533, 48.3093965}, {76.9233116, 50.0985771}},
+</div>
+
+<div id="cubic3x5x">
+{{20.8291142, 74.9221559}, {16.6750469, 57.513008}, {21.1249099, 46.360262}, {76.9233116, 50.0985771}},
+{{20.8291142, 74.9221559}, {18.3562971, 66.1376314}, {21.1261573, 59.9182775}},
+{{21.1261573, 59.9182775}, {23.8960175, 53.6989236}, {36.6579558, 51.0041468}},
+{{36.6579558, 51.0041468}, {49.4198942, 48.3093701}, {76.9233116, 50.0985771}},
+</div>
+
+<div id="cubic3x6">
+{{39.306348, 21.7912016}, {44.72463, 86.8568551}, {3.16400146, 77.3725818}, {0.981986477, 4.24671164}},
+{{39.306348, 21.7912016}, {41.8751537, 52.6388057}, {32.26342, 62.4108917}},
+{{32.26342, 62.4108917}, {23.2193358, 71.6058581}, {13.0917792, 55.754704}},
+{{13.0917792, 55.754704}, {2.00097021, 38.3959145}, {0.981986477, 4.24671164}},
+</div>
+
+<div id="cubic3x6x">
+{{39.306348, 21.7912016}, {44.72463, 86.8568551}, {3.16400146, 77.3725818}, {0.981986477, 4.24671164}},
+{{39.306348, 21.7912016}, {41.2158823, 54.2230253}, {32.26342, 62.4108917}},
+{{32.26342, 62.4108917}, {23.3109577, 70.5987581}, {13.0917792, 55.754704}},
+{{13.0917792, 55.754704}, {2.87260067, 40.9106498}, {0.981986477, 4.24671164}},
+</div>
+
+<div id="cubic3x7">
+{{85.4907277, 42.6604079}, {93.4752654, 38.7852218}, {63.2230996, 90.6357313}, {14.7351715, 54.0271501}},
+{{85.4907277, 42.6604079}, {92.9820656, 39.0245896}, {81.4704732, 52.0202764}},
+{{81.4704732, 52.0202764}, {71.0697229, 63.7619094}, {56.4037366, 66.489545}},
+{{56.4037366, 66.489545}, {36.2148038, 70.2443591}, {14.7351715, 54.0271501}},
+</div>
+
+<div id="cubic3x7x">
+{{85.4907277, 42.6604079}, {93.4752654, 38.7852218}, {63.2230996, 90.6357313}, {14.7351715, 54.0271501}},
+{{85.4907277, 42.6604079}, {89.2978026, 42.0578592}, {81.4704732, 52.0202764}},
+{{81.4704732, 52.0202764}, {73.6431437, 61.9826936}, {56.4037366, 66.489545}},
+{{56.4037366, 66.489545}, {39.1643295, 70.9963964}, {14.7351715, 54.0271501}},
+</div>
+
+<div id="cubic3x8">
+{{95.2957887, 36.3209844}, {46.7852652, 19.9519225}, {31.9607143, 63.7251956}, {29.3620354, 87.7284659}},
+{{95.2957887, 36.3209844}, {73.0036621, 28.7988805}, {57.2191042, 37.0396513}},
+{{57.2191042, 37.0396513}, {44.4309585, 43.7160611}, {36.8308235, 60.0949108}},
+{{36.8308235, 60.0949108}, {30.9912846, 72.6795466}, {29.3620354, 87.7284659}},
+</div>
+
+<div id="cubic3x8x">
+{{95.2957887, 36.3209844}, {46.7852652, 19.9519225}, {31.9607143, 63.7251956}, {29.3620354, 87.7284659}},
+{{95.2957887, 36.3209844}, {71.2392316, 28.8763825}, {57.2191042, 37.0396513}},
+{{57.2191042, 37.0396513}, {43.1989768, 45.20292}, {36.8308235, 60.0949108}},
+{{36.8308235, 60.0949108}, {30.4626702, 74.9869017}, {29.3620354, 87.7284659}},
+</div>
+
+<div id="cubic3x9">
+{{11.6274826, 23.1005334}, {50.665531, 35.5788199}, {73.2259434, 8.43082047}, {96.7997166, 12.8374226}},
+{{11.6274826, 23.1005334}, {26.8690196, 27.9724026}, {42.2684837, 25.6341105}},
+{{42.2684837, 25.6341105}, {51.3514943, 24.254924}, {67.0182186, 18.4582098}},
+{{67.0182186, 18.4582098}, {87.1065443, 11.0254957}, {96.7997166, 12.8374226}},
+</div>
+
+<div id="cubic3x9x">
+{{11.6274826, 23.1005334}, {50.665531, 35.5788199}, {73.2259434, 8.43082047}, {96.7997166, 12.8374226}},
+{{11.6274826, 23.1005334}, {28.7555518, 28.1569895}, {42.2684837, 25.6341105}},
+{{42.2684837, 25.6341105}, {55.7814156, 23.1112314}, {67.0182186, 18.4582098}},
+{{67.0182186, 18.4582098}, {82.5639521, 11.3566582}, {96.7997166, 12.8374226}},
+</div>
+
+<div id="cubic4x0">
+{{24.2578299, 1.34695745}, {38.313885, 41.465269}, {6.77689729, 99.312693}, {48.4308047, 76.5337766}},
+{{24.2578299, 1.34695745}, {27.9750096, 11.9564045}, {28.1705087, 26.7539994}},
+{{28.1705087, 26.7539994}, {28.2848367, 35.4076429}, {26.8433323, 51.6959666}},
+{{26.8433323, 51.6959666}, {24.9672902, 72.8943612}, {27.4957684, 77.9195086}},
+{{27.4957684, 77.9195086}, {31.4664535, 85.8109263}, {48.4308047, 76.5337766}},
+</div>
+
+<div id="cubic4x0x">
+{{24.2578299, 1.34695745}, {38.313885, 41.465269}, {6.77689729, 99.312693}, {48.4308047, 76.5337766}},
+{{24.2578299, 1.34695745}, {28.2364584, 13.5769276}, {28.1705087, 26.7539994}},
+{{28.1705087, 26.7539994}, {28.104559, 39.9310711}, {26.8433323, 51.6959666}},
+{{26.8433323, 51.6959666}, {24.5051265, 69.7176532}, {27.4957684, 77.9195086}},
+{{27.4957684, 77.9195086}, {30.4864104, 86.121364}, {48.4308047, 76.5337766}},
+</div>
+
+<div id="cubic4x1">
+{{3.18338154, 3.09354817}, {93.264044, 88.7879534}, {59.132973, 47.8778685}, {83.3354337, 18.6335197}},
+{{3.18338154, 3.09354817}, {35.8260971, 34.1468066}, {48.9325859, 44.6922254}},
+{{48.9325859, 44.6922254}, {62.4915199, 55.6016796}, {67.1257676, 54.3074276}},
+{{67.1257676, 54.3074276}, {70.2512267, 53.4345498}, {72.6465479, 43.2128608}},
+{{72.6465479, 43.2128608}, {76.4594277, 26.9419442}, {83.3354337, 18.6335197}},
+</div>
+
+<div id="cubic4x1x">
+{{3.18338154, 3.09354817}, {93.264044, 88.7879534}, {59.132973, 47.8778685}, {83.3354337, 18.6335197}},
+{{3.18338154, 3.09354817}, {34.807442, 33.2979688}, {48.9325859, 44.6922254}},
+{{48.9325859, 44.6922254}, {63.0577297, 56.086482}, {67.1257676, 54.3074276}},
+{{67.1257676, 54.3074276}, {71.1938054, 52.5283732}, {72.6465479, 43.2128608}},
+{{72.6465479, 43.2128608}, {74.0679283, 31.8888383}, {83.3354337, 18.6335197}},
+</div>
+
+<div id="cubic4x2">
+{{5.3607232, 97.6747591}, {19.6754743, 85.6972941}, {14.421376, 80.0662188}, {72.9397619, 98.5790647}},
+{{5.3607232, 97.6747591}, {7.15867444, 96.170374}, {9.99357731, 93.4972246}},
+{{9.99357731, 93.4972246}, {15.4658237, 88.3372129}, {19.1058065, 87.2914549}},
+{{19.1058065, 87.2914549}, {24.5745003, 85.7203128}, {35.9606711, 88.1040392}},
+{{35.9606711, 88.1040392}, {47.3960315, 90.4980635}, {72.9397619, 98.5790647}},
+</div>
+
+<div id="cubic4x2x">
+{{5.3607232, 97.6747591}, {19.6754743, 85.6972941}, {14.421376, 80.0662188}, {72.9397619, 98.5790647}},
+{{5.3607232, 97.6747591}, {8.01636596, 95.4093652}, {9.99357731, 93.4972246}},
+{{9.99357731, 93.4972246}, {14.161732, 88.9702622}, {19.1058065, 87.2914549}},
+{{19.1058065, 87.2914549}, {24.0498811, 85.6126476}, {35.9606711, 88.1040392}},
+{{35.9606711, 88.1040392}, {47.871461, 90.5954307}, {72.9397619, 98.5790647}},
+</div>
+
+<div id="cubic4x3">
+{{18.340571, 49.9760211}, {46.9862021, 97.0991299}, {45.0770262, 9.57918773}, {97.4081647, 39.0235061}},
+{{18.340571, 49.9760211}, {27.6759966, 65.333137}, {34.9254897, 64.1054159}},
+{{34.9254897, 64.1054159}, {39.46946, 63.3358824}, {47.9840785, 52.4199142}},
+{{47.9840785, 52.4199142}, {58.4594697, 38.9901831}, {66.2068641, 35.3036404}},
+{{66.2068641, 35.3036404}, {79.5296469, 28.9640886}, {97.4081647, 39.0235061}},
+</div>
+
+<div id="cubic4x3x">
+{{18.340571, 49.9760211}, {46.9862021, 97.0991299}, {45.0770262, 9.57918773}, {97.4081647, 39.0235061}},
+{{18.340571, 49.9760211}, {28.406995, 66.1423531}, {34.9254897, 64.1054159}},
+{{34.9254897, 64.1054159}, {41.4439844, 62.0684788}, {47.9840785, 52.4199142}},
+{{47.9840785, 52.4199142}, {54.9532374, 41.9238105}, {66.2068641, 35.3036404}},
+{{66.2068641, 35.3036404}, {77.4604907, 28.6834703}, {97.4081647, 39.0235061}},
+</div>
+
+<div id="cubic4x4">
+{{68.0670356, 2.66693188}, {23.1241074, 46.8739094}, {9.79601006, 41.5410025}, {79.6294187, 31.6402602}},
+{{68.0670356, 2.66693188}, {56.7853646, 13.7638629}, {41.0137122, 27.3042275}},
+{{41.0137122, 27.3042275}, {29.788878, 36.941033}, {31.0433353, 38.0354879}},
+{{31.0433353, 38.0354879}, {32.2977925, 39.1299429}, {51.0493703, 36.059867}},
+{{51.0493703, 36.059867}, {67.7999464, 33.3174024}, {79.6294187, 31.6402602}},
+</div>
+
+<div id="cubic4x4x">
+{{68.0670356, 2.66693188}, {23.1241074, 46.8739094}, {9.79601006, 41.5410025}, {79.6294187, 31.6402602}},
+{{68.0670356, 2.66693188}, {50.940695, 19.1344928}, {41.0137122, 27.3042275}},
+{{41.0137122, 27.3042275}, {29.4752637, 36.6674193}, {31.0433353, 38.0354879}},
+{{31.0433353, 38.0354879}, {32.6114068, 39.4035566}, {51.0493703, 36.059867}},
+{{51.0493703, 36.059867}, {61.9478496, 34.2106234}, {79.6294187, 31.6402602}},
+</div>
+
+<div id="cubic4x5">
+{{80.6109054, 27.4877124}, {85.9817399, 95.1019056}, {77.7276185, 68.083746}, {83.5185407, 96.1129614}},
+{{80.6109054, 27.4877124}, {82.5499811, 51.8990092}, {82.5259477, 65.0368853}},
+{{82.5259477, 65.0368853}, {82.5124299, 72.4264589}, {81.7002161, 78.1564815}},
+{{81.7002161, 78.1564815}, {81.2315331, 81.462956}, {81.4316783, 83.8990214}},
+{{81.4316783, 83.8990214}, {81.7199102, 87.4072321}, {83.5185407, 96.1129614}},
+</div>
+
+<div id="cubic4x5x">
+{{80.6109054, 27.4877124}, {85.9817399, 95.1019056}, {77.7276185, 68.083746}, {83.5185407, 96.1129614}},
+{{80.6109054, 27.4877124}, {82.6925268, 54.7439407}, {82.5259477, 65.0368853}},
+{{82.5259477, 65.0368853}, {82.3593687, 75.3298298}, {81.7002161, 78.1564815}},
+{{81.7002161, 78.1564815}, {81.2086421, 80.6624342}, {81.4316783, 83.8990214}},
+{{81.4316783, 83.8990214}, {81.6547145, 87.1356086}, {83.5185407, 96.1129614}},
+</div>
+
+<div id="cubic4x6">
+{{70.5424749, 7.37512261}, {53.6857094, 95.7185581}, {41.8065019, 41.8776796}, {38.1617001, 83.6927474}},
+{{70.5424749, 7.37512261}, {64.0240124, 41.5372735}, {57.0495799, 54.4661558}},
+{{57.0495799, 54.4661558}, {53.0372544, 61.9040203}, {46.633996, 64.0865108}},
+{{46.633996, 64.0865108}, {42.8562175, 65.3741311}, {41.4346736, 67.9484678}},
+{{41.4346736, 67.9484678}, {39.1777978, 72.0355437}, {38.1617001, 83.6927474}},
+</div>
+
+<div id="cubic4x6x">
+{{70.5424749, 7.37512261}, {53.6857094, 95.7185581}, {41.8065019, 41.8776796}, {38.1617001, 83.6927474}},
+{{70.5424749, 7.37512261}, {63.0887524, 44.8198844}, {57.0495799, 54.4661558}},
+{{57.0495799, 54.4661558}, {51.0104074, 64.1124273}, {46.633996, 64.0865108}},
+{{46.633996, 64.0865108}, {43.5741104, 64.6069899}, {41.4346736, 67.9484678}},
+{{41.4346736, 67.9484678}, {39.2952367, 71.2899457}, {38.1617001, 83.6927474}},
+</div>
+
+<div id="cubic4x7">
+{{24.0062249, 72.6211198}, {43.1612821, 11.6690897}, {22.3913226, 30.9587957}, {24.4801394, 37.7033828}},
+{{24.0062249, 72.6211198}, {30.5430063, 51.8208637}, {31.8675739, 40.5026282}},
+{{31.8675739, 40.5026282}, {32.9179067, 31.5276894}, {30.6430223, 29.7760199}},
+{{30.6430223, 29.7760199}, {28.7741669, 28.3369942}, {26.2185506, 31.7425273}},
+{{26.2185506, 31.7425273}, {23.6812077, 35.1237098}, {24.4801394, 37.7033828}},
+</div>
+
+<div id="cubic4x7x">
+{{24.0062249, 72.6211198}, {43.1612821, 11.6690897}, {22.3913226, 30.9587957}, {24.4801394, 37.7033828}},
+{{24.0062249, 72.6211198}, {30.9441221, 50.1265572}, {31.8675739, 40.5026282}},
+{{31.8675739, 40.5026282}, {32.7910257, 30.8786991}, {30.6430223, 29.7760199}},
+{{30.6430223, 29.7760199}, {28.4950189, 28.6733406}, {26.2185506, 31.7425273}},
+{{26.2185506, 31.7425273}, {23.9420823, 34.811714}, {24.4801394, 37.7033828}},
+</div>
+
+<div id="cubic4x8">
+{{83.4128604, 19.944285}, {3.59808416, 73.0005231}, {19.791118, 29.3197498}, {77.0346567, 21.4750355}},
+{{83.4128604, 19.944285}, {56.4409479, 37.8736494}, {40.6945347, 43.6697281}},
+{{40.6945347, 43.6697281}, {27.6832174, 48.4590486}, {28.8268904, 43.5475174}},
+{{28.8268904, 43.5475174}, {29.9619906, 38.6728026}, {42.6576802, 32.0063781}},
+{{42.6576802, 32.0063781}, {57.6563877, 24.1306537}, {77.0346567, 21.4750355}},
+</div>
+
+<div id="cubic4x8x">
+{{83.4128604, 19.944285}, {3.59808416, 73.0005231}, {19.791118, 29.3197498}, {77.0346567, 21.4750355}},
+{{83.4128604, 19.944285}, {53.6969963, 39.3225107}, {40.6945347, 43.6697281}},
+{{40.6945347, 43.6697281}, {27.6920731, 48.0169456}, {28.8268904, 43.5475174}},
+{{28.8268904, 43.5475174}, {29.9617077, 39.0780892}, {42.6576802, 32.0063781}},
+{{42.6576802, 32.0063781}, {55.3536527, 24.9346669}, {77.0346567, 21.4750355}},
+</div>
+
+<div id="cubic4x9">
+{{13.6133623, 99.7800201}, {2.79733483, 14.8064674}, {52.2975031, 64.1339272}, {98.9146078, 57.8132952}},
+{{13.6133623, 99.7800201}, {10.1036384, 72.2067072}, {14.9007617, 60.2808941}},
+{{14.9007617, 60.2808941}, {19.0431228, 49.9828423}, {30.5807774, 49.2954321}},
+{{30.5807774, 49.2954321}, {37.7022355, 48.8711377}, {56.117561, 53.190874}},
+{{56.117561, 53.190874}, {84.2814202, 59.7973522}, {98.9146078, 57.8132952}},
+</div>
+
+<div id="cubic4x9x">
+{{13.6133623, 99.7800201}, {2.79733483, 14.8064674}, {52.2975031, 64.1339272}, {98.9146078, 57.8132952}},
+{{13.6133623, 99.7800201}, {10.0919269, 71.197946}, {14.9007617, 60.2808941}},
+{{14.9007617, 60.2808941}, {19.7095965, 49.3638421}, {30.5807774, 49.2954321}},
+{{30.5807774, 49.2954321}, {41.4519583, 49.2270222}, {56.117561, 53.190874}},
+{{56.117561, 53.190874}, {76.476137, 59.3205959}, {98.9146078, 57.8132952}},
+</div>
+
+<div id="cubic5x0">
+{{73.5652707, 11.5053172}, {69.8658631, 35.5604111}, {63.8300007, 90.8210508}, {29.4000415, 26.4971589}},
+{{73.5652707, 11.5053172}, {73.3885843, 12.654206}, {73.0163837, 15.1491032}},
+{{73.0163837, 15.1491032}, {70.9175181, 29.2180034}, {69.2121151, 36.2586021}},
+{{69.2121151, 36.2586021}, {66.2435432, 48.5140774}, {62.0808557, 53.3239452}},
+{{62.0808557, 53.3239452}, {56.8541433, 59.3632637}, {49.5132747, 54.1388813}},
+{{49.5132747, 54.1388813}, {40.9233022, 48.0255311}, {29.4000415, 26.4971589}},
+</div>
+
+<div id="cubic5x0x">
+{{73.5652707, 11.5053172}, {69.8658631, 35.5604111}, {63.8300007, 90.8210508}, {29.4000415, 26.4971589}},
+{{73.5652707, 11.5053172}, {73.3009509, 13.2327574}, {73.0163837, 15.1491032}},
+{{73.0163837, 15.1491032}, {71.6823308, 25.1891102}, {69.2121151, 36.2586021}},
+{{69.2121151, 36.2586021}, {66.7418995, 47.328094}, {62.0808557, 53.3239452}},
+{{62.0808557, 53.3239452}, {57.4198119, 59.3197964}, {49.5132747, 54.1388813}},
+{{49.5132747, 54.1388813}, {41.6067374, 48.9579661}, {29.4000415, 26.4971589}},
+</div>
+
+<div id="cubic5x1">
+{{80.7539402, 31.4736433}, {77.5229567, 28.3334108}, {99.6348716, 63.2867312}, {60.0910899, 50.9480224}},
+{{80.7539402, 31.4736433}, {80.1293736, 30.8666193}, {81.0927918, 33.3061348}},
+{{81.0927918, 33.3061348}, {82.7770389, 37.5708941}, {83.4256875, 40.319949}},
+{{83.4256875, 40.319949}, {84.5951485, 45.2762733}, {83.5574674, 48.3997103}},
+{{83.5574674, 48.3997103}, {82.2131806, 52.4460356}, {77.2064841, 53.3431558}},
+{{77.2064841, 53.3431558}, {71.2104332, 54.4175525}, {60.0910899, 50.9480224}},
+</div>
+
+<div id="cubic5x1x">
+{{80.7539402, 31.4736433}, {77.5229567, 28.3334108}, {99.6348716, 63.2867312}, {60.0910899, 50.9480224}},
+{{80.7539402, 31.4736433}, {79.9741932, 30.7172975}, {81.0927918, 33.3061348}},
+{{81.0927918, 33.3061348}, {82.2743126, 36.0212723}, {83.4256875, 40.319949}},
+{{83.4256875, 40.319949}, {84.5770623, 44.6186258}, {83.5574674, 48.3997103}},
+{{83.5574674, 48.3997103}, {82.5378725, 52.1807949}, {77.2064841, 53.3431558}},
+{{77.2064841, 53.3431558}, {71.8750956, 54.5055166}, {60.0910899, 50.9480224}},
+</div>
+
+<div id="cubic5x2">
+{{30.9220007, 6.06626757}, {55.7590106, 41.691652}, {11.5944877, 68.5545306}, {95.99508, 89.3088364}},
+{{30.9220007, 6.06626757}, {36.6680413, 14.3081978}, {38.6632502, 23.8943374}},
+{{38.6632502, 23.8943374}, {39.8467707, 29.5806556}, {40.109652, 40.2122054}},
+{{40.109652, 40.2122054}, {40.4211241, 52.8088852}, {42.588681, 58.5795625}},
+{{42.588681, 58.5795625}, {46.141784, 68.0389732}, {57.1660752, 74.8906876}},
+{{57.1660752, 74.8906876}, {70.1317224, 82.9489754}, {95.99508, 89.3088364}},
+</div>
+
+<div id="cubic5x2x">
+{{30.9220007, 6.06626757}, {55.7590106, 41.691652}, {11.5944877, 68.5545306}, {95.99508, 89.3088364}},
+{{30.9220007, 6.06626757}, {37.1487855, 15.3683637}, {38.6632502, 23.8943374}},
+{{38.6632502, 23.8943374}, {40.1777149, 32.4203112}, {40.109652, 40.2122054}},
+{{40.109652, 40.2122054}, {39.8437309, 49.9303489}, {42.588681, 58.5795625}},
+{{42.588681, 58.5795625}, {45.3336311, 67.2287761}, {57.1660752, 74.8906876}},
+{{57.1660752, 74.8906876}, {68.9985192, 82.5525991}, {95.99508, 89.3088364}},
+</div>
+
+<div id="cubic5x3">
+{{74.7743754, 32.9274563}, {11.7577089, 11.8127863}, {37.4985242, 37.696964}, {72.8744837, 1.44809908}},
+{{74.7743754, 32.9274563}, {60.7273344, 28.2207866}, {49.5992486, 25.6169692}},
+{{49.5992486, 25.6169692}, {43.5981152, 24.2127874}, {37.944249, 23.3667683}},
+{{37.944249, 23.3667683}, {35.9715145, 23.0715771}, {37.4878767, 22.7373028}},
+{{37.4878767, 22.7373028}, {44.1459136, 21.2695724}, {50.3950023, 18.2059418}},
+{{50.3950023, 18.2059418}, {62.1391178, 12.4483612}, {72.8744837, 1.44809908}},
+</div>
+
+<div id="cubic5x3x">
+{{74.7743754, 32.9274563}, {11.7577089, 11.8127863}, {37.4985242, 37.696964}, {72.8744837, 1.44809908}},
+{{74.7743754, 32.9274563}, {58.4980331, 27.5812922}, {49.5992486, 25.6169692}},
+{{49.5992486, 25.6169692}, {40.7004642, 23.6526461}, {37.944249, 23.3667683}},
+{{37.944249, 23.3667683}, {35.0992403, 23.0813479}, {37.4878767, 22.7373028}},
+{{37.4878767, 22.7373028}, {40.7578786, 22.4379602}, {50.3950023, 18.2059418}},
+{{50.3950023, 18.2059418}, {60.0321261, 13.9739234}, {72.8744837, 1.44809908}},
+</div>
+
+<div id="cubic5x4">
+{{72.6117562, 85.7863012}, {10.3637705, 83.8910282}, {56.5110395, 81.0400843}, {40.6969416, 93.4977145}},
+{{72.6117562, 85.7863012}, {59.583082, 85.3896153}, {50.1257271, 84.8566602}},
+{{50.1257271, 84.8566602}, {45.0042708, 84.5680481}, {40.4221343, 84.1941021}},
+{{40.4221343, 84.1941021}, {38.6138335, 84.0465275}, {39.3498335, 84.2964765}},
+{{39.3498335, 84.2964765}, {42.6031074, 85.4013033}, {43.6650027, 86.8548971}},
+{{43.6650027, 86.8548971}, {45.661047, 89.587217}, {40.6969416, 93.4977145}},
+</div>
+
+<div id="cubic5x4x">
+{{72.6117562, 85.7863012}, {10.3637705, 83.8910282}, {56.5110395, 81.0400843}, {40.6969416, 93.4977145}},
+{{72.6117562, 85.7863012}, {57.6253009, 85.3070124}, {50.1257271, 84.8566602}},
+{{50.1257271, 84.8566602}, {42.6261532, 84.4063079}, {40.4221343, 84.1941021}},
+{{40.4221343, 84.1941021}, {37.9777583, 83.9471466}, {39.3498335, 84.2964765}},
+{{39.3498335, 84.2964765}, {41.4441267, 84.7344658}, {43.6650027, 86.8548971}},
+{{43.6650027, 86.8548971}, {45.8858786, 88.9753284}, {40.6969416, 93.4977145}},
+</div>
+
+<div id="cubic5x5">
+{{49.5466436, 30.4382438}, {75.5627334, 82.8610433}, {45.5550553, 43.8144668}, {89.743077, 11.8944428}},
+{{49.5466436, 30.4382438}, {54.1919031, 39.7985093}, {58.8653675, 50.331813}},
+{{58.8653675, 50.331813}, {61.2282341, 55.6573679}, {61.7133948, 56.0247083}},
+{{61.7133948, 56.0247083}, {62.1985554, 56.3920486}, {62.7466525, 53.2705356}},
+{{62.7466525, 53.2705356}, {64.4490529, 43.5750541}, {68.2227928, 36.1296005}},
+{{68.2227928, 36.1296005}, {75.1711506, 22.4207397}, {89.743077, 11.8944428}},
+</div>
+
+<div id="cubic5x5x">
+{{49.5466436, 30.4382438}, {75.5627334, 82.8610433}, {45.5550553, 43.8144668}, {89.743077, 11.8944428}},
+{{49.5466436, 30.4382438}, {56.3292139, 44.3383274}, {58.8653675, 50.331813}},
+{{58.8653675, 50.331813}, {61.106944, 55.5655329}, {61.7133948, 56.0247083}},
+{{61.7133948, 56.0247083}, {62.3198455, 56.4838837}, {62.7466525, 53.2705356}},
+{{62.7466525, 53.2705356}, {63.1064311, 47.7098594}, {68.2227928, 36.1296005}},
+{{68.2227928, 36.1296005}, {73.3391545, 24.5493417}, {89.743077, 11.8944428}},
+</div>
+
+<div id="cubic5x6">
+{{24.3042985, 82.344259}, {59.9615856, 74.3697725}, {32.7666043, 8.31767205}, {95.114078, 82.3081283}},
+{{24.3042985, 82.344259}, {33.0316107, 80.3924607}, {38.6135161, 73.199581}},
+{{38.6135161, 73.199581}, {41.8734961, 68.9987494}, {45.7986582, 59.6541823}},
+{{45.7986582, 59.6541823}, {49.7161349, 50.3279117}, {52.6598645, 48.50717}},
+{{52.6598645, 48.50717}, {57.3512738, 45.6054619}, {66.2796867, 52.394109}},
+{{66.2796867, 52.394109}, {76.3758191, 60.0706222}, {95.114078, 82.3081283}},
+</div>
+
+<div id="cubic5x6x">
+{{24.3042985, 82.344259}, {59.9615856, 74.3697725}, {32.7666043, 8.31767205}, {95.114078, 82.3081283}},
+{{24.3042985, 82.344259}, {33.965356, 79.8151924}, {38.6135161, 73.199581}},
+{{38.6135161, 73.199581}, {43.2616761, 66.5839696}, {45.7986582, 59.6541823}},
+{{45.7986582, 59.6541823}, {48.5966015, 51.6963295}, {52.6598645, 48.50717}},
+{{52.6598645, 48.50717}, {56.7231274, 45.3180106}, {66.2796867, 52.394109}},
+{{66.2796867, 52.394109}, {75.8362459, 59.4702074}, {95.114078, 82.3081283}},
+</div>
+
+<div id="cubic5x7">
+{{14.6365061, 95.7588134}, {18.3773411, 67.9719648}, {4.8126874, 86.837213}, {73.0391371, 68.7771361}},
+{{14.6365061, 95.7588134}, {15.2275148, 91.3688129}, {15.5044612, 85.7525859}},
+{{15.5044612, 85.7525859}, {15.7576453, 80.618239}, {16.904617, 79.6508905}},
+{{16.904617, 79.6508905}, {18.0515887, 78.683542}, {24.680235, 78.0137979}},
+{{24.680235, 78.0137979}, {33.9302732, 77.0791942}, {41.7459023, 75.745697}},
+{{41.7459023, 75.745697}, {55.7221394, 73.3610813}, {73.0391371, 68.7771361}},
+</div>
+
+<div id="cubic5x7x">
+{{14.6365061, 95.7588134}, {18.3773411, 67.9719648}, {4.8126874, 86.837213}, {73.0391371, 68.7771361}},
+{{14.6365061, 95.7588134}, {15.4253236, 89.2562084}, {15.5044612, 85.7525859}},
+{{15.5044612, 85.7525859}, {15.4709024, 80.8600761}, {16.904617, 79.6508905}},
+{{16.904617, 79.6508905}, {18.3383317, 78.4417048}, {24.680235, 78.0137979}},
+{{24.680235, 78.0137979}, {30.2055996, 77.5914828}, {41.7459023, 75.745697}},
+{{41.7459023, 75.745697}, {53.2862049, 73.8999113}, {73.0391371, 68.7771361}},
+</div>
+
+<div id="cubic5x8">
+{{11.3940197, 99.2884769}, {41.4314282, 38.0142946}, {6.25007991, 45.0930539}, {78.9565565, 22.8458219}},
+{{11.3940197, 99.2884769}, {17.9356966, 85.9439202}, {21.7417223, 74.0130457}},
+{{21.7417223, 74.0130457}, {23.9251358, 67.1686272}, {26.0700343, 57.3329391}},
+{{26.0700343, 57.3329391}, {28.2750992, 47.2213507}, {30.7090957, 43.764797}},
+{{30.7090957, 43.764797}, {34.2237869, 38.7735329}, {44.5059747, 34.4313542}},
+{{44.5059747, 34.4313542}, {53.4825656, 30.6405304}, {78.9565565, 22.8458219}},
+</div>
+
+<div id="cubic5x8x">
+{{11.3940197, 99.2884769}, {41.4314282, 38.0142946}, {6.25007991, 45.0930539}, {78.9565565, 22.8458219}},
+{{11.3940197, 99.2884769}, {18.6635437, 84.168545}, {21.7417223, 74.0130457}},
+{{21.7417223, 74.0130457}, {24.8199009, 63.8575464}, {26.0700343, 57.3329391}},
+{{26.0700343, 57.3329391}, {27.5370962, 48.6793447}, {30.7090957, 43.764797}},
+{{30.7090957, 43.764797}, {33.8810951, 38.8502494}, {44.5059747, 34.4313542}},
+{{44.5059747, 34.4313542}, {55.1308542, 30.012459}, {78.9565565, 22.8458219}},
+</div>
+
+<div id="cubic5x9">
+{{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441}, {80.392774, 61.3533852}},
+{{69.7292014, 38.6877352}, {57.2585085, 34.3784487}, {54.0073216, 37.8534623}},
+{{54.0073216, 37.8534623}, {51.2791269, 40.7694784}, {55.3644243, 48.2785885}},
+{{55.3644243, 48.2785885}, {59.0228346, 55.0030454}, {65.6488241, 61.3874162}},
+{{65.6488241, 61.3874162}, {72.4185069, 67.9102405}, {76.7088359, 68.6042477}},
+{{76.7088359, 68.6042477}, {81.6560742, 69.4045171}, {80.392774, 61.3533852}},
+</div>
+
+<div id="cubic5x9x">
+{{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441}, {80.392774, 61.3533852}},
+{{69.7292014, 38.6877352}, {56.5795552, 34.3837867}, {54.0073216, 37.8534623}},
+{{54.0073216, 37.8534623}, {51.4350879, 41.3231378}, {55.3644243, 48.2785885}},
+{{55.3644243, 48.2785885}, {59.2937606, 55.2340392}, {65.6488241, 61.3874162}},
+{{65.6488241, 61.3874162}, {72.0038877, 67.5407932}, {76.7088359, 68.6042477}},
+{{76.7088359, 68.6042477}, {81.413784, 69.6677022}, {80.392774, 61.3533852}},
+</div>
+
+<div id="cubic6x0">
+{{60.7765365, 71.2493073}, {87.1078942, 22.3776699}, {1.49747543, 68.0695699}, {45.2619466, 17.5360766}},
+{{60.7765365, 71.2493073}, {66.8034381, 60.063232}, {65.7606554, 53.9755229}},
+{{65.7606554, 53.9755229}, {64.9026034, 48.9662616}, {59.0403561, 46.9365029}},
+{{59.0403561, 46.9365029}, {55.5624487, 45.7323037}, {47.0319968, 45.0593685}},
+{{47.0319968, 45.0593685}, {38.6438055, 44.3976557}, {35.915024, 43.1681828}},
+{{35.915024, 43.1681828}, {31.4270492, 41.1460923}, {32.9921574, 35.8381417}},
+{{32.9921574, 35.8381417}, {34.8405988, 29.5692874}, {45.2619466, 17.5360766}},
+</div>
+
+<div id="cubic6x0x">
+{{60.7765365, 71.2493073}, {87.1078942, 22.3776699}, {1.49747543, 68.0695699}, {45.2619466, 17.5360766}},
+{{60.7765365, 71.2493073}, {66.9967453, 59.4196142}, {65.7606554, 53.9755229}},
+{{65.7606554, 53.9755229}, {64.5245656, 48.5314316}, {59.0403561, 46.9365029}},
+{{59.0403561, 46.9365029}, {53.5561467, 45.3415741}, {47.0319968, 45.0593685}},
+{{47.0319968, 45.0593685}, {40.2998025, 44.7818435}, {35.915024, 43.1681828}},
+{{35.915024, 43.1681828}, {31.5302455, 41.5545222}, {32.9921574, 35.8381417}},
+{{32.9921574, 35.8381417}, {34.4540694, 30.1217612}, {45.2619466, 17.5360766}},
+</div>
+
+<div id="cubic6x1">
+{{7.56463181, 38.7667716}, {53.1298274, 53.009038}, {22.9012888, 1.96013199}, {43.9383991, 72.6733402}},
+{{7.56463181, 38.7667716}, {18.0499753, 42.0441646}, {24.9041761, 41.2832621}},
+{{24.9041761, 41.2832621}, {30.0084481, 40.7166236}, {32.7855974, 37.9676099}},
+{{32.7855974, 37.9676099}, {34.25762, 36.5105005}, {35.0000192, 34.4010014}},
+{{35.0000192, 34.4010014}, {35.1477005, 33.9813707}, {35.2051475, 34.7029855}},
+{{35.2051475, 34.7029855}, {35.5299907, 38.7834725}, {36.7160087, 44.7150792}},
+{{36.7160087, 44.7150792}, {38.9607709, 55.9417619}, {43.9383991, 72.6733402}},
+</div>
+
+<div id="cubic6x1x">
+{{7.56463181, 38.7667716}, {53.1298274, 53.009038}, {22.9012888, 1.96013199}, {43.9383991, 72.6733402}},
+{{7.56463181, 38.7667716}, {19.0728251, 42.1807008}, {24.9041761, 41.2832621}},
+{{24.9041761, 41.2832621}, {30.7355271, 40.3858233}, {32.7855974, 37.9676099}},
+{{32.7855974, 37.9676099}, {34.8356678, 35.5493964}, {35.0000192, 34.4010014}},
+{{35.0000192, 34.4010014}, {35.1702591, 33.6960593}, {35.2051475, 34.7029855}},
+{{35.2051475, 34.7029855}, {35.1391248, 36.1152585}, {36.7160087, 44.7150792}},
+{{36.7160087, 44.7150792}, {38.2928925, 53.3148999}, {43.9383991, 72.6733402}},
+</div>
+
+<div id="cubic6x2">
+{{53.4808373, 52.4330519}, {42.3039286, 2.12741392}, {55.4457253, 76.3045082}, {49.8689114, 46.7937026}},
+{{53.4808373, 52.4330519}, {50.9719376, 41.1408598}, {49.8115514, 36.9274013}},
+{{49.8115514, 36.9274013}, {48.82027, 33.3279765}, {48.8115145, 34.8928161}},
+{{48.8115145, 34.8928161}, {48.8045445, 36.1385203}, {49.4292469, 40.7546742}},
+{{49.4292469, 40.7546742}, {49.7879548, 43.4052983}, {50.613269, 48.9383534}},
+{{50.613269, 48.9383534}, {51.3518777, 53.8901197}, {51.31236, 53.9808933}},
+{{51.31236, 53.9808933}, {51.2529165, 54.1174374}, {49.8689114, 46.7937026}},
+</div>
+
+<div id="cubic6x2x">
+{{53.4808373, 52.4330519}, {42.3039286, 2.12741392}, {55.4457253, 76.3045082}, {49.8689114, 46.7937026}},
+{{53.4808373, 52.4330519}, {50.8474472, 40.6156325}, {49.8115514, 36.9274013}},
+{{49.8115514, 36.9274013}, {48.7756556, 33.2391701}, {48.8115145, 34.8928161}},
+{{48.8115145, 34.8928161}, {48.8473733, 36.5464621}, {49.4292469, 40.7546742}},
+{{49.4292469, 40.7546742}, {50.0111204, 44.9628863}, {50.613269, 48.9383534}},
+{{50.613269, 48.9383534}, {51.3082301, 53.5085713}, {51.31236, 53.9808933}},
+{{51.31236, 53.9808933}, {51.31649, 54.4532153}, {49.8689114, 46.7937026}},
+</div>
+
+<div id="cubic6x3">
+{{30.270176, 50.8484091}, {9.21238377, 32.534054}, {99.8452993, 99.9447358}, {71.1751053, 39.994736}},
+{{30.270176, 50.8484091}, {26.1998702, 47.3083881}, {27.3542845, 47.6109361}},
+{{27.3542845, 47.6109361}, {28.1421178, 47.8174109}, {33.7377536, 50.9254737}},
+{{33.7377536, 50.9254737}, {43.6710144, 56.4428448}, {49.5826034, 59.2306974}},
+{{49.5826034, 59.2306974}, {60.0794163, 64.1809007}, {66.5061178, 65.1608314}},
+{{66.5061178, 65.1608314}, {74.7232814, 66.4137682}, {76.404788, 61.2406021}},
+{{76.404788, 61.2406021}, {78.4000331, 55.1022173}, {71.1751053, 39.994736}},
+</div>
+
+<div id="cubic6x3x">
+{{30.270176, 50.8484091}, {9.21238377, 32.534054}, {99.8452993, 99.9447358}, {71.1751053, 39.994736}},
+{{30.270176, 50.8484091}, {26.0151068, 47.1560011}, {27.3542845, 47.6109361}},
+{{27.3542845, 47.6109361}, {28.6934622, 48.0658712}, {33.7377536, 50.9254737}},
+{{33.7377536, 50.9254737}, {40.3775737, 54.7374488}, {49.5826034, 59.2306974}},
+{{49.5826034, 59.2306974}, {58.787633, 63.723946}, {66.5061178, 65.1608314}},
+{{66.5061178, 65.1608314}, {74.2246025, 66.5977168}, {76.404788, 61.2406021}},
+{{76.404788, 61.2406021}, {78.5849735, 55.8834875}, {71.1751053, 39.994736}},
+</div>
+
+<div id="cubic6x4">
+{{52.3256249, 36.7777584}, {23.7859194, 69.9470399}, {99.9000587, 20.2858463}, {44.2180221, 72.2977287}},
+{{52.3256249, 36.7777584}, {46.323817, 43.7531512}, {45.7451821, 46.7544892}},
+{{45.7451821, 46.7544892}, {45.2655359, 49.2423797}, {48.551811, 49.2476269}},
+{{48.551811, 49.2476269}, {50.5144448, 49.2507606}, {55.9087216, 48.0313516}},
+{{55.9087216, 48.0313516}, {62.3329105, 46.5791247}, {63.9943433, 47.0044226}},
+{{63.9943433, 47.0044226}, {66.7468982, 47.7090289}, {62.987381, 52.8381772}},
+{{62.987381, 52.8381772}, {58.5075376, 58.9500739}, {44.2180221, 72.2977287}},
+</div>
+
+<div id="cubic6x4x">
+{{52.3256249, 36.7777584}, {23.7859194, 69.9470399}, {99.9000587, 20.2858463}, {44.2180221, 72.2977287}},
+{{52.3256249, 36.7777584}, {46.0840368, 44.1087946}, {45.7451821, 46.7544892}},
+{{45.7451821, 46.7544892}, {45.4063273, 49.4001838}, {48.551811, 49.2476269}},
+{{48.551811, 49.2476269}, {51.6972946, 49.09507}, {55.9087216, 48.0313516}},
+{{55.9087216, 48.0313516}, {61.1409519, 46.6483554}, {63.9943433, 47.0044226}},
+{{63.9943433, 47.0044226}, {66.8477347, 47.3604898}, {62.987381, 52.8381772}},
+{{62.987381, 52.8381772}, {59.1270273, 58.3158645}, {44.2180221, 72.2977287}},
+</div>
+
+<div id="cubic6x5">
+{{42.9059103, 19.6341859}, {91.762872, 58.5903164}, {27.4474096, 8.61261101}, {52.1532298, 39.3337672}},
+{{42.9059103, 19.6341859}, {54.1145994, 28.5714415}, {58.004639, 31.7917065}},
+{{58.004639, 31.7917065}, {62.019725, 35.1154878}, {61.7728162, 35.2682181}},
+{{61.7728162, 35.2682181}, {61.6064162, 35.3711481}, {58.4041375, 33.5820691}},
+{{58.4041375, 33.5820691}, {53.244257, 30.6992989}, {50.8183004, 29.6863137}},
+{{50.8183004, 29.6863137}, {46.5331956, 27.8970204}, {46.2915313, 29.5538521}},
+{{46.2915313, 29.5538521}, {45.9839754, 31.662432}, {52.1532298, 39.3337672}},
+</div>
+
+<div id="cubic6x5x">
+{{42.9059103, 19.6341859}, {91.762872, 58.5903164}, {27.4474096, 8.61261101}, {52.1532298, 39.3337672}},
+{{42.9059103, 19.6341859}, {53.8121244, 28.322992}, {58.004639, 31.7917065}},
+{{58.004639, 31.7917065}, {62.1971535, 35.260421}, {61.7728162, 35.2682181}},
+{{61.7728162, 35.2682181}, {61.3484789, 35.2760153}, {58.4041375, 33.5820691}},
+{{58.4041375, 33.5820691}, {54.7626269, 31.4620033}, {50.8183004, 29.6863137}},
+{{50.8183004, 29.6863137}, {46.8739739, 27.9106241}, {46.2915313, 29.5538521}},
+{{46.2915313, 29.5538521}, {45.7090887, 31.1970802}, {52.1532298, 39.3337672}},
+</div>
+
+<div id="cubic6x6">
+{{73.4375576, 65.030414}, {66.1679208, 84.2450892}, {7.2134248, 36.0306381}, {66.9352454, 80.6694031}},
+{{73.4375576, 65.030414}, {71.5866063, 69.9227391}, {65.0188473, 69.7835224}},
+{{65.0188473, 69.7835224}, {60.0105733, 69.677362}, {52.4355896, 66.6514651}},
+{{52.4355896, 66.6514651}, {48.2960051, 64.9978699}, {42.4281173, 61.975806}},
+{{42.4281173, 61.975806}, {40.1794784, 60.817718}, {40.5562709, 61.1722188}},
+{{40.5562709, 61.1722188}, {40.9330633, 61.5267196}, {45.4424567, 64.8118124}},
+{{45.4424567, 64.8118124}, {56.3732534, 72.774897}, {66.9352454, 80.6694031}},
+</div>
+
+<div id="cubic6x6x">
+{{73.4375576, 65.030414}, {66.1679208, 84.2450892}, {7.2134248, 36.0306381}, {66.9352454, 80.6694031}},
+{{73.4375576, 65.030414}, {71.1118808, 70.1709551}, {65.0188473, 69.7835224}},
+{{65.0188473, 69.7835224}, {58.9258137, 69.3960897}, {52.4355896, 66.6514651}},
+{{52.4355896, 66.6514651}, {45.9453656, 63.9068405}, {42.4281173, 61.975806}},
+{{42.4281173, 61.975806}, {40.0852803, 60.7290928}, {40.5562709, 61.1722188}},
+{{40.5562709, 61.1722188}, {41.0272614, 61.6153448}, {45.4424567, 64.8118124}},
+{{45.4424567, 64.8118124}, {51.3278432, 69.0492921}, {66.9352454, 80.6694031}},
+</div>
+
+<div id="cubic6x7">
+{{46.6695473, 75.0819435}, {2.22400357, 78.828021}, {62.548768, 57.1436195}, {12.8128845, 46.150303}},
+{{46.6695473, 75.0819435}, {36.8374139, 75.9106415}, {32.735691, 75.1220326}},
+{{32.735691, 75.1220326}, {29.1601585, 74.4345905}, {29.1402184, 72.2922743}},
+{{29.1402184, 72.2922743}, {29.1278273, 70.9609985}, {31.0504514, 67.4052326}},
+{{31.0504514, 67.4052326}, {33.5289159, 62.8214769}, {33.7777617, 60.3248048}},
+{{33.7777617, 60.3248048}, {34.2044612, 56.0437252}, {30.1210496, 52.8325143}},
+{{30.1210496, 52.8325143}, {25.0685705, 48.8592251}, {12.8128845, 46.150303}},
+</div>
+
+<div id="cubic6x7x">
+{{46.6695473, 75.0819435}, {2.22400357, 78.828021}, {62.548768, 57.1436195}, {12.8128845, 46.150303}},
+{{46.6695473, 75.0819435}, {36.5139384, 75.9210204}, {32.735691, 75.1220326}},
+{{32.735691, 75.1220326}, {28.9574435, 74.3230448}, {29.1402184, 72.2922743}},
+{{29.1402184, 72.2922743}, {29.3229933, 70.2615038}, {31.0504514, 67.4052326}},
+{{31.0504514, 67.4052326}, {33.1016834, 64.1207271}, {33.7777617, 60.3248048}},
+{{33.7777617, 60.3248048}, {34.45384, 56.5288825}, {30.1210496, 52.8325143}},
+{{30.1210496, 52.8325143}, {25.7882591, 49.1361461}, {12.8128845, 46.150303}},
+</div>
+
+<div id="cubic6x8">
+{{55.3467924, 13.540705}, {78.628494, 9.09824134}, {14.1422475, 88.7534345}, {78.9736115, 9.48816133}},
+{{55.3467924, 13.540705}, {60.6017653, 12.5379851}, {60.6776079, 17.0952733}},
+{{60.6776079, 17.0952733}, {60.7373059, 20.6824519}, {57.5541656, 27.8149349}},
+{{57.5541656, 27.8149349}, {55.7485732, 31.8607373}, {51.7778474, 39.2052874}},
+{{51.7778474, 39.2052874}, {48.7947857, 44.7229805}, {49.1759091, 44.7804774}},
+{{49.1759091, 44.7804774}, {49.7203899, 44.8626187}, {56.152876, 37.2125188}},
+{{56.152876, 37.2125188}, {61.4648441, 30.8950413}, {78.9736115, 9.48816133}},
+</div>
+
+<div id="cubic6x8x">
+{{55.3467924, 13.540705}, {78.628494, 9.09824134}, {14.1422475, 88.7534345}, {78.9736115, 9.48816133}},
+{{55.3467924, 13.540705}, {60.8509374, 12.7149155}, {60.6776079, 17.0952733}},
+{{60.6776079, 17.0952733}, {60.5042785, 21.475631}, {57.5541656, 27.8149349}},
+{{57.5541656, 27.8149349}, {54.6040527, 34.1542387}, {51.7778474, 39.2052874}},
+{{51.7778474, 39.2052874}, {48.8652599, 44.4020133}, {49.1759091, 44.7804774}},
+{{49.1759091, 44.7804774}, {49.4865584, 45.1589416}, {56.152876, 37.2125188}},
+{{56.152876, 37.2125188}, {62.8191937, 29.2660961}, {78.9736115, 9.48816133}},
+</div>
+
+<div id="cubic6x9">
+{{6.436938, 85.6170305}, {65.4323691, 21.8280372}, {63.8217439, 52.0501321}, {6.57091737, 37.4082543}},
+{{6.436938, 85.6170305}, {18.3889022, 72.6939321}, {29.5730232, 62.1640306}},
+{{29.5730232, 62.1640306}, {35.8331919, 56.2700528}, {44.0352491, 49.2100734}},
+{{44.0352491, 49.2100734}, {50.0664784, 44.0186496}, {50.0976641, 43.1465107}},
+{{50.0976641, 43.1465107}, {50.1288498, 42.2743718}, {44.2847347, 42.2329622}},
+{{44.2847347, 42.2329622}, {36.0580615, 42.1746705}, {29.7656368, 41.5603994}},
+{{29.7656368, 41.5603994}, {18.5096561, 40.4615824}, {6.57091737, 37.4082543}},
+</div>
+
+<div id="cubic6x9x">
+{{6.436938, 85.6170305}, {65.4323691, 21.8280372}, {63.8217439, 52.0501321}, {6.57091737, 37.4082543}},
+{{6.436938, 85.6170305}, {20.1874324, 70.8746109}, {29.5730232, 62.1640306}},
+{{29.5730232, 62.1640306}, {38.958614, 53.4534502}, {44.0352491, 49.2100734}},
+{{44.0352491, 49.2100734}, {50.058682, 44.2366844}, {50.0976641, 43.1465107}},
+{{50.0976641, 43.1465107}, {50.1366462, 42.0563371}, {44.2847347, 42.2329622}},
+{{44.2847347, 42.2329622}, {39.2093652, 42.3394209}, {29.7656368, 41.5603994}},
+{{29.7656368, 41.5603994}, {20.3219083, 40.7813779}, {6.57091737, 37.4082543}},
+</div>
+
+<div id="cubic7x0">
+{{47.0675449, 64.2273128}, {68.4467872, 85.1524572}, {57.3478562, 45.4193099}, {62.340955, 64.4298956}},
+{{47.0675449, 64.2273128}, {52.2209452, 69.2712543}, {55.5069225, 70.2618397}},
+{{55.5069225, 70.2618397}, {58.2344663, 71.0840808}, {59.6132451, 69.0985913}},
+{{59.6132451, 69.0985913}, {60.6680756, 67.5795987}, {60.901578, 64.4621115}},
+{{60.901578, 64.4621115}, {61.029698, 62.7515845}, {60.8869865, 60.0769443}},
+{{60.8869865, 60.0769443}, {60.8258684, 58.931493}, {60.9363129, 59.2030029}},
+{{60.9363129, 59.2030029}, {61.0467573, 59.4745129}, {61.7705423, 62.2490239}},
+{{61.7705423, 62.2490239}, {62.1209644, 63.5923097}, {62.340955, 64.4298956}},
+</div>
+
+<div id="cubic7x0x">
+{{47.0675449, 64.2273128}, {68.4467872, 85.1524572}, {57.3478562, 45.4193099}, {62.340955, 64.4298956}},
+{{47.0675449, 64.2273128}, {52.5598806, 69.5095881}, {55.5069225, 70.2618397}},
+{{55.5069225, 70.2618397}, {58.4539644, 71.0140913}, {59.6132451, 69.0985913}},
+{{59.6132451, 69.0985913}, {60.7725259, 67.1830912}, {60.901578, 64.4621115}},
+{{60.901578, 64.4621115}, {61.0306302, 61.7411317}, {60.8869865, 60.0769443}},
+{{60.8869865, 60.0769443}, {60.7982573, 58.8636155}, {60.9363129, 59.2030029}},
+{{60.9363129, 59.2030029}, {61.0743684, 59.5423904}, {61.7705423, 62.2490239}},
+{{61.7705423, 62.2490239}, {62.0120077, 63.1760698}, {62.340955, 64.4298956}},
+</div>
+
+<div id="cubic7x1">
+{{44.6639438, 66.9040647}, {56.6149349, 27.2102873}, {23.2993796, 92.6723405}, {44.026369, 51.1832799}},
+{{44.6639438, 66.9040647}, {47.1627908, 58.6044453}, {47.3201686, 55.4528932}},
+{{47.3201686, 55.4528932}, {47.4533435, 52.7860125}, {45.9070214, 53.8259442}},
+{{45.9070214, 53.8259442}, {44.6909451, 54.6437792}, {42.4058247, 57.7914576}},
+{{42.4058247, 57.7914576}, {41.1108979, 59.5751769}, {38.7979012, 63.1176732}},
+{{38.7979012, 63.1176732}, {37.2505608, 65.4875199}, {37.097116, 65.5770977}},
+{{37.097116, 65.5770977}, {36.8720355, 65.7084948}, {38.3585854, 62.6270533}},
+{{38.3585854, 62.6270533}, {39.7438195, 59.7556272}, {44.026369, 51.1832799}},
+</div>
+
+<div id="cubic7x1x">
+{{44.6639438, 66.9040647}, {56.6149349, 27.2102873}, {23.2993796, 92.6723405}, {44.026369, 51.1832799}},
+{{44.6639438, 66.9040647}, {47.2570645, 58.1934532}, {47.3201686, 55.4528932}},
+{{47.3201686, 55.4528932}, {47.3832726, 52.7123331}, {45.9070214, 53.8259442}},
+{{45.9070214, 53.8259442}, {44.4307701, 54.9395554}, {42.4058247, 57.7914576}},
+{{42.4058247, 57.7914576}, {40.3808794, 60.6433599}, {38.7979012, 63.1176732}},
+{{38.7979012, 63.1176732}, {37.3874524, 65.3142201}, {37.097116, 65.5770977}},
+{{37.097116, 65.5770977}, {36.8067796, 65.8399752}, {38.3585854, 62.6270533}},
+{{38.3585854, 62.6270533}, {39.9103912, 59.4141313}, {44.026369, 51.1832799}},
+</div>
+
+<div id="cubic7x2">
+{{8.53545089, 55.3230609}, {14.6846658, 5.17757498}, {19.5026836, 81.6040195}, {18.7564744, 40.0648544}},
+{{8.53545089, 55.3230609}, {9.80938657, 44.9343975}, {11.1496907, 40.6303968}},
+{{11.1496907, 40.6303968}, {12.2999484, 36.9366756}, {13.510145, 37.7040519}},
+{{13.510145, 37.7040519}, {14.4789672, 38.3183745}, {15.5359245, 41.871143}},
+{{15.5359245, 41.871143}, {16.1464176, 43.9232035}, {17.1461401, 48.4587871}},
+{{17.1461401, 48.4587871}, {17.9290951, 52.0109309}, {18.2017805, 52.6665884}},
+{{18.2017805, 52.6665884}, {18.6299694, 53.6961462}, {18.7604053, 51.1306715}},
+{{18.7604053, 51.1306715}, {18.904388, 48.2987505}, {18.7564744, 40.0648544}},
+</div>
+
+<div id="cubic7x2x">
+{{8.53545089, 55.3230609}, {14.6846658, 5.17757498}, {19.5026836, 81.6040195}, {18.7564744, 40.0648544}},
+{{8.53545089, 55.3230609}, {9.89590602, 44.4510387}, {11.1496907, 40.6303968}},
+{{11.1496907, 40.6303968}, {12.4034754, 36.8097549}, {13.510145, 37.7040519}},
+{{13.510145, 37.7040519}, {14.6168146, 38.5983488}, {15.5359245, 41.871143}},
+{{15.5359245, 41.871143}, {16.4550345, 45.1439372}, {17.1461401, 48.4587871}},
+{{17.1461401, 48.4587871}, {17.7900216, 51.5253445}, {18.2017805, 52.6665884}},
+{{18.2017805, 52.6665884}, {18.6135393, 53.8078322}, {18.7604053, 51.1306715}},
+{{18.7604053, 51.1306715}, {18.9072713, 48.4535108}, {18.7564744, 40.0648544}},
+</div>
+
+<div id="cubic7x3">
+{{77.2429303, 21.9290386}, {61.3518447, 40.4530391}, {94.2286334, 0.642292155}, {95.0042533, 36.4855481}},
+{{77.2429303, 21.9290386}, {76.5648343, 22.7194849}, {75.4687566, 23.992131}},
+{{75.4687566, 23.992131}, {73.1344126, 26.702517}, {72.9524614, 27.0039848}},
+{{72.9524614, 27.0039848}, {72.7705102, 27.3054527}, {74.013147, 26.4038735}},
+{{74.013147, 26.4038735}, {76.9396956, 24.2805538}, {79.071521, 23.1293275}},
+{{79.071521, 23.1293275}, {82.9580525, 21.0305265}, {85.9547875, 20.9037075}},
+{{85.9547875, 20.9037075}, {89.8728448, 20.737899}, {92.1150103, 23.9485892}},
+{{92.1150103, 23.9485892}, {94.8166786, 27.8172686}, {95.0042533, 36.4855481}},
+</div>
+
+<div id="cubic7x3x">
+{{77.2429303, 21.9290386}, {61.3518447, 40.4530391}, {94.2286334, 0.642292155}, {95.0042533, 36.4855481}},
+{{77.2429303, 21.9290386}, {76.2273572, 23.1121054}, {75.4687566, 23.992131}},
+{{75.4687566, 23.992131}, {73.1799004, 26.6271501}, {72.9524614, 27.0039848}},
+{{72.9524614, 27.0039848}, {72.7250224, 27.3808196}, {74.013147, 26.4038735}},
+{{74.013147, 26.4038735}, {75.7676189, 25.0320659}, {79.071521, 23.1293275}},
+{{79.071521, 23.1293275}, {82.3754232, 21.226589}, {85.9547875, 20.9037075}},
+{{85.9547875, 20.9037075}, {89.5341519, 20.580826}, {92.1150103, 23.9485892}},
+{{92.1150103, 23.9485892}, {94.6958688, 27.3163524}, {95.0042533, 36.4855481}},
+</div>
+
+<div id="cubic7x4">
+{{85.1880251, 55.1384624}, {12.1381459, 5.8187271}, {95.3464197, 87.2766355}, {58.4136199, 57.7104629}},
+{{85.1880251, 55.1384624}, {69.4954757, 44.5436142}, {61.7319269, 40.861468}},
+{{61.7319269, 40.861468}, {55.009991, 37.6733448}, {54.1223283, 39.6260005}},
+{{54.1223283, 39.6260005}, {53.4043154, 41.2054652}, {56.5289517, 46.3315706}},
+{{56.5289517, 46.3315706}, {58.3527212, 49.323546}, {63.1215192, 55.8776895}},
+{{63.1215192, 55.8776895}, {66.7797089, 60.9054346}, {67.68997, 62.4640064}},
+{{67.68997, 62.4640064}, {69.1578236, 64.9773019}, {67.3518779, 64.1520255}},
+{{67.3518779, 64.1520255}, {65.2740366, 63.2024988}, {58.4136199, 57.7104629}},
+</div>
+
+<div id="cubic7x4x">
+{{85.1880251, 55.1384624}, {12.1381459, 5.8187271}, {95.3464197, 87.2766355}, {58.4136199, 57.7104629}},
+{{85.1880251, 55.1384624}, {68.7695663, 44.1020224}, {61.7319269, 40.861468}},
+{{61.7319269, 40.861468}, {54.6942874, 37.6209137}, {54.1223283, 39.6260005}},
+{{54.1223283, 39.6260005}, {53.5503693, 41.6310872}, {56.5289517, 46.3315706}},
+{{56.5289517, 46.3315706}, {59.5075341, 51.032054}, {63.1215192, 55.8776895}},
+{{63.1215192, 55.8776895}, {66.1706775, 59.9915119}, {67.68997, 62.4640064}},
+{{67.68997, 62.4640064}, {69.2092626, 64.9365008}, {67.3518779, 64.1520255}},
+{{67.3518779, 64.1520255}, {65.4944933, 63.3675501}, {58.4136199, 57.7104629}},
+</div>
+
+<div id="cubic7x5">
+{{8.65591127, 79.9006976}, {91.0247206, 52.4786449}, {8.58452539, 80.1182901}, {48.1023732, 56.5861463}},
+{{8.65591127, 79.9006976}, {10.1949106, 79.3883372}, {13.1021747, 78.4205896}},
+{{13.1021747, 78.4205896}, {33.3914306, 71.6668594}, {38.6134953, 69.8848279}},
+{{38.6134953, 69.8848279}, {45.9243859, 67.3899838}, {46.7629899, 66.8658296}},
+{{46.7629899, 66.8658296}, {47.3619928, 66.4914338}, {44.5359397, 66.7758818}},
+{{44.5359397, 66.7758818}, {40.3498242, 67.197223}, {38.6707421, 67.0021236}},
+{{38.6707421, 67.0021236}, {35.6941333, 66.6562593}, {37.1605001, 64.6054153}},
+{{37.1605001, 64.6054153}, {39.032712, 61.9869609}, {48.1023732, 56.5861463}},
+</div>
+
+<div id="cubic7x5x">
+{{8.65591127, 79.9006976}, {91.0247206, 52.4786449}, {8.58452539, 80.1182901}, {48.1023732, 56.5861463}},
+{{8.65591127, 79.9006976}, {10.9639426, 79.1323302}, {13.1021747, 78.4205896}},
+{{13.1021747, 78.4205896}, {31.0714516, 72.4500538}, {38.6134953, 69.8848279}},
+{{38.6134953, 69.8848279}, {46.155539, 67.319602}, {46.7629899, 66.8658296}},
+{{46.7629899, 66.8658296}, {47.3704409, 66.4120572}, {44.5359397, 66.7758818}},
+{{44.5359397, 66.7758818}, {41.5267469, 67.1697889}, {38.6707421, 67.0021236}},
+{{38.6707421, 67.0021236}, {35.8147372, 66.8344583}, {37.1605001, 64.6054153}},
+{{37.1605001, 64.6054153}, {38.5062629, 62.3763723}, {48.1023732, 56.5861463}},
+</div>
+
+<div id="cubic7x6">
+{{42.8441148, 81.0382013}, {9.0486696, 80.9900212}, {99.2855478, 92.2020003}, {39.0193165, 97.6524087}},
+{{42.8441148, 81.0382013}, {36.5292256, 81.0291985}, {35.3205259, 81.5652507}},
+{{35.3205259, 81.5652507}, {34.274399, 82.0292026}, {36.9288084, 83.0293311}},
+{{36.9288084, 83.0293311}, {38.5699459, 83.647679}, {43.916545, 85.1977854}},
+{{43.916545, 85.1977854}, {50.735588, 87.1747884}, {53.7318941, 88.2602772}},
+{{53.7318941, 88.2602772}, {58.8545207, 90.11608}, {59.9920782, 91.5927712}},
+{{59.9920782, 91.5927712}, {61.3953813, 93.4144333}, {56.9901885, 94.8414282}},
+{{56.9901885, 94.8414282}, {51.9120881, 96.4864013}, {39.0193165, 97.6524087}},
+</div>
+
+<div id="cubic7x6x">
+{{42.8441148, 81.0382013}, {9.0486696, 80.9900212}, {99.2855478, 92.2020003}, {39.0193165, 97.6524087}},
+{{42.8441148, 81.0382013}, {36.3303003, 81.0383861}, {35.3205259, 81.5652507}},
+{{35.3205259, 81.5652507}, {34.3107514, 82.0921153}, {36.9288084, 83.0293311}},
+{{36.9288084, 83.0293311}, {39.5468653, 83.9665469}, {43.916545, 85.1977854}},
+{{43.916545, 85.1977854}, {48.9996472, 86.6173008}, {53.7318941, 88.2602772}},
+{{53.7318941, 88.2602772}, {58.4641409, 89.9032535}, {59.9920782, 91.5927712}},
+{{59.9920782, 91.5927712}, {61.5200154, 93.2822889}, {56.9901885, 94.8414282}},
+{{56.9901885, 94.8414282}, {52.4603617, 96.4005675}, {39.0193165, 97.6524087}},
+</div>
+
+<div id="cubic7x7">
+{{70.2955832, 57.867012}, {70.8709129, 27.4331047}, {68.1912432, 90.2241446}, {97.1991291, 25.763215}},
+{{70.2955832, 57.867012}, {70.3286385, 56.1184463}, {70.3691418, 53.5027125}},
+{{70.3691418, 53.5027125}, {70.4294268, 49.6094598}, {70.5008735, 49.1586526}},
+{{70.5008735, 49.1586526}, {70.5723202, 48.7078455}, {70.9407155, 49.8962556}},
+{{70.9407155, 49.8962556}, {71.8329974, 52.7746761}, {72.7964345, 54.1820074}},
+{{72.7964345, 54.1820074}, {74.5546706, 56.7503333}, {77.0780289, 56.0896348}},
+{{77.0780289, 56.0896348}, {80.3799099, 55.2250934}, {84.85557, 48.8673125}},
+{{84.85557, 48.8673125}, {90.2513448, 41.2024868}, {97.1991291, 25.763215}},
+</div>
+
+<div id="cubic7x7x">
+{{70.2955832, 57.867012}, {70.8709129, 27.4331047}, {68.1912432, 90.2241446}, {97.1991291, 25.763215}},
+{{70.2955832, 57.867012}, {70.3435094, 55.2546173}, {70.3691418, 53.5027125}},
+{{70.3691418, 53.5027125}, {70.4115651, 49.7221615}, {70.5008735, 49.1586526}},
+{{70.5008735, 49.1586526}, {70.5901819, 48.5951437}, {70.9407155, 49.8962556}},
+{{70.9407155, 49.8962556}, {71.3958651, 51.7896844}, {72.7964345, 54.1820074}},
+{{72.7964345, 54.1820074}, {74.1970039, 56.5743304}, {77.0780289, 56.0896348}},
+{{77.0780289, 56.0896348}, {79.9590538, 55.6049393}, {84.85557, 48.8673125}},
+{{84.85557, 48.8673125}, {89.7520861, 42.1296858}, {97.1991291, 25.763215}},
+</div>
+
+<div id="cubic7x8">
+{{50.528201, 27.4745214}, {64.2810473, 71.5620589}, {43.5236709, 2.33669765}, {72.8774712, 51.6581711}},
+{{50.528201, 27.4745214}, {53.3761101, 36.6040713}, {54.38742, 39.6099548}},
+{{54.38742, 39.6099548}, {55.5064406, 42.9359834}, {55.7720854, 42.9677817}},
+{{55.7720854, 42.9677817}, {55.9546136, 42.9896307}, {55.8827854, 40.8375726}},
+{{55.8827854, 40.8375726}, {55.7725733, 37.5354848}, {55.9802468, 35.9707481}},
+{{55.9802468, 35.9707481}, {56.3474228, 33.2042239}, {57.7009449, 33.0167369}},
+{{57.7009449, 33.0167369}, {59.424989, 32.7779259}, {62.7612346, 36.6782932}},
+{{62.7612346, 36.6782932}, {66.7088662, 41.293425}, {72.8774712, 51.6581711}},
+</div>
+
+<div id="cubic7x8x">
+{{50.528201, 27.4745214}, {64.2810473, 71.5620589}, {43.5236709, 2.33669765}, {72.8774712, 51.6581711}},
+{{50.528201, 27.4745214}, {53.2265224, 36.1478361}, {54.38742, 39.6099548}},
+{{54.38742, 39.6099548}, {55.5483175, 43.0720735}, {55.7720854, 42.9677817}},
+{{55.7720854, 42.9677817}, {55.9958532, 42.8634898}, {55.8827854, 40.8375726}},
+{{55.8827854, 40.8375726}, {55.7402514, 38.5138013}, {55.9802468, 35.9707481}},
+{{55.9802468, 35.9707481}, {56.2202423, 33.4276949}, {57.7009449, 33.0167369}},
+{{57.7009449, 33.0167369}, {59.1816474, 32.6057789}, {62.7612346, 36.6782932}},
+{{62.7612346, 36.6782932}, {66.3408218, 40.7508075}, {72.8774712, 51.6581711}},
+</div>
+
+<div id="cubic7x9">
+{{30.3413925, 47.7835835}, {98.6874047, 39.210338}, {8.15117029, 96.7190508}, {57.0872154, 64.8290379}},
+{{30.3413925, 47.7835835}, {46.0782555, 45.8095694}, {52.5540659, 48.2453346}},
+{{52.5540659, 48.2453346}, {57.9511897, 50.2753703}, {56.8130484, 55.3244869}},
+{{56.8130484, 55.3244869}, {55.9369414, 59.2111449}, {51.1961195, 64.811489}},
+{{51.1961195, 64.811489}, {48.5753558, 67.9074035}, {43.7810591, 72.4967897}},
+{{43.7810591, 72.4967897}, {41.2648644, 74.905441}, {42.230691, 74.4021224}},
+{{42.230691, 74.4021224}, {43.1965176, 73.8988039}, {51.5076718, 68.470241}},
+{{51.5076718, 68.470241}, {54.9756852, 66.2050527}, {57.0872154, 64.8290379}},
+</div>
+
+<div id="cubic7x9x">
+{{30.3413925, 47.7835835}, {98.6874047, 39.210338}, {8.15117029, 96.7190508}, {57.0872154, 64.8290379}},
+{{30.3413925, 47.7835835}, {46.9458744, 45.8339148}, {52.5540659, 48.2453346}},
+{{52.5540659, 48.2453346}, {58.1622574, 50.6567543}, {56.8130484, 55.3244869}},
+{{56.8130484, 55.3244869}, {55.4638393, 59.9922194}, {51.1961195, 64.811489}},
+{{51.1961195, 64.811489}, {46.9283998, 69.6307587}, {43.7810591, 72.4967897}},
+{{43.7810591, 72.4967897}, {41.0234077, 75.0312707}, {42.230691, 74.4021224}},
+{{42.230691, 74.4021224}, {43.4379742, 73.7729742}, {51.5076718, 68.470241}},
+{{51.5076718, 68.470241}, {53.9259122, 66.8899375}, {57.0872154, 64.8290379}},
+</div>
+
+<div id="cubic8x0">
+{{42.5967063, 22.8420382}, {6.13525533, 15.2363991}, {78.1588409, 15.6382141}, {31.4640028, 15.4944166}},
+{{42.5967063, 22.8420382}, {34.7914244, 21.2139034}, {32.6593413, 19.878761}},
+{{32.6593413, 19.878761}, {30.8695713, 18.7579803}, {33.1023831, 17.8591237}},
+{{33.1023831, 17.8591237}, {34.8391625, 17.1599535}, {39.0195021, 16.5984277}},
+{{39.0195021, 16.5984277}, {41.3590736, 16.2841638}, {45.5043686, 15.9119743}},
+{{45.5043686, 15.9119743}, {47.7538437, 15.7100029}, {47.8024311, 15.6545014}},
+{{47.8024311, 15.6545014}, {47.8704535, 15.5767993}, {44.9932779, 15.5487509}},
+{{44.9932779, 15.5487509}, {42.9381525, 15.5287163}, {34.6690635, 15.5040796}},
+{{34.6690635, 15.5040796}, {32.6017407, 15.4979203}, {31.4640028, 15.4944166}},
+</div>
+
+<div id="cubic8x0x">
+{{42.5967063, 22.8420382}, {6.13525533, 15.2363991}, {78.1588409, 15.6382141}, {31.4640028, 15.4944166}},
+{{42.5967063, 22.8420382}, {34.4196308, 21.1014023}, {32.6593413, 19.878761}},
+{{32.6593413, 19.878761}, {30.8990517, 18.6561197}, {33.1023831, 17.8591237}},
+{{33.1023831, 17.8591237}, {35.3057145, 17.0621277}, {39.0195021, 16.5984277}},
+{{39.0195021, 16.5984277}, {42.7332897, 16.1347277}, {45.5043686, 15.9119743}},
+{{45.5043686, 15.9119743}, {47.6292231, 15.7339768}, {47.8024311, 15.6545014}},
+{{47.8024311, 15.6545014}, {47.9756391, 15.575026}, {44.9932779, 15.5487509}},
+{{44.9932779, 15.5487509}, {42.0109167, 15.5224759}, {34.6690635, 15.5040796}},
+{{34.6690635, 15.5040796}, {33.169788, 15.4996412}, {31.4640028, 15.4944166}},
+</div>
+
+<div id="cubic8x1">
+{{29.0323957, 47.4745379}, {4.55778772, 2.73824763}, {21.7278637, 80.2053625}, {11.5269861, 34.0549996}},
+{{29.0323957, 47.4745379}, {23.6693619, 37.6716337}, {20.3221199, 34.4936205}},
+{{20.3221199, 34.4936205}, {17.5126817, 31.826221}, {16.162445, 33.8631071}},
+{{16.162445, 33.8631071}, {15.1123784, 35.4471745}, {14.9546458, 39.8886357}},
+{{14.9546458, 39.8886357}, {14.8663892, 42.3737787}, {15.0999972, 46.8758445}},
+{{15.0999972, 46.8758445}, {15.2259779, 49.3037298}, {15.1224749, 49.3309043}},
+{{15.1224749, 49.3309043}, {14.9775707, 49.3689487}, {14.2215808, 46.1220869}},
+{{14.2215808, 46.1220869}, {13.6815881, 43.8028999}, {11.62512, 34.4989774}},
+{{11.62512, 34.4989774}, {11.559983, 34.2042829}, {11.5269861, 34.0549996}},
+</div>
+
+<div id="cubic8x1x">
+{{29.0323957, 47.4745379}, {4.55778772, 2.73824763}, {21.7278637, 80.2053625}, {11.5269861, 34.0549996}},
+{{29.0323957, 47.4745379}, {23.339767, 37.184683}, {20.3221199, 34.4936205}},
+{{20.3221199, 34.4936205}, {17.3044729, 31.802558}, {16.162445, 33.8631071}},
+{{16.162445, 33.8631071}, {15.0204171, 35.9236561}, {14.9546458, 39.8886357}},
+{{14.9546458, 39.8886357}, {14.8888745, 43.8536153}, {15.0999972, 46.8758445}},
+{{15.0999972, 46.8758445}, {15.2455546, 49.1755419}, {15.1224749, 49.3309043}},
+{{15.1224749, 49.3309043}, {14.9993952, 49.4862668}, {14.2215808, 46.1220869}},
+{{14.2215808, 46.1220869}, {13.4437665, 42.757907}, {11.62512, 34.4989774}},
+{{11.62512, 34.4989774}, {11.5764809, 34.2789225}, {11.5269861, 34.0549996}},
+</div>
+
+<div id="cubic8x2">
+{{30.5187597, 28.7944151}, {47.7341773, 68.3182353}, {24.579915, 14.6321317}, {22.237118, 39.2417454}},
+{{30.5187597, 28.7944151}, {30.823715, 29.4945431}, {31.3959629, 30.8080078}},
+{{31.3959629, 30.8080078}, {34.9281707, 38.9153861}, {35.7821411, 40.9504208}},
+{{35.7821411, 40.9504208}, {36.9776996, 43.7994693}, {36.8148424, 43.845334}},
+{{36.8148424, 43.845334}, {36.6985159, 43.8780945}, {35.3792396, 41.9741019}},
+{{35.3792396, 41.9741019}, {33.3589087, 39.0583404}, {32.0725075, 37.4554574}},
+{{32.0725075, 37.4554574}, {29.7893226, 34.6105606}, {28.0338583, 33.4024856}},
+{{28.0338583, 33.4024856}, {25.7901854, 31.8584352}, {24.3823693, 32.9522328}},
+{{24.3823693, 32.9522328}, {22.7123482, 34.2497498}, {22.237118, 39.2417454}},
+</div>
+
+<div id="cubic8x2x">
+{{30.5187597, 28.7944151}, {47.7341773, 68.3182353}, {24.579915, 14.6321317}, {22.237118, 39.2417454}},
+{{30.5187597, 28.7944151}, {30.9761076, 29.8443688}, {31.3959629, 30.8080078}},
+{{31.3959629, 30.8080078}, {34.5380678, 38.0012585}, {35.7821411, 40.9504208}},
+{{35.7821411, 40.9504208}, {37.0262144, 43.899583}, {36.8148424, 43.845334}},
+{{36.8148424, 43.845334}, {36.6034705, 43.791085}, {35.3792396, 41.9741019}},
+{{35.3792396, 41.9741019}, {34.0487375, 39.9904922}, {32.0725075, 37.4554574}},
+{{32.0725075, 37.4554574}, {30.0962775, 34.9204225}, {28.0338583, 33.4024856}},
+{{28.0338583, 33.4024856}, {25.9714391, 31.8845487}, {24.3823693, 32.9522328}},
+{{24.3823693, 32.9522328}, {22.7932996, 34.019917}, {22.237118, 39.2417454}},
+</div>
+
+<div id="cubic8x3">
+{{44.0194731, 35.2849254}, {33.7413378, 90.6639866}, {89.6236054, 3.93610117}, {54.0523993, 58.6752083}},
+{{44.0194731, 35.2849254}, {41.8034025, 47.2252151}, {43.4093427, 51.8147312}},
+{{43.4093427, 51.8147312}, {44.7573046, 55.6669869}, {48.7793294, 54.2868164}},
+{{48.7793294, 54.2868164}, {51.9073817, 53.2134153}, {56.6514498, 48.9586591}},
+{{56.6514498, 48.9586591}, {59.3060696, 46.5778416}, {63.5477208, 42.0877376}},
+{{63.5477208, 42.0877376}, {65.8388976, 39.6623562}, {66.0159848, 39.6831127}},
+{{66.0159848, 39.6831127}, {66.2639069, 39.7121719}, {64.0479481, 43.2239426}},
+{{64.0479481, 43.2239426}, {62.4651204, 45.7323502}, {55.9567221, 55.7452241}},
+{{55.9567221, 55.7452241}, {54.718939, 57.649497}, {54.0523993, 58.6752083}},
+</div>
+
+<div id="cubic8x3x">
+{{44.0194731, 35.2849254}, {33.7413378, 90.6639866}, {89.6236054, 3.93610117}, {54.0523993, 58.6752083}},
+{{44.0194731, 35.2849254}, {41.7846308, 47.8464432}, {43.4093427, 51.8147312}},
+{{43.4093427, 51.8147312}, {45.0340547, 55.7830192}, {48.7793294, 54.2868164}},
+{{48.7793294, 54.2868164}, {52.524604, 52.7906136}, {56.6514498, 48.9586591}},
+{{56.6514498, 48.9586591}, {60.7782955, 45.1267046}, {63.5477208, 42.0877376}},
+{{63.5477208, 42.0877376}, {65.6800669, 39.7784361}, {66.0159848, 39.6831127}},
+{{66.0159848, 39.6831127}, {66.3519027, 39.5877893}, {64.0479481, 43.2239426}},
+{{64.0479481, 43.2239426}, {61.7439935, 46.8600958}, {55.9567221, 55.7452241}},
+{{55.9567221, 55.7452241}, {55.0519496, 57.1371078}, {54.0523993, 58.6752083}},
+</div>
+
+<div id="cubic8x4">
+{{34.4823321, 44.5556425}, {46.6831036, 2.92242272}, {17.1586486, 85.4072306}, {39.3243103, 23.6824388}},
+{{34.4823321, 44.5556425}, {36.9549821, 36.1181121}, {37.4421857, 33.458373}},
+{{37.4421857, 33.458373}, {37.852416, 31.2188464}, {36.8498458, 33.1020754}},
+{{36.8498458, 33.1020754}, {36.0666826, 34.5731701}, {34.4114682, 38.5859621}},
+{{34.4114682, 38.5859621}, {33.4805756, 40.8427565}, {31.8332086, 45.009246}},
+{{31.8332086, 45.009246}, {30.8106719, 47.5954265}, {30.8033416, 47.5462886}},
+{{30.8033416, 47.5462886}, {30.793079, 47.4774956}, {32.1835801, 43.5816708}},
+{{32.1835801, 43.5816708}, {33.1767952, 40.7989388}, {37.1569859, 29.7171487}},
+{{37.1569859, 29.7171487}, {38.5382511, 25.8713805}, {39.3243103, 23.6824388}},
+</div>
+
+<div id="cubic8x4x">
+{{34.4823321, 44.5556425}, {46.6831036, 2.92242272}, {17.1586486, 85.4072306}, {39.3243103, 23.6824388}},
+{{34.4823321, 44.5556425}, {37.0635768, 35.7091664}, {37.4421857, 33.458373}},
+{{37.4421857, 33.458373}, {37.8207947, 31.2075797}, {36.8498458, 33.1020754}},
+{{36.8498458, 33.1020754}, {35.878897, 34.996571}, {34.4114682, 38.5859621}},
+{{34.4114682, 38.5859621}, {32.9440394, 42.1753532}, {31.8332086, 45.009246}},
+{{31.8332086, 45.009246}, {30.8636314, 47.4784019}, {30.8033416, 47.5462886}},
+{{30.8033416, 47.5462886}, {30.7430517, 47.6141753}, {32.1835801, 43.5816708}},
+{{32.1835801, 43.5816708}, {33.6241085, 39.5491663}, {37.1569859, 29.7171487}},
+{{37.1569859, 29.7171487}, {38.146263, 26.9628595}, {39.3243103, 23.6824388}},
+</div>
+
+<div id="cubic8x5">
+{{67.1009526, 65.7102964}, {92.9511368, 29.7558215}, {6.09136899, 77.6386629}, {73.0077305, 40.9268787}},
+{{67.1009526, 65.7102964}, {72.234988, 58.569475}, {72.0584002, 54.9887776}},
+{{72.0584002, 54.9887776}, {71.9092626, 51.9646911}, {67.9699024, 51.5063347}},
+{{67.9699024, 51.5063347}, {64.8799872, 51.1468138}, {59.401457, 52.3770387}},
+{{59.401457, 52.3770387}, {56.3059837, 53.072139}, {50.919062, 54.7149606}},
+{{50.919062, 54.7149606}, {47.1095083, 55.8767404}, {47.0889098, 55.6405516}},
+{{47.0889098, 55.6405516}, {47.0600718, 55.3098873}, {52.2780952, 52.3607383}},
+{{52.2780952, 52.3607383}, {56.0052548, 50.2542034}, {70.9344915, 42.0642523}},
+{{70.9344915, 42.0642523}, {72.2995322, 41.3154119}, {73.0077305, 40.9268787}},
+</div>
+
+<div id="cubic8x5x">
+{{67.1009526, 65.7102964}, {92.9511368, 29.7558215}, {6.09136899, 77.6386629}, {73.0077305, 40.9268787}},
+{{67.1009526, 65.7102964}, {72.4119125, 58.1790269}, {72.0584002, 54.9887776}},
+{{72.0584002, 54.9887776}, {71.7048879, 51.7985283}, {67.9699024, 51.5063347}},
+{{67.9699024, 51.5063347}, {64.2349168, 51.2141411}, {59.401457, 52.3770387}},
+{{59.401457, 52.3770387}, {54.5679971, 53.5399363}, {50.919062, 54.7149606}},
+{{50.919062, 54.7149606}, {47.3051356, 55.8776986}, {47.0889098, 55.6405516}},
+{{47.0889098, 55.6405516}, {46.8726839, 55.4034046}, {52.2780952, 52.3607383}},
+{{52.2780952, 52.3607383}, {57.6835065, 49.3180721}, {70.9344915, 42.0642523}},
+{{70.9344915, 42.0642523}, {71.9455119, 41.5096286}, {73.0077305, 40.9268787}},
+</div>
+
+<div id="cubic8x6">
+{{34.6917773, 64.7007906}, {26.1879157, 40.299837}, {19.3601908, 86.7271998}, {24.0468774, 55.867852}},
+{{34.6917773, 64.7007906}, {32.8936408, 59.5412235}, {30.9809892, 57.9762826}},
+{{30.9809892, 57.9762826}, {29.3733084, 56.6608702}, {27.7048898, 57.9022642}},
+{{27.7048898, 57.9022642}, {26.4047253, 58.8696572}, {25.0679408, 61.4005651}},
+{{25.0679408, 61.4005651}, {24.3182009, 62.820033}, {23.2746043, 65.3930147}},
+{{23.2746043, 65.3930147}, {22.674283, 66.8731035}, {22.5736375, 66.8767729}},
+{{22.5736375, 66.8767729}, {22.4327337, 66.88191}, {22.7095685, 64.8303341}},
+{{22.7095685, 64.8303341}, {22.9073076, 63.3649228}, {23.7989097, 57.4996081}},
+{{23.7989097, 57.4996081}, {23.9603141, 56.4378254}, {24.0468774, 55.867852}},
+</div>
+
+<div id="cubic8x6x">
+{{34.6917773, 64.7007906}, {26.1879157, 40.299837}, {19.3601908, 86.7271998}, {24.0468774, 55.867852}},
+{{34.6917773, 64.7007906}, {32.7532689, 59.2911428}, {30.9809892, 57.9762826}},
+{{30.9809892, 57.9762826}, {29.2087096, 56.6614223}, {27.7048898, 57.9022642}},
+{{27.7048898, 57.9022642}, {26.2010699, 59.1431061}, {25.0679408, 61.4005651}},
+{{25.0679408, 61.4005651}, {23.9348117, 63.658024}, {23.2746043, 65.3930147}},
+{{23.2746043, 65.3930147}, {22.7294605, 66.7981817}, {22.5736375, 66.8767729}},
+{{22.5736375, 66.8767729}, {22.4178145, 66.955364}, {22.7095685, 64.8303341}},
+{{22.7095685, 64.8303341}, {23.0013225, 62.7053042}, {23.7989097, 57.4996081}},
+{{23.7989097, 57.4996081}, {23.9170479, 56.7225788}, {24.0468774, 55.867852}},
+</div>
+
+<div id="cubic8x7">
+{{33.7213174, 54.1809799}, {19.7290722, 86.516309}, {79.6055485, 30.4535282}, {32.4492169, 73.9888241}},
+{{33.7213174, 54.1809799}, {30.929259, 60.6332771}, {31.7178101, 63.1685506}},
+{{31.7178101, 63.1685506}, {32.3822203, 65.3046982}, {35.5832592, 64.6365068}},
+{{35.5832592, 64.6365068}, {38.0860576, 64.1140676}, {42.1661464, 61.8599641}},
+{{42.1661464, 61.8599641}, {44.4631798, 60.5909351}, {48.3149537, 58.1140379}},
+{{48.3149537, 58.1140379}, {50.7761073, 56.5313841}, {50.8756876, 56.6229029}},
+{{50.8756876, 56.6229029}, {51.0150999, 56.7510292}, {48.1271345, 59.4792497}},
+{{48.1271345, 59.4792497}, {46.064302, 61.4279786}, {37.7133917, 69.1313755}},
+{{37.7133917, 69.1313755}, {34.3790695, 72.2071608}, {32.4492169, 73.9888241}},
+</div>
+
+<div id="cubic8x7x">
+{{33.7213174, 54.1809799}, {19.7290722, 86.516309}, {79.6055485, 30.4535282}, {32.4492169, 73.9888241}},
+{{33.7213174, 54.1809799}, {30.8583849, 60.9640583}, {31.7178101, 63.1685506}},
+{{31.7178101, 63.1685506}, {32.5772353, 65.3730429}, {35.5832592, 64.6365068}},
+{{35.5832592, 64.6365068}, {38.589283, 63.8999708}, {42.1661464, 61.8599641}},
+{{42.1661464, 61.8599641}, {45.7430098, 59.8199575}, {48.3149537, 58.1140379}},
+{{48.3149537, 58.1140379}, {50.6281545, 56.5876371}, {50.8756876, 56.6229029}},
+{{50.8756876, 56.6229029}, {51.1232206, 56.6581687}, {48.1271345, 59.4792497}},
+{{48.1271345, 59.4792497}, {45.1310483, 62.3003307}, {37.7133917, 69.1313755}},
+{{37.7133917, 69.1313755}, {35.340896, 71.3195506}, {32.4492169, 73.9888241}},
+</div>
+
+<div id="cubic8x8">
+{{53.9853472, 31.729689}, {80.8833995, 7.2950833}, {8.46509939, 72.9253675}, {56.6511208, 35.3872682}},
+{{53.9853472, 31.729689}, {54.6892116, 31.0902877}, {55.8968586, 29.9930402}},
+{{55.8968586, 29.9930402}, {59.7242319, 26.5155538}, {60.0744106, 26.2078693}},
+{{60.0744106, 26.2078693}, {60.4245892, 25.9001849}, {58.6982878, 27.5315648}},
+{{58.6982878, 27.5315648}, {54.8548255, 31.1636922}, {52.2592609, 33.6639071}},
+{{52.2592609, 33.6639071}, {47.5369986, 38.2126941}, {44.5524326, 41.278553}},
+{{44.5524326, 41.278553}, {40.6568202, 45.2802731}, {39.8604011, 46.6125979}},
+{{39.8604011, 46.6125979}, {38.901811, 48.2162178}, {42.4657645, 45.9031378}},
+{{42.4657645, 45.9031378}, {46.5800498, 43.232881}, {56.6511208, 35.3872682}},
+</div>
+
+<div id="cubic8x8x">
+{{53.9853472, 31.729689}, {80.8833995, 7.2950833}, {8.46509939, 72.9253675}, {56.6511208, 35.3872682}},
+{{53.9853472, 31.729689}, {55.0401587, 30.7714526}, {55.8968586, 29.9930402}},
+{{55.8968586, 29.9930402}, {59.6366873, 26.5924749}, {60.0744106, 26.2078693}},
+{{60.0744106, 26.2078693}, {60.5121339, 25.8232638}, {58.6982878, 27.5315648}},
+{{58.6982878, 27.5315648}, {56.3310494, 29.756797}, {52.2592609, 33.6639071}},
+{{52.2592609, 33.6639071}, {48.1874723, 37.5710172}, {44.5524326, 41.278553}},
+{{44.5524326, 41.278553}, {40.9173929, 44.9860887}, {39.8604011, 46.6125979}},
+{{39.8604011, 46.6125979}, {38.8034093, 48.2391072}, {42.4657645, 45.9031378}},
+{{42.4657645, 45.9031378}, {46.1281196, 43.5671684}, {56.6511208, 35.3872682}},
+</div>
+
+<div id="cubic8x9">
+{{73.1810322, 53.6088109}, {3.28446082, 1.93300047}, {87.9389074, 89.2160727}, {54.5340817, 54.3107021}},
+{{73.1810322, 53.6088109}, {58.5574459, 42.7973267}, {51.3867695, 38.9320081}},
+{{51.3867695, 38.9320081}, {45.1924119, 35.5929695}, {44.6615376, 37.4897176}},
+{{44.6615376, 37.4897176}, {44.2222697, 39.0591673}, {47.6643015, 44.1654891}},
+{{47.6643015, 44.1654891}, {50.2846557, 48.0528359}, {55.0540263, 53.8428721}},
+{{55.0540263, 53.8428721}, {57.6549797, 57.0004406}, {61.4896769, 61.4054165}},
+{{61.4896769, 61.4054165}, {62.8763033, 62.9982556}, {62.6160157, 62.7489128}},
+{{62.6160157, 62.7489128}, {62.3557281, 62.49957}, {59.4073763, 59.4106743}},
+{{59.4073763, 59.4106743}, {56.5808171, 56.449377}, {54.5340817, 54.3107021}},
+</div>
+
+<div id="cubic8x9x">
+{{73.1810322, 53.6088109}, {3.28446082, 1.93300047}, {87.9389074, 89.2160727}, {54.5340817, 54.3107021}},
+{{73.1810322, 53.6088109}, {57.8490138, 42.3222252}, {51.3867695, 38.9320081}},
+{{51.3867695, 38.9320081}, {44.9245252, 35.5417911}, {44.6615376, 37.4897176}},
+{{44.6615376, 37.4897176}, {44.3985499, 39.4376442}, {47.6643015, 44.1654891}},
+{{47.6643015, 44.1654891}, {50.9300531, 48.893334}, {55.0540263, 53.8428721}},
+{{55.0540263, 53.8428721}, {59.1779995, 58.7924103}, {61.4896769, 61.4054165}},
+{{61.4896769, 61.4054165}, {62.9413752, 63.0605913}, {62.6160157, 62.7489128}},
+{{62.6160157, 62.7489128}, {62.2906562, 62.4372343}, {59.4073763, 59.4106743}},
+{{59.4073763, 59.4106743}, {57.5885087, 57.5036974}, {54.5340817, 54.3107021}},
+</div>
+
+<div id="cubic4">
+{{24.2578299, 1.34695745}, {6.77689729, 99.312693}, {3.18338154, 3.09354817}, {59.132973, 47.8778685}},
+{{38.313885, 41.465269}, {48.4308047, 76.5337766}, {93.264044, 88.7879534}, {83.3354337, 18.6335197}}
+</div>
+
+<div id="cubic5">
+{{24.0062249, 72.6211198}, {22.3913226, 30.9587957}, {80.7539402, 31.4736433}, {99.6348716, 63.2867312}},
+{{43.1612821, 11.6690897}, {24.4801394, 37.7033828}, {77.5229567, 28.3334108}, {60.0910899, 50.9480224}}
+$6 = {{x = 24.006224853920855, y = 72.621119847810419}, {x = 24.119692298829129, y = 51.890688643515688}, {x = 38.700154924642845, y = 44.614929583485953}}
+(gdb) p q2
+$7 = {{x = 24.006224853920855, y = 72.621119847810419}, {x = 29.758671200376888, y = 31.764642512385173}, {x = 71.277052443896736, y = 42.309313461363033}}
+</div>
+
+<div id="quad1">
+{{x = 34.879150914024962, y = 83.862726601601125}, {x = 35.095810134304429, y = 83.693473210169543}, {x = 35.359284111931586, y = 83.488069234177502}}
+{{x = 54.503204203015471, y = 76.094098492518242}, {x = 51.366889541918894, y = 71.609856061299155}, {x = 46.53086955445437, y = 69.949863036494207}}
+</div>
+
+<div id="cubic6">
+{{x = 54.080923997834752, y = 38.089631608729078}, {x = 10.447347774378651, y = 88.574043981998258}, {x = 33.294667831293616, y = 83.482240551841556}, {x = 25.649263209500006, y = 87.166762066617025}}
+</div>
+
+<div id="quad2">
+{{x = 25.367434474345036, y = 50.4712103169743}, {x = 17.865013304933097, y = 37.356741010559439}, {x = 16.818988838905465, y = 37.682915484123129}}
+{{x = 16.818988838905465, y = 37.682915484123129}, {x = 15.772964372877833, y = 38.009089957686811}, {x = 20.624104547604965, y = 41.825131596683121}}
+</div>
+
+<div id="cubic7">
+{{x = 25.367434474345036, y = 50.4712103169743}, {x = 5.2367042308844178, y = 13.28800847441331}, {x = 21.031375239152169, y = 74.32364443052731}, {x = 60.821163496384933, y = 21.294883741668837}}
+</div>
+
+<div id="quad3">
+{{x = 36.148792695174222, y = 70.336952793070424}, {x = 36.141613037691357, y = 70.711654739870085}, {x = 36.154708826402597, y = 71.088492662905836}}
+{{x = 35.216235592661825, y = 70.580199617313212}, {x = 36.244476835123969, y = 71.010897787304074}, {x = 37.230244263238326, y = 71.423156953613102}}
+</div>
+
+<div id="quad4">
+{{x = 369.84860200000003, y = 145.68026699999999}, {x = 382.36041299999999, y = 121.298294}, {x = 406.20770299999998, y = 121.298294}}
+{{x = 369.850525, y = 145.67596399999999}, {x = 382.36291499999999, y = 121.29286999999999}, {x = 406.21127300000001, y = 121.29286999999999}}
+</div>
+
+<div id="quad5">
+{{x = 67.25299631583178, y = 21.109080184767524}, {x = 43.617595267398613, y = 33.658034168577529}, {x = 33.38371819435676, y = 44.214192553988745}}
+{{x = 40.476838859398541, y = 39.543209911285999}, {x = 36.701186108431131, y = 34.8817994016458}, {x = 30.102144288878023, y = 26.739063172945315}}
+</div>
+
+<div id="quad6">
+{{x = 59.981867574297752, y = 19.243986850744687}, {x = 59.992798861020468, y = 19.257454808070786}, {x = 60.003741189575571, y = 19.270930807443623}}
+{{x = 47.800898294803176, y = 89.697640756935641}, {x = 38.74069898238357, y = 58.416865487251982}, {x = 37.639862598936119, y = 44.208141075385868}}
+</div>
+
+<div id="cubic8">
+{{x = 53.674595921148828, y = 8.9336467482771944}, {x = 48.248201817389678, y = 7.5279448682106773}, {x = 89.942031162763953, y = 55.717752573880254}, {x = 81.402728418541486, y = 35.656530426655216}}
+{{x = 47.800898294803176, y = 89.697640756935641}, {x = 22.169400016856102, y = 1.1060833004797266}, {x = 48.267509205391399, y = 32.027215013293187}, {x = 79.306880794142785, y = 10.745507157754854}}
+</div>
+
+<div id="quad7">
+{{x = 33.567436351153468, y = 62.336347586395924}, {x = 35.200980274619084, y = 65.038561460144479}, {x = 36.479571811084995, y = 67.632178905412445}}
+{{x = 41.349524945572696, y = 67.886658677862641}, {x = 39.125562529359087, y = 67.429772735149214}, {x = 35.600314083992416, y = 66.705372160552685}}
+</div>
+
+<div id="quad8">
+{{x = 36.148792695174222, y = 70.336952793070424}, {x = 36.141613037691357, y = 70.711654739870085}, {x = 36.154708826402597, y = 71.088492662905836}}
+{{x = 35.216235592661825, y = 70.580199617313212}, {x = 36.244476835123969, y = 71.010897787304074}, {x = 37.230244263238326, y = 71.423156953613102}}
+</div>
+
+<div id="quad9">
+{{353.2948,194.351074}, {353.2948,173.767563}, {364.167572,160.819855}},
+{{360.416077,166.795715}, {370.126831,147.872162}, {388.635406,147.872162}},
+</div>
+
+<div id="quad10">
+    {{8, 8}, {10, 10}, {8, -10}},
+    {{8, 8}, {12, 12}, {14, 4}},
+</div>
+
+<div id="quad11">
+{{x = 50.934805397717923, y = 51.52391952648901}, {x = 56.803308902971423, y = 44.246234610627596}, {x = 69.776888596721406, y = 40.166645096692555}}
+{{x = 50.230212796400401, y = 38.386469101526998}, {x = 49.855620812184917, y = 38.818990392153609}, {x = 56.356567496227363, y = 47.229909093319407}}
+</div>
+
+<div id="cubic9">
+{{18.1312339, 31.6473732}, {95.5711034, 63.5350219}, {92.3283165, 62.0158945}, {18.5656052, 32.1268808}},
+{{97.402018, 35.7169972}, {33.1127443, 25.8935163}, {1.13970027, 54.9424981}, {56.4860195, 60.529264}},
+</div>
+
+<div id="cubic10">
+{{67.4265481, 37.9937726}, {23.4836959, 90.4768632}, {35.5970651, 79.8724826}, {75.3863417, 18.24489}},
+{{61.3365082, 82.6931328}, {44.6393809, 54.0748258}, {16.8156155, 20.0497047}, {41.866885, 56.7355037}},
+</div>
+
+<div id="cubic11">
+{{40.3684631, 72.7588382}, {85.2198593, 90.174892}, {31.9101421, 13.7580149}, {72.0483425, 16.4930846}},
+{{57.7943379, 49.4368549}, {69.4103137, 79.1415428}, {30.9563231, 82.9221187}, {99.2731298, 83.4922981}},
+</div>
+
+<div id="cubic12">
+{{98.3415562, 26.5353662}, {15.3721551, 59.8107939}, {77.1895742, 25.1742572}, {11.7326863, 91.2589209}},
+{{79.899867, 77.0640431}, {40.0129651, 97.9042774}, {3.74105489, 75.9095456}, {88.6837571, 7.90615282}},
+</div>
+
+<div id="cubic13">
+{{95.6513419, 12.1029701}, {63.4801516, 10.9081754}, {41.0209588, 39.2537121}, {65.9441362, 23.0970739}},
+{{14.6179238, 83.4452002}, {33.7032426, 50.3981092}, {37.1399002, 10.3032037}, {92.5218685, 15.0431467}},
+</div>
+
+<div id="cubic14">
+{{67.4265481, 37.9937726}, {23.4836959, 90.4768632}, {35.5970651, 79.8724826}, {75.3863417, 18.24489}},
+{{61.3365082, 82.6931328}, {44.6393809, 54.0748258}, {16.8156155, 20.0497047}, {41.866885, 56.7355037}},
+{{67.4265481,37.9937726}, {51.1295132,57.5422812}, {44.5947482,65.6442673}},
+{{44.5947482,65.6442673}, {35.2387481,77.3910511}, {43.2346162,66.2224493}},
+{{43.2346162,66.2224493}, {51.8234203,54.2750917}, {75.3863417,18.24489}},
+{{61.3365082,82.6931328}, {54.8250789,71.6639328}, {47.7274442,61.4049645}},
+{{47.7274442,61.4049645}, {40.6298095,51.1459962}, {35.9460478,45.2252785}},
+{{35.9460478,45.2252785}, {31.2622861,39.3045608}, {31.9924758,41.2901124}},
+{{31.9924758,41.2901124}, {32.7226655,43.275664}, {41.866885,56.7355037}},
+</div>
+
+<div id="quad12">
+{{x = 67.426548091427676, y = 37.993772624988935}, {x = 51.129513170665042, y = 57.542281234563646}, {x = 44.594748190899182, y = 65.644267382683879}}
+{{x = 61.336508189019057, y = 82.693132843213675}, {x = 54.825078921449354, y = 71.663932799212432}, {x = 47.727444217558926, y = 61.4049645128392}}
+</div>
+
+<div id="quad13">
+{{x = 53.774852327053594, y = 53.318060789841951}, {x = 45.787877803416805, y = 51.393492026284981}, {x = 46.703936967162392, y = 53.06860709822206}}
+{{x = 46.703936967162392, y = 53.06860709822206}, {x = 47.619996130907957, y = 54.74372217015916}, {x = 53.020051653535361, y = 48.633140968832024}}
+</div>
+
+<div id="cubic15">
+{{40.3684631, 72.7588382}, {85.2198593, 90.174892}, {31.9101421, 13.7580149}, {72.0483425, 16.4930846}},
+{{57.7943379, 49.4368549}, {69.4103137, 79.1415428}, {30.9563231, 82.9221187}, {99.2731298, 83.4922981}},
+</div>
+
+<div id="cubic16">
+{{98.3415562, 26.5353662}, {15.3721551, 59.8107939}, {77.1895742, 25.1742572}, {11.7326863, 91.2589209}},
+{{79.899867, 77.0640431}, {40.0129651, 97.9042774}, {3.74105489, 75.9095456}, {88.6837571, 7.90615282}},
+</div>
+
+<div id="cubic17">
+{{95.6513419, 12.1029701}, {63.4801516, 10.9081754}, {41.0209588, 39.2537121}, {65.9441362, 23.0970739}},
+{{14.6179238, 83.4452002}, {33.7032426, 50.3981092}, {37.1399002, 10.3032037}, {92.5218685, 15.0431467}},
+{{95.6513419,12.1029701}, {79.216947,12.1911515}, {68.1126831,18.0126375}},
+{{68.1126831,18.0126375}, {57.0084192,23.8341235}, {55.4198832,27.1619689}},
+{{55.4198832,27.1619689}, {53.8313472,30.4898143}, {65.9441362,23.0970739}},
+{{14.6179238,83.4452002}, {20.3825754,73.0912112}, {25.0377248,62.5209893}},
+{{25.0377248,62.5209893}, {33.2090045,41.6303758}, {46.9147771,27.3313746}},
+{{46.9147771,27.3313746}, {60.6205496,13.0323735}, {92.5218685,15.0431467}},
+</div>
+
+<div id="cubic18">
+{{55.7513494, 12.929877}, {46.4296358, 42.8887602}, {16.8160022, 26.5487217}, {4.93643419, 66.6494508}},
+{{12.4426199, 23.1121812}, {31.3921366, 7.64067448}, {4.36561578, 72.9044408}, {77.3190123, 0.63959742}},
+
+{{55.7513494,12.929877}, {52.399261,23.0070161}, {46.4958203,27.5595279}},
+{{46.4958203,27.5595279}, {40.5923795,32.1120397}, {33.5495747,34.9548738}},
+{{33.5495747,34.9548738}, {25.3214643,38.1279717}, {17.6150117,44.5570525}},
+{{17.6150117,44.5570525}, {9.90855921,50.9861334}, {4.93643419,66.6494508}},
+
+{{12.4426199,23.1121812}, {17.0040168,19.5021098}, {18.7553606,21.0894159}},
+{{18.7553606,21.0894159}, {20.5067044,22.676722}, {21.4650431,26.4450934}},
+{{21.4650431,26.4450934}, {22.5692754,31.7464864}, {26.4671516,34.648351}},
+{{26.4671516,34.648351}, {30.3650278,37.5502156}, {41.873704,30.8489321}},
+{{41.873704,30.8489321}, {53.3823801,24.1476487}, {77.3190123,0.63959742}},
+</div>
+
+<div id="quad14">
+{{67.4265481,37.9937726}, {51.1295132,57.5422812}, {44.5947482,65.6442674}},
+{{61.3365082,82.6931328}, {54.8250789,71.6639328}, {47.7274442,61.4049645}},
+</div>
+
+<div id="quad15">
+{{x = 80.897794748143198, y = 49.236332042718459}, {x = 81.082078218891212, y = 64.066749904488631}, {x = 69.972305057149981, y = 72.968595519850993}}
+{{x = 72.503745601281395, y = 32.952320736577882}, {x = 88.030880716061645, y = 38.137194847810164}, {x = 73.193774825517906, y = 67.773492479591397}}
+</div>
+
+<div id="cubic19">
+{{x = 34.560092601254624, y = 51.476349286491221}, {x = 27.498466254909744, y = 66.722346267999313}, {x = 42.500359724508769, y = 3.5458898188294325}, {x = 73.37353619438295, y = 89.022818994253328}}
+{{x = 63.002458057833124, y = 82.312578001205154}, {x = 2.4737262644217006, y = 75.917326135522373}, {x = 95.77018506628005, y = 9.5004089686555826}, {x = 6.5188364156143912, y = 62.083637231068508}}
+</div>
+
+<div id="cubic20">
+ {{x = 42.449716172390481, y = 52.379709366885805}, {x = 27.896043159019225, y = 48.797373636065686}, {x = 92.770268299044233, y = 89.899302036454571}, {x = 12.102066544863426, y = 99.43241951960718}}
+{{x = 45.77532924980639, y = 45.958701495993274}, {x = 37.458701356062065, y = 68.393691335056758}, {x = 37.569326692060258, y = 27.673713456687381}, {x = 60.674866037757539, y = 62.47349659096146}}
+</div>
+
+<div id="cubic21">
+{{x = 26.192053931854691, y = 9.8504326817814416}, {x = 10.174241480498686, y = 98.476562741434464}, {x = 21.177712558385782, y = 33.814968789841501}, {x = 75.329030899018534, y = 55.02231980442177}}
+{{x = 56.222082700683771, y = 24.54395039218662}, {x = 95.589995289030483, y = 81.050822735322086}, {x = 28.180450866082897, y = 28.837706255185282}, {x = 60.128952916771617, y = 87.311672180570511}}
+</div>
+
+<div id="quad16">
+{{x = 67.965974918365831, y = 52.573040929556633}, {x = 67.973015821010591, y = 52.57495862082331}, {x = 67.980057838863502, y = 52.576878275262274}}
+{{x = 67.975025709349239, y = 52.572750461020817}, {x = 67.973101328974863, y = 52.57506284863603}, {x = 67.971173663444745, y = 52.577372136133093}}
+</div>
+
+<div id="quad17">
+{{x = 52.14807018377202, y = 65.012420045148644}, {x = 44.778669050208237, y = 66.315562705604378}, {x = 51.619118408823567, y = 63.787827046262684}}
+{{x = 30.004993234763383, y = 93.921296668202288}, {x = 53.384822003076991, y = 60.732180341802753}, {x = 58.652998934338584, y = 43.111073088306185}}
+</div>
+
+<div id="quad18">
+{{x = 369.850525, y = 145.67596399999999}, {x = 382.36291499999999, y = 121.29286999999999}, {x = 406.21127300000001, y = 121.29286999999999}}
+{{x = 369.962311, y = 137.976044}, {x = 383.97189300000002, y = 121.29286999999999}, {x = 406.21612499999998, y = 121.29286999999999}}
+</div>
+
+<div id="quad19">
+{{x = 406.23635899999999, y = 121.254936}, {x = 409.44567899999998, y = 121.254936}, {x = 412.97595200000001, y = 121.789818}}
+{{x = 406.23599200000001, y = 121.254936}, {x = 425.70590199999998, y = 121.254936}, {x = 439.71994000000001, y = 137.087616}}
+</div>
+
+<div id="cubic22">
+{{x = 7.5374809128872498, y = 82.441702896003477}, {x = 22.444346930107265, y = 22.138854312775123}, {x = 66.76091829629658, y = 50.753805856571446}, {x = 78.193478508942519, y = 97.7932997968948}}
+{{x = 97.700573130371311, y = 53.53260215070685}, {x = 87.72443481149358, y = 84.575876772671876}, {x = 19.215031396232092, y = 47.032676472809484}, {x = 11.989686410869325, y = 10.659507480757082}}
+  {{7.53748091,82.4417029}, {15.5677076,52.942994}, {29.9404074,49.1672596}},
+  {{29.9404074,49.1672596}, {44.3131071,45.3915253}, {58.1067559,59.5061814}},
+  {{58.1067559,59.5061814}, {71.9004047,73.6208375}, {78.1934785,97.7932998}},
+
+  {{97.7005731,53.5326022}, {91.6030843,68.4083459}, {72.6510251,64.2972928}},
+  {{72.6510251,64.2972928}, {53.6989659,60.1862397}, {35.2053722,44.8391126}},
+  {{35.2053722,44.8391126}, {16.7117786,29.4919856}, {11.9896864,10.6595075}},
+</div>
+
+<div id="quad20">
+  {{29.9404074,49.1672596}, {44.3131071,45.3915253}, {58.1067559,59.5061814}},
+  {{72.6510251,64.2972928}, {53.6989659,60.1862397}, {35.2053722,44.8391126}},
+</div>
+
+<div id="cubic23">
+{{x = 32.484981432782945, y = 75.082940782924624}, {x = 42.467313093350882, y = 48.131159948246157}, {x = 3.5963115764764657, y = 43.208665839959245}, {x = 79.442476890721579, y = 89.709102357602262}}
+{{x = 18.98573861410177, y = 93.308887208490106}, {x = 40.405250173250792, y = 91.039661826118675}, {x = 8.0467721950480584, y = 42.100282172719147}, {x = 40.883324221187891, y = 26.030185504830527}}
+  {{32.4849814,75.0829408}, {35.4553509,65.5763004}, {33.5767697,60.2097835}},
+  {{33.5767697,60.2097835}, {31.6981886,54.8432666}, {31.1663962,54.7302484}},
+  {{31.1663962,54.7302484}, {31.1662882,54.7301074}, {31.1663969,54.7302485}},
+  {{31.1663969,54.7302485}, {30.4117445,54.6146017}, {40.1631726,62.9428436}},
+  {{40.1631726,62.9428436}, {49.9146008,71.2710854}, {79.4424769,89.7091024}},
+
+  {{18.9857386,93.3088872}, {25.7662938,92.3417699}, {26.5917262,85.8225583}},
+  {{26.5917262,85.8225583}, {27.4171586,79.3033467}, {26.141946,69.8089528}},
+  {{26.141946,69.8089528}, {24.2922348,57.665767}, {26.0404936,45.4260361}},
+  {{26.0404936,45.4260361}, {27.7887523,33.1863051}, {40.8833242,26.0301855}},
+</div>
+
+<div id="quad21">
+  {{31.1663962,54.7302484}, {31.1662882,54.7301074}, {31.1663969,54.7302485}},
+  {{26.0404936,45.4260361}, {27.7887523,33.1863051}, {40.8833242,26.0301855}},
+</div>
+
+<div id="cubic24">
+{{x = 65.454505973241524, y = 93.881892270353575}, {x = 45.867360264932437, y = 92.723972719499827}, {x = 2.1464054482739447, y = 74.636369140183717}, {x = 33.774068594804994, y = 40.770872887582925}}
+{{x = 72.963387832494163, y = 95.659300729473728}, {x = 11.809496633619768, y = 82.209921247423594}, {x = 13.456139067865974, y = 57.329313623406605}, {x = 36.060621606214262, y = 70.867335643091849}}
+  {{65.454506,93.8818923}, {54.7397995,93.2922678}, {41.5072916,87.1234036}},
+  {{41.5072916,87.1234036}, {28.2747836,80.9545395}, {23.5780771,69.3344126}},
+  {{23.5780771,69.3344126}, {18.8813706,57.7142857}, {33.7740686,40.7708729}},
+
+  {{72.9633878,95.6593007}, {42.7738746,88.4730382}, {31.1932785,80.2458029}},
+  {{31.1932785,80.2458029}, {19.6126823,72.0185676}, {21.9918152,68.2892325}},
+  {{21.9918152,68.2892325}, {24.370948,64.5598974}, {36.0606216,70.8673356}},
+</div>
+
+<div id="quad22">
+  {{41.5072916,87.1234036}, {28.2747836,80.9545395}, {23.5780771,69.3344126}},
+  {{72.9633878,95.6593007}, {42.7738746,88.4730382}, {31.1932785,80.2458029}},
+</div>
+
+<div id="cubic25">
+{{x = 39.765160968417838, y = 33.060396198677083}, {x = 5.1922921581157908, y = 66.854301452103215}, {x = 31.619281802149157, y = 25.269248720849514}, {x = 81.541621071073038, y = 70.025341524754353}}
+{{x = 46.078911165743556, y = 48.259962651999651}, {x = 20.24450549867214, y = 49.403916182650214}, {x = 0.26325131778756683, y = 24.46489805563581}, {x = 15.915006546264051, y = 83.515023059917155}}
+  {{39.765161,33.0603962}, {30.6426004,41.804305}, {26.9359756,44.8138368}},
+  {{26.9359756,44.8138368}, {21.5667569,48.8605535}, {26.2727712,47.6735862}},
+  {{26.2727712,47.6735862}, {31.3832959,46.2642047}, {45.8264929,49.1528875}},
+  {{45.8264929,49.1528875}, {60.2696898,52.0415702}, {81.5416211,70.0253415}},
+
+  {{46.0789112,48.2599627}, {35.5887068,48.1941457}, {27.2014026,45.6924463}},
+  {{27.2014026,45.6924463}, {19.5490336,43.4817863}, {15.020365,44.2719744}},
+  {{15.020365,44.2719744}, {10.4916964,45.0621625}, {10.3896311,53.6689795}},
+  {{10.3896311,53.6689795}, {10.2875658,62.2757965}, {15.9150065,83.5150231}},
+</div>
+
+<div id="cubic26">
+{{x = 95.837747722788592, y = 45.025976907939643}, {x = 16.564570095652982, y = 0.72959763963222402}, {x = 63.209855865319199, y = 68.047528419665767}, {x = 57.640240647662544, y = 59.524565264361243}}
+{{x = 51.593891741518817, y = 38.53849970667553}, {x = 62.34752929878772, y = 74.924924725166022}, {x = 74.810149322641152, y = 34.17966562983564}, {x = 29.368398119401373, y = 94.66719277886078}}
+  {{95.8377477,45.0259769}, {72.4120612,32.1119735}, {61.9589898,30.3422249}},
+  {{61.9589898,30.3422249}, {51.5059185,28.5724763}, {49.7502617,33.4480576}},
+  {{49.7502617,33.4480576}, {47.9946048,38.3236388}, {50.6611618,45.345625}},
+  {{50.6611618,45.345625}, {53.3277187,52.3676112}, {56.1412886,57.0370775}},
+  {{56.1412886,57.0370775}, {58.9548585,61.7065438}, {57.6402406,59.5245653}},
+
+  {{51.5938917,38.5384997}, {54.39659,47.5609728}, {56.9124968,51.2509862}},
+  {{56.9124968,51.2509862}, {59.4284036,54.9409997}, {60.7901347,55.8937858}},
+  {{60.7901347,55.8937858}, {63.1940269,56.8659601}, {59.551481,59.5998651}},
+  {{59.551481,59.5998651}, {56.8806183,61.8512737}, {49.6576236,69.6523525}},
+  {{49.6576236,69.6523525}, {42.434629,77.4534313}, {29.3683981,94.6671928}},
+</div>
+
+<div id="quad23">
+{{x = 56.14128857485079, y = 57.037077517172825}, {x = 58.954858484191291, y = 61.706543802985237}, {x = 57.640240647662544, y = 59.524565264361243}}
+{{x = 59.551480981235549, y = 59.599865066889976}, {x = 56.880618274428095, y = 61.851273706132794}, {x = 49.657623623535379, y = 69.652352522894546}}
+</div>
+
+<div id="cubic27">
+{{x = 56.14128857485079, y = 57.037077517172825}, {x = 57.779490695232283, y = 59.900114769069532}, {x = 58.754163691193881, y = 61.229157895422141}, {x = 57.640240647662544, y = 59.524565264361243}}
+{{x = 56.14128857485079, y = 57.037077517172825}, {x = 58.954858484191291, y = 61.706543802985237}, {x = 57.640240647662544, y = 59.524565264361243}}
+</div>
+
+<div id="testCubic1">
+{{0, 0}, {0, 1}, {1, 1}, {1, 0}},
+{{1, 0}, {0, 0}, {0, 1}, {1, 1}},
+
+  {{0,0}, {0.0185185185,0.5}, {0.259259259,0.666666667}},
+  {{0.259259259,0.666666667}, {0.5,0.833333333}, {0.740740741,0.666666667}},
+  {{0.740740741,0.666666667}, {0.981481481,0.5}, {1,0}},
+
+  {{1,0}, {0.5,0.0185185185}, {0.333333333,0.259259259}},
+  {{0.333333333,0.259259259}, {0.166666667,0.5}, {0.333333333,0.740740741}},
+  {{0.333333333,0.740740741}, {0.5,0.981481481}, {1,1}},
+</div>
+
+<div id="testCubic1a">
+{{x = 0.30075438676757493, y = 0.69070348972827045}, {x = 0.30339450221247349, y = 0.69543451478800855}, {x = 0.30613761677734441, y = 0.7001387457168422}, {x = 0.30898373046218741, y = 0.70481409186990207}}
+{{x = 0.29518590813009821, y = 0.69101626953781281}, {x = 0.29986125428315819, y = 0.69386238322265548}, {x = 0.30456548521199123, y = 0.69660549778752689}, {x = 0.30929651027172955, y = 0.69924561323242507}}
+</div>
+
+<div id="testCubic1b">
+{{x = 0.3039751936710845, y = 0.69622610811401087}, {x = 0.3037698832307662, y = 0.69610758676672113}}
+{{x = 0.3037698832307662, y = 0.69610758676672113}, {x = 0.30387252963474076, y = 0.69616688005807803}}
+{{x = 0.30387252963474076, y = 0.69616688005807803}, {x = 0.3039751936710845, y = 0.69622610811401087}}
+</div>
+
+<div id="cubicOp1d">
+{{0, 1}, {0, 2}, {1, 0}, {1, 0}},
+{{0, 1}, {0, 1}, {1, 0}, {2, 0}},
+
+  {{0,1}, {0.0078125,1.35546875}, {0.15625,1.265625}},
+  {{0.15625,1.265625}, {0.3046875,1.17578125}, {0.5,0.875}},
+  {{0.5,0.875}, {0.6953125,0.57421875}, {0.84375,0.296875}},
+  {{0.84375,0.296875}, {0.9921875,0.01953125}, {1,0}},
+
+  {{0,1}, {0.00925925926,0.981481481}, {0.296296296,0.740740741}},
+  {{0.296296296,0.740740741}, {0.583333333,0.5}, {1.03703704,0.259259259}},
+  {{1.03703704,0.259259259}, {1.49074074,0.0185185185}, {2,0}},
+</div>
+
+</div>
+
+<script type="text/javascript">
+
+var testDivs = [
+    cubicOp1d,
+    testCubic1b,
+    testCubic1a,
+    testCubic1,
+    cubic27,
+    cubic26,
+    quad23,
+    cubic25,
+    quad22,
+    cubic24,
+    quad21,
+    cubic23,
+    quad20,
+    cubic22,
+    quad19,
+    quad18,
+    quad17,
+    quad16,
+    cubic21,
+    cubic20,
+    cubic19,
+    quad15,
+    quad14,
+    cubic18,
+    cubic17,
+    cubic16,
+    cubic15,
+    quad13,
+    quad12,
+    cubic14,
+    cubic13,
+    cubic12,
+    cubic11,
+    cubic10,
+    cubic9,
+    quad11,
+    quad10,
+    quad9,
+    quad8,
+    quad7,
+    cubic8,
+    quad6,
+    quad5,
+    quad4,
+    quad3,
+    cubic7,
+    quad2,
+    cubic6,
+    quad1,
+    cubic5,
+    cubic4,
+    cubic1x0,
+    cubic1x0x,
+    cubic1x1,
+    cubic1x1x,
+    cubic1x2,
+    cubic1x2x,
+    cubic1x3,
+    cubic1x3x,
+    cubic1x4,
+    cubic1x4x,
+    cubic1x5,
+    cubic1x5x,
+    cubic1x6,
+    cubic1x6x,
+    cubic1x7,
+    cubic1x7x,
+    cubic1x8,
+    cubic1x8x,
+    cubic1x9,
+    cubic1x9x,
+    cubic2x0,
+    cubic2x0x,
+    cubic2x1,
+    cubic2x1x,
+    cubic2x2,
+    cubic2x2x,
+    cubic2x3,
+    cubic2x3x,
+    cubic2x4,
+    cubic2x4x,
+    cubic2x5,
+    cubic2x5x,
+    cubic2x6,
+    cubic2x6x,
+    cubic2x7,
+    cubic2x7x,
+    cubic2x8,
+    cubic2x8x,
+    cubic2x9,
+    cubic2x9x,
+    cubic3x0,
+    cubic3x0x,
+    cubic3x1,
+    cubic3x1x,
+    cubic3x2,
+    cubic3x2x,
+    cubic3x3,
+    cubic3x3x,
+    cubic3x4,
+    cubic3x4x,
+    cubic3x5,
+    cubic3x5x,
+    cubic3x6,
+    cubic3x6x,
+    cubic3x7,
+    cubic3x7x,
+    cubic3x8,
+    cubic3x8x,
+    cubic3x9,
+    cubic3x9x,
+    cubic4x0,
+    cubic4x0x,
+    cubic4x1,
+    cubic4x1x,
+    cubic4x2,
+    cubic4x2x,
+    cubic4x3,
+    cubic4x3x,
+    cubic4x4,
+    cubic4x4x,
+    cubic4x5,
+    cubic4x5x,
+    cubic4x6,
+    cubic4x6x,
+    cubic4x7,
+    cubic4x7x,
+    cubic4x8,
+    cubic4x8x,
+    cubic4x9,
+    cubic4x9x,
+    cubic5x0,
+    cubic5x0x,
+    cubic5x1,
+    cubic5x1x,
+    cubic5x2,
+    cubic5x2x,
+    cubic5x3,
+    cubic5x3x,
+    cubic5x4,
+    cubic5x4x,
+    cubic5x5,
+    cubic5x5x,
+    cubic5x6,
+    cubic5x6x,
+    cubic5x7,
+    cubic5x7x,
+    cubic5x8,
+    cubic5x8x,
+    cubic5x9,
+    cubic5x9x,
+    cubic6x0,
+    cubic6x0x,
+    cubic6x1,
+    cubic6x1x,
+    cubic6x2,
+    cubic6x2x,
+    cubic6x3,
+    cubic6x3x,
+    cubic6x4,
+    cubic6x4x,
+    cubic6x5,
+    cubic6x5x,
+    cubic6x6,
+    cubic6x6x,
+    cubic6x7,
+    cubic6x7x,
+    cubic6x8,
+    cubic6x8x,
+    cubic6x9,
+    cubic6x9x,
+    cubic7x0,
+    cubic7x0x,
+    cubic7x1,
+    cubic7x1x,
+    cubic7x2,
+    cubic7x2x,
+    cubic7x3,
+    cubic7x3x,
+    cubic7x4,
+    cubic7x4x,
+    cubic7x5,
+    cubic7x5x,
+    cubic7x6,
+    cubic7x6x,
+    cubic7x7,
+    cubic7x7x,
+    cubic7x8,
+    cubic7x8x,
+    cubic7x9,
+    cubic7x9x,
+    cubic8x0,
+    cubic8x0x,
+    cubic8x1,
+    cubic8x1x,
+    cubic8x2,
+    cubic8x2x,
+    cubic8x3,
+    cubic8x3x,
+    cubic8x4,
+    cubic8x4x,
+    cubic8x5,
+    cubic8x5x,
+    cubic8x6,
+    cubic8x6x,
+    cubic8x7,
+    cubic8x7x,
+    cubic8x8,
+    cubic8x8x,
+    cubic8x9,
+    cubic8x9x,
+    cubic3,
+    cubic2,
+    cubic1,
+];
+
+var scale, columns, rows, xStart, yStart;
+
+var ticks = 10;
+var at_x = 13 + 0.5;
+var at_y = 23 + 0.5;
+var decimal_places = 3;
+var tests = [];
+var testTitles = [];
+var testIndex = 0;
+var ctx;
+var minScale = 1;
+var subscale = 1;
+var curveT = -1;
+var drawCubics = true;
+var drawQuads = true;
+var drawControlLines = true;
+var xmin, xmax, ymin, ymax;
+
+function parse(test, title) {
+    var curveStrs = test.split("{{");
+    if (curveStrs.length == 1)
+        curveStrs = test.split("=(");
+    var pattern = /[a-z$=]?-?\d+\.*\d*/g;
+    var curves = [];
+    for (var c in curveStrs) {
+        var curveStr = curveStrs[c];
+        var points = curveStr.match(pattern);
+        var pts = [];
+        for (var wd in points) {
+            var num = parseFloat(points[wd]);
+            if (isNaN(num)) continue;
+            pts.push(num);
+        }
+        if (pts.length > 2)
+            curves.push(pts);
+    }
+    if (curves.length >= 1) {
+        tests.push(curves);
+        testTitles.push(title);
+    }
+}
+
+function init(test) {
+    var canvas = document.getElementById('canvas');
+    if (!canvas.getContext) return;
+    canvas.width = window.innerWidth - at_x;
+    canvas.height = window.innerHeight - at_y;
+    ctx = canvas.getContext('2d');
+    xmin = Infinity;
+    xmax = -Infinity;
+    ymin = Infinity;
+    ymax = -Infinity;
+    for (var curves in test) {
+        var curve = test[curves];
+        var last = curve.length;
+        for (var idx = 0; idx < last; idx += 2) {
+            xmin = Math.min(xmin, curve[idx]);
+            xmax = Math.max(xmax, curve[idx]);
+            ymin = Math.min(ymin, curve[idx + 1]);
+            ymax = Math.max(ymax, curve[idx + 1]);
+        }
+    }
+    var testW = xmax - xmin;
+    var testH = ymax - ymin;
+    subscale = 1;
+    while (testW * subscale < 0.1 && testH * subscale < 0.1) {
+        subscale *= 10;
+    }
+    while (testW * subscale > 10 && testH * subscale > 10) {
+        subscale /= 10;
+    }
+    calcFromScale();
+}
+
+function calcFromScale() {
+    xStart = Math.floor(xmin * subscale) / subscale;
+    yStart = Math.floor(ymin * subscale) / subscale;
+    var xEnd = Math.ceil(xmin * subscale) / subscale;
+    var yEnd = Math.ceil(ymin * subscale) / subscale;
+    var cCelsW = Math.floor(ctx.canvas.width / 10);
+    var cCelsH = Math.floor(ctx.canvas.height / 10);
+    var testW = xEnd - xStart;
+    var testH = yEnd - yStart; 
+    var scaleWH = 1;
+    while (cCelsW > testW * scaleWH * 10 && cCelsH > testH * scaleWH * 10) {
+        scaleWH *= 10;
+    }
+    while (cCelsW * 10 < testW * scaleWH && cCelsH * 10 < testH * scaleWH) {
+        scaleWH /= 10;
+    }
+    
+    columns = Math.ceil(xmax * subscale) - Math.floor(xmin * subscale) + 1;
+    rows = Math.ceil(ymax * subscale) - Math.floor(ymin * subscale) + 1;
+    
+    var hscale = ctx.canvas.width / columns / ticks;
+    var vscale = ctx.canvas.height / rows / ticks;
+    minScale = Math.floor(Math.min(hscale, vscale));
+    scale = minScale * subscale;
+}
+
+function drawPoint(px, py, xoffset, yoffset, unit) {
+    var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
+    var _px = px * unit + xoffset;
+    var _py = py * unit + yoffset;
+    ctx.beginPath();
+    ctx.arc(_px, _py, 3, 0, Math.PI*2, true);
+    ctx.closePath();
+    ctx.fill();
+    ctx.fillText(label, _px + 5, _py);
+}
+
+function draw(test, title, scale) {
+    ctx.fillStyle = "rgba(0,0,0, 0.1)";
+    ctx.font = "normal 50px Arial";
+    ctx.fillText(title, 50, 50);
+    ctx.font = "normal 10px Arial";
+
+    var unit = scale * ticks;
+    ctx.lineWidth = 1;
+    var i;
+    for (i = 0; i <= rows * ticks; ++i) {
+        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(200,200,200)" : "black";
+        ctx.beginPath();
+        ctx.moveTo(at_x + 0, at_y + i * minScale);
+        ctx.lineTo(at_x + ticks * columns * minScale, at_y + i * minScale);
+        ctx.stroke();
+    }
+    for (i = 0; i <= columns * ticks; ++i) {
+        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(200,200,200)" : "black";
+        ctx.beginPath();
+        ctx.moveTo(at_x + i * minScale, at_y + 0);
+        ctx.lineTo(at_x + i * minScale, at_y + ticks * rows * minScale);
+        ctx.stroke();
+    }
+ 
+    var xoffset = xStart * -unit + at_x;
+    var yoffset = yStart * -unit + at_y;
+
+    ctx.fillStyle = "rgb(40,80,60)"
+    for (i = 0; i <= columns; i += 1)
+    {
+        num = xStart + i / subscale; 
+        ctx.fillText(num.toFixed(decimal_places), xoffset + num * unit - 5, 10);
+    }
+    for (i = 0; i <= rows; i += 1)
+    {
+        num = yStart + i / subscale; 
+        ctx.fillText(num.toFixed(decimal_places), 0, yoffset + num * unit + 0);
+    }
+    var curves, pts;
+    for (curves in test) {
+        var curve = test[curves];
+        if (curve.length == 6 && !drawQuads) {
+            continue;
+        }
+        if (curve.length == 8 && !drawCubics) {
+            continue;
+        }
+        ctx.beginPath();
+        ctx.moveTo(xoffset + curve[0] * unit, yoffset + curve[1] * unit);
+        switch (curve.length) {
+            case 4:
+                ctx.lineTo(
+                    xoffset + curve[2] * unit, yoffset + curve[3] * unit);
+                break;
+            case 6:
+                ctx.quadraticCurveTo(
+                    xoffset + curve[2] * unit, yoffset + curve[3] * unit,
+                    xoffset + curve[4] * unit, yoffset + curve[5] * unit);
+                break;
+            case 8:
+                ctx.bezierCurveTo(
+                    xoffset + curve[2] * unit, yoffset + curve[3] * unit,
+                    xoffset + curve[4] * unit, yoffset + curve[5] * unit,
+                    xoffset + curve[6] * unit, yoffset + curve[7] * unit);
+                break;
+        }
+        if (curves == 2) ctx.strokeStyle = curves ? "red" : "blue";
+        ctx.stroke();
+        if (drawControlLines && curve.length >= 6) {
+            ctx.strokeStyle = "rgba(0,0,0, 0.3)";
+            ctx.beginPath();
+            ctx.moveTo(xoffset + curve[0] * unit, yoffset + curve[1] * unit);
+            ctx.lineTo(xoffset + curve[2] * unit, yoffset + curve[3] * unit);
+            ctx.lineTo(xoffset + curve[4] * unit, yoffset + curve[5] * unit);
+            if (curve.length == 8)
+                ctx.lineTo(xoffset + curve[6] * unit, yoffset + curve[7] * unit);
+            ctx.stroke();
+        }
+        if (curveT >= 0 && curveT <= 1) {
+            var x, y;
+            var t = curveT;
+            switch (curve.length) {
+                case 4:
+                    var a = 1 - t;
+                    var b = t;
+                    x = a * curve[0] + b * curve[2];
+                    y = a * curve[1] + b * curve[3];
+                    break;
+                case 6:
+                    var one_t = 1 - t;
+                    var a = one_t * one_t;
+                    var b = 2 * one_t * t;
+                    var c = t * t;
+                    x = a * curve[0] + b * curve[2] + c * curve[4];
+                    y = a * curve[1] + b * curve[3] + c * curve[5];
+                    break;
+                case 8:
+                    var one_t = 1 - t;
+                    var one_t2 = one_t * one_t;
+                    var a = one_t2 * one_t;
+                    var b = 3 * one_t2 * t;
+                    var t2 = t * t;
+                    var c = 3 * one_t * t2;
+                    var d = t2 * t;
+                    x = a * curve[0] + b * curve[2] + c * curve[4] + d * curve[6];
+                    y = a * curve[1] + b * curve[3] + c * curve[5] + d * curve[7];
+                    break;
+            }
+            drawPoint(x, y, xoffset, yoffset, unit);
+            var num = curveT.toFixed(decimal_places);
+            ctx.beginPath();
+            ctx.rect(200,10,200,10);
+            ctx.fillStyle="white";
+            ctx.fill();
+            ctx.fillStyle="black";
+            ctx.fillText(num, 230, 18);
+            if (curve.length == 8) {
+                var one_t = 1 - t;
+                var a = curve[0];
+                var b = curve[2];
+                var c = curve[4];
+                var d = curve[6];
+                var dx = (b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t;
+                a = curve[1];
+                b = curve[3];
+                c = curve[5];
+                d = curve[7];
+                var dy = (b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t;
+                ctx.beginPath();
+                ctx.moveTo(xoffset + (x - dx) * unit, yoffset + (y - dy) * unit);
+                ctx.lineTo(xoffset + (x + dx) * unit, yoffset + (y + dy) * unit);
+                ctx.stroke();
+            }
+        }
+    }
+}
+
+function drawTop() {
+    init(tests[testIndex]);
+    redraw();
+}
+
+function redraw() {
+    ctx.beginPath();
+    ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
+    ctx.fillStyle="white";
+    ctx.fill();
+    draw(tests[testIndex], testTitles[testIndex], scale);
+}
+
+function doKeyPress(evt) {
+    var char = String.fromCharCode(evt.charCode);
+    switch (char) {
+    case 'c':
+        drawCubics ^= true;
+        redraw();
+        break;
+    case 'd':
+        decimal_places++;
+        redraw();
+        break;
+    case 'D':
+        decimal_places--;
+        if (decimal_places < 1) {
+            decimal_places = 1;
+        }
+        redraw();
+        break;
+    case 'l':
+        drawControlLines ^= true;
+        redraw();
+        break;
+    case 'N':
+        testIndex += 9;
+    case 'n':
+        if (++testIndex >= tests.length)
+            testIndex = 0;
+        mouseX = Infinity;
+        drawTop();
+        break;
+    case 'P':
+        testIndex -= 9;
+    case 'p':
+        if (--testIndex < 0)
+            testIndex = tests.length - 1;
+        mouseX = Infinity;
+        drawTop();
+        break;
+    case 'q':
+        drawQuads ^= true;
+        redraw();
+        break;
+    case 'x':
+        drawCubics ^= true;
+        drawQuads ^= true;
+        redraw();
+        break;
+    case '-':
+    case '_':
+        subscale /= 2;
+        calcFromScale();
+        redraw();
+        break;
+    case '+':
+    case '=':
+        subscale *= 2;
+        calcFromScale();
+        redraw();
+        break;
+    }
+}
+
+function handleMouseClick() {
+    var e = window.event;
+	var tgt = e.target || e.srcElement;
+    var min = tgt.offsetTop + Math.ceil(at_y);
+    var max = min + ticks * rows * minScale;
+    curveT = (e.clientY - min) / (max - min);
+    redraw();
+}
+
+function calcXY() {
+    var e = window.event;
+	var tgt = e.target || e.srcElement;
+    var left = tgt.offsetLeft;
+    var top = tgt.offsetTop;
+    var unit = scale * ticks;
+    mouseX = (e.clientX - left - Math.ceil(at_x) + 1) / unit + xStart;
+    mouseY = (e.clientY - top - Math.ceil(at_y)) / unit + yStart;
+}
+
+function handleMouseOver() {
+    calcXY();
+    var num = mouseX.toFixed(decimal_places) + ", " + mouseY.toFixed(decimal_places);
+    ctx.beginPath();
+    ctx.rect(30,10,200,10);
+    ctx.fillStyle="white";
+    ctx.fill();
+    ctx.fillStyle="black";
+    ctx.fillText(num, 30, 18);
+}
+
+function start() {
+    for (i = 0; i < testDivs.length; ++i) {
+        var title = testDivs[i].id.toString();
+        var str = testDivs[i].firstChild.data;
+        parse(str, title);
+    }
+    drawTop();
+    window.addEventListener('keypress', doKeyPress, true);
+    window.onresize = function() {
+        drawTop();
+    }
+}
+
+</script>
+</head>
+
+<body onLoad="start();">
+<canvas id="canvas" width="750" height="500"
+    onmousemove="handleMouseOver()"
+    onclick="handleMouseClick()"
+    ></canvas >
+</body>
+</html>
diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker.sln b/experimental/LightSymbolsUtil/Callstacker/Callstacker.sln
new file mode 100644
index 0000000..3be208d
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker.sln
@@ -0,0 +1,20 @@
+

+Microsoft Visual Studio Solution File, Format Version 11.00

+# Visual Studio 2010

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Callstacker", "Callstacker\Callstacker.vcxproj", "{A0E72F45-561E-4B28-B9B2-6C9E9F6BA8BA}"

+EndProject

+Global

+	GlobalSection(SolutionConfigurationPlatforms) = preSolution

+		Debug|Win32 = Debug|Win32

+		Release|Win32 = Release|Win32

+	EndGlobalSection

+	GlobalSection(ProjectConfigurationPlatforms) = postSolution

+		{A0E72F45-561E-4B28-B9B2-6C9E9F6BA8BA}.Debug|Win32.ActiveCfg = Debug|Win32

+		{A0E72F45-561E-4B28-B9B2-6C9E9F6BA8BA}.Debug|Win32.Build.0 = Debug|Win32

+		{A0E72F45-561E-4B28-B9B2-6C9E9F6BA8BA}.Release|Win32.ActiveCfg = Release|Win32

+		{A0E72F45-561E-4B28-B9B2-6C9E9F6BA8BA}.Release|Win32.Build.0 = Release|Win32

+	EndGlobalSection

+	GlobalSection(SolutionProperties) = preSolution

+		HideSolutionNode = FALSE

+	EndGlobalSection

+EndGlobal

diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.cpp b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.cpp
new file mode 100644
index 0000000..348e657
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.cpp
@@ -0,0 +1,673 @@
+// Callstacker.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+
+#include <string>
+#include <map>
+#include <vector>
+
+using namespace std;
+
+// can't delete, only add files repository!
+class SkSourceDb {
+public:
+  SkSourceDb(const char* szBaseSrcPath, const char* szLightSymbolsDbFile) {
+    this->baseSrcPath = szBaseSrcPath;
+    this->lightSymbolsDbFile = szLightSymbolsDbFile;
+    nextId = 1;
+  }
+
+  const string& getBaseSrcPath() const {
+    return baseSrcPath;
+  }
+
+  string GetStoredFilename(const string& filename) {
+    string base = filename.substr(0, baseSrcPath.length());
+    if (base != baseSrcPath) {
+      return "";
+    }
+
+    string relative = filename.substr(baseSrcPath.length());
+    char tmp[10000];
+    strcpy(tmp, relative.c_str()); // insecure
+    char* sz = tmp;
+    while (*sz) {
+      if (*sz == '\\') *sz = '/';
+      sz++;
+    }
+    sz = tmp;
+    if (*sz == '/') sz++;
+
+    return string(sz);
+  }
+
+  int obtainFileId(const string& filename) {
+    string stored = GetStoredFilename(filename);
+    if (stored.empty()) {
+      return -1;
+    }
+
+    if (filenames.find(stored) == filenames.end()) {
+      int id = nextId;
+      nextId++;
+      filenames[stored] = id;
+      return id;
+    } else {
+      return filenames[stored];
+    }
+  }
+
+  static void Load(char* szFileName, SkSourceDb** ret, const char* whereToSave, const char* szBaseSrcPath) {
+    char szLine[10000];
+    FILE* file = fopen(szFileName, "rt");
+    if (file == NULL) {
+      *ret = NULL;
+      return;
+    }
+
+    const char* trimed;
+    SkSourceDb* db = new SkSourceDb(szBaseSrcPath, whereToSave == NULL ? szFileName : whereToSave);
+
+    map<int, string> ids;
+    int id;
+    while (true) {
+      id = -1;
+      if (fscanf(file, "%i", &id) == 0) break;
+      if (id == -1) break;
+      *szLine = '\0';
+      fgets(szLine, 10000, file);
+      trimed = trim(szLine);
+
+      if (id < 0 || ids[id] != "") {
+        printf("fatal error: duplicate value for id = %i, existing = \"%s\", new = \"%s\"\n", id, ids[id].c_str(), trimed);
+        exit(-1);
+      }
+
+      if (trimed == NULL || *trimed == '\0') {
+        printf("fatal error: no valuefor id = %i\n", id);
+        exit(-1);
+      }
+
+      if (db->filenames.find(trimed) != db->filenames.end()) {
+        printf("fatal error: duplicate id for same file: file = %s, existing id = %i, new id = %i\n", trimed, db->filenames[trimed], id);
+//        exit(-1);
+      }
+
+      string value = trimed;
+      ids[id] = value;
+      db->filenames[value] = id;
+      if (db->nextId <= id) {
+        db->nextId = id + 1;
+      }
+    }
+
+    *ret = db;
+  }
+
+  // dumb comit, smarter: use append
+  void commit() {
+    save(lightSymbolsDbFile.c_str());
+  }
+
+private:
+
+  static const char* trim(char* sz) {
+    if (sz == NULL) return NULL;
+
+    while (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',')
+      sz++;
+
+    if (*sz == '\0') return sz;
+
+    int len = strlen(sz);
+    char* start = sz;
+    sz = sz + (len - 1);
+
+    while (sz >= start && (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',')) {
+      *sz = '\0';
+      sz--;
+    }
+
+    return start;
+  }
+
+  void save(const char* szFilename) {
+    char szLine[10000];
+    FILE* file = fopen(szFilename, "wt");
+
+    map<string, int>::const_iterator itr;
+
+    for(itr = filenames.begin(); itr != filenames.end(); ++itr){
+      fprintf(file, "%i, %s\n", (*itr).second, (*itr).first.c_str());
+    }
+    fclose(file);
+  }
+
+  string baseSrcPath;
+  string lightSymbolsDbFile;
+  map<string, int> filenames;
+  int nextId;
+};
+
+SkSourceDb* source_db = NULL;
+
+bool endsWith(const char* who, const char* what) {
+    int a = strlen(who);
+    int b = strlen(what);
+    return stricmp(who + a - b, what) == 0; // insecure
+}
+
+bool sourceFile(const char* szFileName) {
+    return endsWith(szFileName, ".h") || endsWith(szFileName, ".c") || endsWith(szFileName, ".cpp") || endsWith(szFileName, ".cc");
+}
+
+// "
+// //
+// /*
+class CppState {
+public:
+
+    CppState() : line(1), inComment(false), inLineComment(false), inDoubleQuote(false), inSingleQuote(false), isEscaping(false), commentEnding(false), commentMightBeStarting(false) {
+    }
+
+    void apply(int ch) {
+        if (ch == '\n') {
+            line++;
+            if (inLineComment) inLineComment = false;
+        }
+
+        if (inLineComment) {
+            return;
+        }
+
+        if (commentMightBeStarting) {
+            commentMightBeStarting = false;
+            if (ch == '*') {
+                inComment = true;
+            } else if (ch == '/') {
+                inLineComment = true;
+            } else {
+                add('/');//previously we has / but was not pushed on tokens
+                newToken();//
+            }
+        }
+
+        if (inSingleQuote) {
+            if (isEscaping)
+                isEscaping = false;
+            else if (ch == '\\')
+                isEscaping = true;
+            else if (ch == '\'') {
+                inSingleQuote = false;
+                newToken();
+                pushToken("__SINGLE_QUOTE__");
+                newToken();
+            }
+
+            return;
+        } else if (inDoubleQuote) {
+            if (isEscaping)
+                isEscaping = false;
+            else if (ch == '\\')
+                isEscaping = true;
+            else if (ch == '\"') {
+                inDoubleQuote = false;
+                newToken();
+                pushToken("__DOUBLE_QUOTE__");
+                newToken();
+            }
+
+            return;
+        } else if (inComment) {
+            if (ch == '*') {
+                commentEnding = true;
+            } else if (ch == '/') {
+                inComment = false;
+                commentEnding = false;
+            } else {
+                commentEnding = false;
+            }
+
+            return;
+        }
+
+        switch (ch) {
+        case '\'':
+            newToken();
+            inSingleQuote = true;
+            return;
+
+        case '\"':
+            newToken();
+            inDoubleQuote = true;
+            return;
+
+        case '/':
+            newToken();
+            commentMightBeStarting = true;
+            return;
+        }
+
+        if (isspace(ch)) {
+            newToken();
+        } else if (tokenDelimiter(ch)) {
+            newToken();
+            if (isSingleCharToken(ch)) {
+                add(ch);
+                newToken();
+            }
+        } else if (isTokenable(ch)) {
+              add(ch);
+        } else {
+            printf("undexpected ... %c", (char)ch);
+        }
+    }
+
+    bool enteredFunction() {
+        if (inComment || inLineComment || inDoubleQuote || inSingleQuote || commentMightBeStarting) {
+            return false;
+        }
+
+        if (tokens.size() == 0) {
+            return false;
+        }
+
+        if (tokens[tokens.size() - 1] != "{") {
+            return false;
+        }
+
+        int i = tokens.size() - 2;
+
+        bool foundCloseBraket = false;
+        int innerBrakets = 0;
+        bool foundOpenBraket = false;
+    int iName = -1;
+
+        while (i >= 0) {
+            string t_i = tokens[i]; // debugging sucks!
+
+      if (!foundCloseBraket && (tokens[i] == "enum"
+                             || tokens[i] == "struct"
+                             || tokens[i] == "class"
+                             || tokens[i] == "namespace"
+                             || tokens[i] == "public"
+                             || tokens[i] == "private"
+                             || tokens[i] == "protected"
+                             || tokens[i] == "__asm"
+                             || tokens[i] == "catch"
+                             || tokens[i] == "__except"
+                             )) {
+        return false;
+      }
+
+            if (tokens[i] == ")") {
+                if (foundCloseBraket)
+                    innerBrakets++;
+                else if (i >= 3 && tokens[i - 1] == "." && tokens[i - 2] == "." && tokens[i - 3] == ".") {
+                    i -= 3;
+                }
+                foundCloseBraket = true;
+            } else if (tokens[i] == "(" && innerBrakets > 0) {
+                innerBrakets--;
+            } else if (tokens[i] == "(" && innerBrakets == 0) {
+                foundOpenBraket = true;
+                i--; if ( i < 0) return false;
+                string name = tokens[i];
+        iName = i;
+
+                if (name == "if" || name == "while" || name == "switch"|| name == "for") {
+                    return false;
+                }
+
+                if (!CouldBeFunctionName(name)) return false;
+                if (i >= 6 && tokens[i - 1] == ":" && tokens[i - 2] == ":" && CouldBeClassnName(tokens[i - 3]) && tokens[i - 4] == ":" && tokens[i - 5] == ":" && CouldBeClassnName(tokens[i - 6])) {
+                    name =  tokens[i - 6] + "::" + tokens[i - 3] + "::" + name;
+          iName = i - 6;
+                    if (i >= 7 && (tokens[i - 7] == ":" || tokens[i-7] == ",")) {
+                        i -= 7 + 1;
+                        name = "";
+                        foundCloseBraket = false;
+                        foundOpenBraket = false;
+                        innerBrakets = 0;
+                        continue;
+                    }
+                }
+                else if (i >= 3 && tokens[i - 1] == ":" && tokens[i - 2] == ":" && CouldBeClassnName(tokens[i - 3])) {
+                    name = tokens[i - 3] + "::" + name;
+          iName = i - 3;
+                    if (i >= 4 && (tokens[i - 4] == ":" || tokens[i-4] == ",")) {
+                        i -= 4 + 1;
+                        name = "";
+                        foundCloseBraket = false;
+                        foundOpenBraket = false;
+                        innerBrakets = 0;
+                        continue;
+                    }
+                }
+                else if (i >= 1 && (tokens[i - 1] == ":" || tokens[i-1] == ",")) {
+                    i -= 1 + 1;
+                    name = "";
+                    foundCloseBraket = false;
+                    foundOpenBraket = false;
+                    innerBrakets = 0;
+                    continue;
+                }
+
+                if (name == "") {
+                    return false;
+                }
+
+        if (iName >= 2 && tokens[iName - 2] == "#" && tokens[iName - 1] == "define") {
+          return false;
+        }
+
+        if (iName >= 1 && (tokens[i - 1] == "enum"
+                        || tokens[i - 1] == "struct"
+                        || tokens[i - 1] == "class"
+                        || tokens[i - 1] == "namespace"
+                        || tokens[i - 1] == "public"
+                        || tokens[i - 1] == "private"
+                        || tokens[i - 1] == "protected"
+                        || tokens[i - 1] == "__asm"
+                        || tokens[i - 1] == "if"
+                        || tokens[i - 1] == "while"
+                        || tokens[i - 1] == "for"
+                        || tokens[i - 1] == "switch"
+                        || tokens[i - 1] == "!"
+                        )) {
+          return false;
+        }
+
+        int k = 10;
+        i = iName - 2;
+        bool isInline = false;// heuristic for inline functions
+        while (k > 0 && i >= 0) {
+          if (tokens[i] == "inline") {
+            isInline = true;
+            break;
+          }
+          if (tokens[i] == ";" || tokens[i] == "{" || tokens[i] == "}") {
+            break;
+          }
+          i--;
+          k--;
+        }
+
+        if (isInline) return false; //do not trace inline functions
+
+                lastFunctionName = name;
+                return true;
+            } else {
+                if (!foundCloseBraket) {
+                    if (!IgnorableFunctionModifier(tokens[i])) {
+                        return false;
+                    }
+                } else {
+                    if (!IgnorableFunctionParameter(tokens[i])) {
+                        return false;
+                    }
+                }
+            }
+
+            i--;
+        }
+
+        return false;
+    }
+
+    const char* functionName() {
+        return lastFunctionName.c_str();
+    }
+
+    int lineNumber() {
+        return line;
+    }
+
+private:
+
+    bool CouldBeFunctionName(const string& str) {
+        if (str.empty()) return false;
+        if (!isalpha(str[0]) && str[0] != '_' && str[0] != '~' && str[0] != ':') return false;
+        for (int i = 1; i < str.length(); i++) {
+            if (!isalpha(str[i]) && !isdigit(str[i]) && str[i] != '_' && str[i] != ':') return false;
+        }
+
+        return true;
+    }
+
+    bool isNumber(const string& str) {
+        if (str.empty()) return false;
+        for (int i = 0; i < str.length(); i++) {
+            if (!isdigit(str[i]) && str[i] != '.' && str[i] != 'x' && str[i] != 'X' && str[i] != 'e' && str[i] != 'E') return false;
+        }
+
+        return true;
+    }
+
+
+    bool isOperator(const string& str) {
+        if (str.empty()) return false;
+        for (int i = 1; i < str.length(); i++) {
+            switch (str[i]) {
+            case '<':
+            case '>':
+            case '=':
+            case '+':
+            case '-':
+            case '*':
+            case '/':
+            case '(':
+            case ')':
+            case '[':
+            case ']':
+            case '!':
+            case '|':
+            case '&':
+            case '^':
+            case '%':
+                break;
+            default:
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    bool CouldBeClassnName(const string& str) {
+        return CouldBeFunctionName(str);
+    }
+
+    bool IgnorableFunctionModifier(const string& str) {
+        return str.empty() || CouldBeFunctionName(str);
+    }
+
+    bool IgnorableFunctionParameter(const string& str) {
+        if (str.empty()) return true;
+        if (CouldBeFunctionName(str)) return true;
+        if (str == ",") return true;
+        if (str == "*") return true;
+        if (str == "=") return true;
+        if (str == "&") return true;
+        if (str == "<") return true;
+        if (str == ">") return true;
+        if (str == ":") return true;
+        if (str == "=") return true;
+        if (isNumber(str)) return true;
+        if (str == "]") return true;
+        if (str == "[") return true;
+
+    if (str == ";") return false;
+
+        return false;
+    }
+
+
+    bool tokenDelimiter(int ch) {
+        if (isspace(ch))    return true;
+        if (isdigit(ch))    return false;
+        if (isalpha(ch))    return false;
+        if (ch == '_')        return false;
+                            return true;
+    }
+
+    bool isTokenable(int ch) {
+        if (isdigit(ch))    return true;
+        if (isalpha(ch))    return true;
+        if (ch == '_')        return true;
+                            return false;
+    }
+
+    bool isSingleCharToken(int ch) {
+        if (isspace(ch))    return false;
+        if (isdigit(ch))    return false;
+        if (isalpha(ch))    return false;
+        if (ch == '_')        return false;
+                            return true;
+    }
+
+    void add(char ch) {
+      token += ch;
+    }
+
+    void pushToken(const char* sz) {
+        newToken();
+        token = sz;
+        newToken();
+    }
+
+    void newToken() {
+        if (token.empty()) return;
+
+        if (tokens.size() > 0) {
+            string last = tokens[tokens.size() -1];
+            if (last == "operator") {
+                if (isOperator(op + token)) {
+                    op += token;
+                    token = "";
+                    return;
+                } else if (op != "" && isOperator(op)) {
+                    tokens[tokens.size() -1] = last + op;
+                    op = "";
+                    return;
+                } else if (isOperator(token)) {
+                    op = token;
+                    token = "";
+                    return;
+                } else {
+                    // compile error?
+                    op = "";
+                }
+            } else if (last == "~") {
+                tokens[tokens.size() -1] = last + token;
+                token = "";
+                return;
+            }
+        }
+
+        tokens.push_back(token);
+        token = "";
+    }
+
+    int line;
+    vector<string> tokens;
+    string token;
+    string lastFunctionName;
+
+    bool inComment;
+    bool inLineComment;
+    bool inDoubleQuote;
+    bool inSingleQuote;
+    bool isEscaping;
+    bool commentEnding;
+    bool commentMightBeStarting;
+
+    string op;
+};
+
+char output[100000000];
+char* now;
+
+
+void emit(char ch) {
+  *now = ch;
+  now++;
+}
+
+void emit(const char* szCode, const char* szFunctionName, int fileId, int line) {
+    sprintf(now, szCode, szFunctionName, fileId, line);
+    while (*now) {
+        now++;
+    }
+}
+
+void runFile(const char* szFileNameInput, const char* szFileNameOutput, const char* szInclude) {
+  printf("%s\n", szFileNameInput);
+
+
+  if (!sourceFile(szFileNameInput))
+    return;
+
+  now = output;
+  int fileId = source_db->obtainFileId(szFileNameInput);
+  FILE* file = fopen(szFileNameInput, "rt");
+  int ch;
+  CppState state;
+  while (true) {
+    int ch = getc(file);
+    if (ch == -1)
+        break;
+    state.apply(ch);
+    emit(ch);
+    if (ch == '{' && state.enteredFunction()) { // {
+      emit("LS_TRACE(\"%s\", %i, %i);", state.functionName(), fileId, state.lineNumber()); // light symbol traces, create a macro to define it
+    }
+  }
+  fclose(file);
+
+  file = fopen(szFileNameOutput, "wt");
+  // TODO: input parameter
+  fprintf(file, "#include \"%s\"\n", szInclude);
+  fwrite(output, 1, now - output, file);
+  fclose(file);
+  //source_db->commit();
+}
+
+// to create the list file:
+// dir *.cpp;*.h;*.cc /s /b
+void runAll(char* szFileHolder, const char* szInclude) {
+  FILE* file = fopen(szFileHolder, "rt");
+  if (file == NULL) {
+    return;
+  }
+
+  while (true) {
+    char szFileName[10000] = "";
+    fgets(szFileName, 10000, file);
+      char* end = szFileName + strlen(szFileName) - 1;
+      while (end > szFileName && (*end == '\n' || *end == '\r' || *end == ' ' || *end == '\t')) {
+        *end = 0;
+        end--;
+      }
+    if (strlen(szFileName) == 0)
+        break;
+
+  runFile(szFileName, szFileName, szInclude);
+  }
+  fclose(file);
+  source_db->commit();
+}
+
+int _tmain(int argc, char* argv[])
+{
+  // base path, include, list.txt, lightSymbolFile, [lightSymbolsOut]
+  SkSourceDb::Load(argv[4], &source_db, argc == 5 ? argv[4] : argv[5], argv[1]);
+  if (source_db == NULL) {
+    source_db = new SkSourceDb(argv[1], argv[4]);
+  }
+
+  runAll(argv[3], argv[2]); // e.g. foo\\src\\lightsymbols\\lightsymbols.h");
+
+  return 0;
+}
diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj
new file mode 100644
index 0000000..61411c4
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>

+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

+  <ItemGroup Label="ProjectConfigurations">

+    <ProjectConfiguration Include="Debug|Win32">

+      <Configuration>Debug</Configuration>

+      <Platform>Win32</Platform>

+    </ProjectConfiguration>

+    <ProjectConfiguration Include="Release|Win32">

+      <Configuration>Release</Configuration>

+      <Platform>Win32</Platform>

+    </ProjectConfiguration>

+  </ItemGroup>

+  <PropertyGroup Label="Globals">

+    <ProjectGuid>{A0E72F45-561E-4B28-B9B2-6C9E9F6BA8BA}</ProjectGuid>

+    <Keyword>Win32Proj</Keyword>

+    <RootNamespace>Callstacker</RootNamespace>

+  </PropertyGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

+    <ConfigurationType>Application</ConfigurationType>

+    <UseDebugLibraries>true</UseDebugLibraries>

+    <CharacterSet>NotSet</CharacterSet>

+  </PropertyGroup>

+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

+    <ConfigurationType>Application</ConfigurationType>

+    <UseDebugLibraries>false</UseDebugLibraries>

+    <WholeProgramOptimization>true</WholeProgramOptimization>

+    <CharacterSet>NotSet</CharacterSet>

+  </PropertyGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

+  <ImportGroup Label="ExtensionSettings">

+  </ImportGroup>

+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">

+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  </ImportGroup>

+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  </ImportGroup>

+  <PropertyGroup Label="UserMacros" />

+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">

+    <LinkIncremental>true</LinkIncremental>

+  </PropertyGroup>

+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

+    <LinkIncremental>false</LinkIncremental>

+  </PropertyGroup>

+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">

+    <ClCompile>

+      <PrecompiledHeader>

+      </PrecompiledHeader>

+      <WarningLevel>Level3</WarningLevel>

+      <Optimization>Disabled</Optimization>

+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>

+    </ClCompile>

+    <Link>

+      <SubSystem>Console</SubSystem>

+      <GenerateDebugInformation>true</GenerateDebugInformation>

+    </Link>

+  </ItemDefinitionGroup>

+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

+    <ClCompile>

+      <WarningLevel>Level3</WarningLevel>

+      <PrecompiledHeader>

+      </PrecompiledHeader>

+      <Optimization>MaxSpeed</Optimization>

+      <FunctionLevelLinking>true</FunctionLevelLinking>

+      <IntrinsicFunctions>true</IntrinsicFunctions>

+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>

+    </ClCompile>

+    <Link>

+      <SubSystem>Console</SubSystem>

+      <GenerateDebugInformation>true</GenerateDebugInformation>

+      <EnableCOMDATFolding>true</EnableCOMDATFolding>

+      <OptimizeReferences>true</OptimizeReferences>

+    </Link>

+  </ItemDefinitionGroup>

+  <ItemGroup>

+    <None Include="ReadMe.txt" />

+  </ItemGroup>

+  <ItemGroup>

+    <ClInclude Include="stdafx.h" />

+    <ClInclude Include="targetver.h" />

+  </ItemGroup>

+  <ItemGroup>

+    <ClCompile Include="Callstacker.cpp" />

+    <ClCompile Include="stdafx.cpp" />

+  </ItemGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+  <ImportGroup Label="ExtensionTargets">

+  </ImportGroup>

+</Project>
\ No newline at end of file
diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj.filters b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj.filters
new file mode 100644
index 0000000..b7e939c
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj.filters
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>

+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

+  <ItemGroup>

+    <Filter Include="Source Files">

+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>

+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>

+    </Filter>

+    <Filter Include="Header Files">

+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>

+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>

+    </Filter>

+    <Filter Include="Resource Files">

+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>

+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>

+    </Filter>

+  </ItemGroup>

+  <ItemGroup>

+    <None Include="ReadMe.txt" />

+  </ItemGroup>

+  <ItemGroup>

+    <ClInclude Include="stdafx.h">

+      <Filter>Header Files</Filter>

+    </ClInclude>

+    <ClInclude Include="targetver.h">

+      <Filter>Header Files</Filter>

+    </ClInclude>

+  </ItemGroup>

+  <ItemGroup>

+    <ClCompile Include="stdafx.cpp">

+      <Filter>Source Files</Filter>

+    </ClCompile>

+    <ClCompile Include="Callstacker.cpp">

+      <Filter>Source Files</Filter>

+    </ClCompile>

+  </ItemGroup>

+</Project>
\ No newline at end of file
diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj.user b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj.user
new file mode 100644
index 0000000..797bf6a
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/Callstacker.vcxproj.user
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>

+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">

+    <LocalDebuggerCommandArguments>D:\edisonn\chromium1_new\ D:\edisonn\chromium1_new\all.txt D:\edisonn\chromium1_new\lightsymbols.txt</LocalDebuggerCommandArguments>

+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>

+  </PropertyGroup>

+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

+    <LocalDebuggerCommandArguments>D:\edisonn\chromium1_new\ D:\edisonn\chromium1_new\all.txt D:\edisonn\chromium1_new\lightsymbols.txt</LocalDebuggerCommandArguments>

+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>

+  </PropertyGroup>

+</Project>
\ No newline at end of file
diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/ReadMe.txt b/experimental/LightSymbolsUtil/Callstacker/Callstacker/ReadMe.txt
new file mode 100644
index 0000000..8f597bf
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/ReadMe.txt
@@ -0,0 +1,40 @@
+========================================================================

+    CONSOLE APPLICATION : Callstacker Project Overview

+========================================================================

+

+AppWizard has created this Callstacker application for you.

+

+This file contains a summary of what you will find in each of the files that

+make up your Callstacker application.

+

+

+Callstacker.vcxproj

+    This is the main project file for VC++ projects generated using an Application Wizard.

+    It contains information about the version of Visual C++ that generated the file, and

+    information about the platforms, configurations, and project features selected with the

+    Application Wizard.

+

+Callstacker.vcxproj.filters

+    This is the filters file for VC++ projects generated using an Application Wizard. 

+    It contains information about the association between the files in your project 

+    and the filters. This association is used in the IDE to show grouping of files with

+    similar extensions under a specific node (for e.g. ".cpp" files are associated with the

+    "Source Files" filter).

+

+Callstacker.cpp

+    This is the main application source file.

+

+/////////////////////////////////////////////////////////////////////////////

+Other standard files:

+

+StdAfx.h, StdAfx.cpp

+    These files are used to build a precompiled header (PCH) file

+    named Callstacker.pch and a precompiled types file named StdAfx.obj.

+

+/////////////////////////////////////////////////////////////////////////////

+Other notes:

+

+AppWizard uses "TODO:" comments to indicate parts of the source code you

+should add to or customize.

+

+/////////////////////////////////////////////////////////////////////////////

diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/stdafx.cpp b/experimental/LightSymbolsUtil/Callstacker/Callstacker/stdafx.cpp
new file mode 100644
index 0000000..e746ff2
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// Callstacker.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/stdafx.h b/experimental/LightSymbolsUtil/Callstacker/Callstacker/stdafx.h
new file mode 100644
index 0000000..b005a83
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/stdafx.h
@@ -0,0 +1,15 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/experimental/LightSymbolsUtil/Callstacker/Callstacker/targetver.h b/experimental/LightSymbolsUtil/Callstacker/Callstacker/targetver.h
new file mode 100644
index 0000000..87c0086
--- /dev/null
+++ b/experimental/LightSymbolsUtil/Callstacker/Callstacker/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
diff --git a/experimental/LightSymbolsUtil/lightsymbols/helper.h b/experimental/LightSymbolsUtil/lightsymbols/helper.h
new file mode 100644
index 0000000..62f375a
--- /dev/null
+++ b/experimental/LightSymbolsUtil/lightsymbols/helper.h
@@ -0,0 +1,65 @@
+#include <stdlib.h>
+#define CANVAS_PATH "CANVAS_PATH"
+
+class SkFile {
+  FILE* file;
+  bool busted;
+  char* sz;
+  mutable int i;
+
+public:
+  SkFile(unsigned long id) {
+    file = NULL;
+    busted = false;
+    sz = new char[100000];
+    set(id);
+    i = 100;
+  }
+
+  ~SkFile() {
+    delete sz;
+    if (file) {
+      fclose(file);
+    }
+  }
+
+  void set(unsigned long id) {
+    if (busted) {
+      return;
+    }
+
+    if (file == NULL) {
+      char sz[10000];
+      sprintf(sz, "%s\\%ul.callstacks.txt", getenv(CANVAS_PATH), id);
+      file = fopen(sz, "a");
+      if (file == NULL) {
+        busted = true;
+      }
+      fprintf(file, "\n\n\nNEW SESSION, just coliding ids ... should generate a new file ideally ...  \n\n\n");
+    }
+  }
+
+  void appendLine(const char* sz) const {
+    if (busted) {
+      return;
+    }
+
+    fprintf(file, "%s\n", sz);
+  }
+
+  void dump(bool flush = false) const {
+    if (busted) {
+      return;
+    }
+
+    LightSymbol::GetCallStack(sz, 100000, " >- ");
+    appendLine(sz);
+
+    i--;
+
+    if (i < 0 || flush) {
+      i = 100;
+      fflush(file);
+    }
+  }
+};
diff --git a/experimental/LightSymbolsUtil/lightsymbols/lightsymbols.cc b/experimental/LightSymbolsUtil/lightsymbols/lightsymbols.cc
new file mode 100644
index 0000000..230faf5
--- /dev/null
+++ b/experimental/LightSymbolsUtil/lightsymbols/lightsymbols.cc
@@ -0,0 +1,169 @@
+#include "lightsymbols.h"

+

+LightSymbol::PLightSymbol LightSymbol::lsFrames[1000];

+HANDLE LightSymbol::handleFrames[1000];

+SZ* LightSymbol::fileNames;

+bool LightSymbol::busted = false;

+

+

+LightSymbol::LightSymbol(const char* sym, int fileId, int lineNumber) {

+  while (busted) {

+    busted = busted;

+  }

+  this->sym = sym;

+  this->fileId = fileId;

+  this->lineNumber = lineNumber;

+

+  LightSymbol** container = getThreadFrameContainer();

+

+  parentFrame = *container;

+  *container = this; // shortcut for get+set current frame

+}

+

+LightSymbol::~LightSymbol() {

+

+// assert  if (GetCurrentFrame() != this) {

+

+  SetCurrentFrame(parentFrame);

+}

+

+bool LightSymbol::GetCallStack(char* sz, int len, const char* separator) {

+  LightSymbol* ls = GetCurrentFrame();

+  if (ls == 0) {

+    return false;

+  } else {

+    return ls->GetCallStackCore(sz, len, separator);

+  }

+}

+

+LightSymbol** LightSymbol::getThreadFrameContainer() {

+  //pthread_t t = pthread_self();

+  HANDLE h = (HANDLE)GetCurrentThreadId(); // f, keep handle so I don't have to recompie tyhe whole app; update toi DWORD one I really need changes in header file

+  int i = 0; 

+  while (handleFrames[i] != h && handleFrames[i] != NULL && i < 1000 - 1) {

+    i++;

+  }

+  if (handleFrames[i] == h) {

+    return &lsFrames[i];

+  }

+  handleFrames[i] = h;

+  return &lsFrames[i];

+}

+

+bool LightSymbol::GetCallStackCore(char* sz, int len, const char* separator) const {

+  if (busted) {

+    return false;

+  }

+  if (fileNames == NULL) { // f multithreading synchr

+    FILE* log = fopen("d:\\edisonn\\log.txt", "wt");

+

+    if (log) { fprintf(log, "build\n");fflush(log); }

+    

+    char szLine[10000];

+    FILE* file = fopen(getenv(LIGHT_SYMBOLS_FILE), "rt");

+    if (file == NULL) {

+      busted = true;

+      return false;

+    }

+

+    const char* trimed;

+      

+    // count number of lines

+    int id;

+    int entries = 0;

+    while (true) {

+      id = -1;

+      if (fscanf(file, "%i", &id) == 0) break;

+      if (id == -1) break;

+      if (entries <= id + 1) {

+        entries = id + 1;

+      }

+      *szLine = '\0';

+      fgets(szLine, 10000, file);

+      trimed = trim(szLine);

+    }

+

+    fclose(file);

+    file = fopen(getenv(LIGHT_SYMBOLS_FILE), "rt");

+    if (file == NULL) {

+      busted = true;

+      return false; // f this

+    }

+

+    if (log) { fprintf(log, "entries: %i\n", entries);fflush(log); }

+

+    SZ* __fileNames = new SZ[entries];

+

+    while (true) {

+      id = -1;

+      if (fscanf(file, "%i", &id) == 0) break;

+      if (id == -1) break;

+      *szLine = '\0';

+      fgets(szLine, 10000, file);

+      trimed = trim(szLine);

+

+      if (log) { fprintf(log, "%i, %s", id, trimed); }

+

+      // ass u me the file is correct

+

+      __fileNames[id] = new char[strlen(trimed) + 1];

+      if (log) { fprintf(log, " - ");fflush(log); }

+      strcpy(__fileNames[id], trimed);

+      if (log) { fprintf(log, " _ \n");fflush(log); }

+    }

+    fclose(file);

+    fileNames = __fileNames;

+    if (log) { fclose(log); }

+  }

+

+  const LightSymbol* ls = this;

+  char* szOut = sz;

+  // f security

+  while (ls != NULL && len > 1000) {

+    sprintf(szOut, "%s, %s:%i%s", ls->sym, fileNames[ls->fileId], ls->lineNumber, separator);

+    while (*szOut && len > 0) {

+      szOut++;

+      len--;

+    }

+    ls = ls->parentFrame;

+  }

+

+  int more = 0;

+  while (ls != NULL) {

+    ls = ls->parentFrame;

+  }

+

+  if (more > 0) {

+    sprintf(szOut, " ... %i more frames. allocate more memory!", more);

+  }

+

+  return true;

+}

+

+LightSymbol* LightSymbol::GetCurrentFrame() {

+  return *getThreadFrameContainer();

+}

+

+void LightSymbol::SetCurrentFrame(LightSymbol* ls) {

+  *getThreadFrameContainer() = ls;

+}

+

+const char* LightSymbol::trim(char* sz) {

+  if (sz == NULL) return NULL;

+    

+  while (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',')

+    sz++;

+

+  if (*sz == '\0') return sz;

+

+  int len = strlen(sz);

+  char* start = sz;

+  sz = sz + (len - 1);

+

+  while (sz >= start && (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',')) {

+    *sz = '\0';

+    sz--;

+  }

+

+  return start;

+}

diff --git a/experimental/LightSymbolsUtil/lightsymbols/lightsymbols.h b/experimental/LightSymbolsUtil/lightsymbols/lightsymbols.h
new file mode 100644
index 0000000..53c2f36
--- /dev/null
+++ b/experimental/LightSymbolsUtil/lightsymbols/lightsymbols.h
@@ -0,0 +1,50 @@
+#ifndef __LIGHT_SYMBOLS__
+#define __LIGHT_SYMBOLS__
+#define LS_TRACE(functionName,fileId,lineNumber) LightSymbol __lstr(functionName,fileId,lineNumber);
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//#include <pthread.h>
+#include <Windows.h>
+
+typedef char* SZ;
+
+#define LIGHT_SYMBOLS_FILE "LIGHT_SYMBOLS_FILE"
+
+class LightSymbol {
+  const char* sym;
+  int fileId;
+  int lineNumber;
+
+  LightSymbol* parentFrame;
+
+  typedef LightSymbol* PLightSymbol;
+
+  static PLightSymbol lsFrames[1000];
+  static HANDLE handleFrames[1000];
+  static SZ* fileNames;
+  static bool busted;
+
+public:
+  LightSymbol(const char* sym, int fileId, int lineNumber);
+
+  ~LightSymbol();
+
+  static bool GetCallStack(char* sz, int len, const char* separator);
+
+private:
+
+  static LightSymbol** getThreadFrameContainer();
+
+  bool GetCallStackCore(char* sz, int len, const char* separator) const ;
+
+  static LightSymbol* GetCurrentFrame() ;
+
+  static void SetCurrentFrame(LightSymbol* ls) ;
+
+  static const char* trim(char* sz) ;
+};
+
+#endif
diff --git a/experimental/Networking/SampleNetPipeReader.cpp b/experimental/Networking/SampleNetPipeReader.cpp
index 4332cbd..aef5653 100644
--- a/experimental/Networking/SampleNetPipeReader.cpp
+++ b/experimental/Networking/SampleNetPipeReader.cpp
@@ -127,4 +127,3 @@
 
 static SkView* MyFactory() { return new NetPipeReaderView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/experimental/SimpleCocoaApp/SimpleApp.h b/experimental/SimpleCocoaApp/SimpleApp.h
index c36007c..fe47a4a 100644
--- a/experimental/SimpleCocoaApp/SimpleApp.h
+++ b/experimental/SimpleCocoaApp/SimpleApp.h
@@ -14,4 +14,3 @@
 @interface SimpleNSView : SkNSView
 - (id)initWithDefaults;
 @end
-
diff --git a/experimental/SimpleCocoaApp/SimpleApp.mm b/experimental/SimpleCocoaApp/SimpleApp.mm
index 922177e..7dac2a4 100644
--- a/experimental/SimpleCocoaApp/SimpleApp.mm
+++ b/experimental/SimpleCocoaApp/SimpleApp.mm
@@ -1,8 +1,53 @@
-#import "SkCanvas.h"
-#import "SkPaint.h"
-#import "SkWindow.h"
-#include "SkGraphics.h"
+#include "SkCanvas.h"
 #include "SkCGUtils.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkOSFile.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkWindow.h"
+
+static void make_filepath(SkString* path, const char* dir, const SkString& name) {
+    size_t len = strlen(dir);
+    path->set(dir);
+    if (len > 0 && dir[len - 1] != '/') {
+        path->append("/");
+    }
+    path->append(name);
+}
+
+static SkPicture* LoadPicture(const char path[]) {
+    SkPicture* pic = NULL;
+
+    SkBitmap bm;
+    if (SkImageDecoder::DecodeFile(path, &bm)) {
+        bm.setImmutable();
+        pic = SkNEW(SkPicture);
+        SkCanvas* can = pic->beginRecording(bm.width(), bm.height());
+        can->drawBitmap(bm, 0, 0, NULL);
+        pic->endRecording();
+    } else {
+        SkFILEStream stream(path);
+        if (stream.isValid()) {
+            pic = SkNEW_ARGS(SkPicture,
+                             (&stream, NULL, &SkImageDecoder::DecodeStream));
+        }
+
+        if (false) { // re-record
+            SkPicture p2;
+            pic->draw(p2.beginRecording(pic->width(), pic->height()));
+            p2.endRecording();
+
+            SkString path2(path);
+            path2.append(".new.skp");
+            SkFILEWStream writer(path2.c_str());
+            p2.serialize(&writer);
+        }
+    }
+    return pic;
+}
+
 class SkSampleView : public SkView {
 public:
     SkSampleView() {
@@ -16,17 +61,216 @@
         p.setTextSize(20);
         p.setAntiAlias(true);
         canvas->drawText("Hello World!", 13, 50, 30, p);
-        SkRect r = {50, 50, 80, 80};
+     //   SkRect r = {50, 50, 80, 80};
         p.setColor(0xAA11EEAA);
-        canvas->drawRect(r, p);
+   //     canvas->drawRect(r, p);
+        
+        SkRect result;
+        SkPath path;
+        path.moveTo(0, 0);
+        path.lineTo(1, 1);
+        path.lineTo(1, 8);
+        path.lineTo(0, 9);
+        SkASSERT(path.hasRectangularInterior(&result));
+        
+        path.reset();
+        path.addRect(10, 10, 100, 100, SkPath::kCW_Direction);
+        path.addRect(20, 20, 50, 50, SkPath::kCW_Direction);
+        path.addRect(50, 50, 90, 90, SkPath::kCCW_Direction);
+        p.setColor(0xAA335577);
+        canvas->drawPath(path, p);
+        SkASSERT(!path.hasRectangularInterior(NULL));
+        path.reset();
+        path.addRect(10, 10, 100, 100, SkPath::kCW_Direction);
+        path.addRect(20, 20, 80, 80, SkPath::kCW_Direction);
+        SkRect expected = {20, 20, 80, 80};
+        SkASSERT(path.hasRectangularInterior(&result));
+        SkASSERT(result == expected);
+
     }
 private:
     typedef SkView INHERITED; 
 };
 
+void application_init();
+void application_term();
+
+static int showPathContour(SkPath::Iter& iter) {
+    uint8_t verb;
+    SkPoint pts[4];
+    int moves = 0;
+    bool waitForClose = false;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                if (!waitForClose) {
+                    ++moves;
+                    waitForClose = true;
+                }
+                SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pts[0].fX, pts[0].fY);
+                break;
+            case SkPath::kLine_Verb:
+                SkDebugf("path.lineTo(%1.9g, %1.9g);\n", pts[1].fX, pts[1].fY);
+                break;
+            case SkPath::kQuad_Verb:
+                SkDebugf("path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n",
+                    pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
+                break;
+            case SkPath::kCubic_Verb:
+                SkDebugf("path.cubicTo(%1.9g, %1.9g, %1.9g, %1.9g, %1.9g, %1.9g);\n",
+                    pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+                    pts[3].fX, pts[3].fY);
+                break;
+            case SkPath::kClose_Verb:
+                waitForClose = false;
+                SkDebugf("path.close();\n");
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                SkASSERT(0);
+                return 0;
+        }
+    }
+    return moves;
+}
+
+class PathCanvas : public SkCanvas {
+    virtual void drawPath(const SkPath& path, const SkPaint& paint) {
+        if (nameonly) {
+            SkDebugf("    %s%d,\n", filename.c_str(), ++count);
+            return;
+        }
+        SkPath::Iter iter(path, true);
+        SkDebugf("<div id=\"%s%d\">\n", filename.c_str(), ++count);
+        SkASSERT(path.getFillType() < SkPath::kInverseWinding_FillType);
+        SkDebugf("path.setFillType(SkPath::k%s_FillType);\n",
+            path.getFillType() == SkPath::kWinding_FillType ? "Winding" : "EvenOdd");
+        int contours = showPathContour(iter);
+        SkRect r;
+        SkRect copy = r;
+        bool hasOne = path.hasRectangularInterior(&r);
+        bool expected = (path.getFillType() == SkPath::kWinding_FillType && contours == 1)
+            || (path.getFillType() == SkPath::kEvenOdd_FillType && contours == 2);
+        if (!expected) {
+            SkDebugf("suspect contours=%d\n", contours);
+        }
+        int verbs = path.countVerbs();
+        int points = path.countPoints();
+        if (hasOne) {
+            if (rectVerbsMin > verbs) {
+                rectVerbsMin = verbs;
+            }
+            if (rectVerbsMax < verbs) {
+                rectVerbsMax = verbs;
+            }
+            if (rectPointsMin > points) {
+                rectPointsMin = points;
+            }
+            if (rectPointsMax < points) {
+                rectPointsMax = points;
+            }
+            SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g);\n",
+                    r.fLeft, r.fTop, r.fRight, r.fBottom);
+        } else {
+            if (verbsMin > verbs) {
+                verbsMin = verbs;
+            }
+            if (verbsMax < verbs) {
+                verbsMax = verbs;
+            }
+            if (pointsMin > points) {
+                pointsMin = points;
+            }
+            if (pointsMax < points) {
+                pointsMax = points;
+            }
+            SkDebugf("no interior bounds\n");
+        }
+        path.hasRectangularInterior(&copy);
+        SkDebugf("</div>\n\n");
+    }
+    
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint& paint) {
+    }
+    
+public:
+    void divName(const SkString& str, bool only) {
+        filename = str;
+        char* chars = filename.writable_str();
+        while (*chars) {
+            if (*chars == '.' || *chars == '-') *chars = '_';
+            chars++;
+        }
+        count = 0;
+        nameonly = only;
+    }
+    
+    void init() {
+        pointsMin = verbsMin = SK_MaxS32;
+        pointsMax = verbsMax = SK_MinS32;
+        rectPointsMin = rectVerbsMin = SK_MaxS32;
+        rectPointsMax = rectVerbsMax = SK_MinS32;
+    }
+    
+    SkString filename;
+    int count;
+    bool nameonly;
+    int pointsMin;
+    int pointsMax;
+    int verbsMin;
+    int verbsMax;
+    int rectPointsMin;
+    int rectPointsMax;
+    int rectVerbsMin;
+    int rectVerbsMax;
+};
+
+bool runone = false;
+
 void application_init() {
     SkGraphics::Init();
     SkEvent::Init();
+    if (runone) {
+        return;
+    }
+    const char pictDir[] = "/Volumes/chrome/nih/skia/skp/skp";
+    SkOSFile::Iter iter(pictDir, "skp");
+    SkString filename;
+    PathCanvas canvas;
+    canvas.init();
+    while (iter.next(&filename)) {
+        SkString path;
+     //   if (true) filename.set("tabl_www_sahadan_com.skp");
+        make_filepath(&path, pictDir, filename);
+        canvas.divName(filename, false);
+        SkPicture* pic = LoadPicture(path.c_str());
+        pic->draw(&canvas);
+        SkDELETE(pic);
+    }
+    SkDebugf("\n</div>\n\n");
+
+    SkDebugf("<script type=\"text/javascript\">\n\n");
+    SkDebugf("var testDivs = [\n");
+
+    iter.reset(pictDir, "skp");
+    while (iter.next(&filename)) {
+        SkString path;
+        make_filepath(&path, pictDir, filename);
+        canvas.divName(filename, true);
+        SkPicture* pic = LoadPicture(path.c_str());
+        pic->draw(&canvas);
+        SkDELETE(pic);
+    }
+    SkDebugf("];\n\n");
+
+    SkDebugf("points min=%d max=%d verbs min=%d max=%d\n", canvas.pointsMin, canvas.pointsMax,
+            canvas.verbsMin, canvas.verbsMax);
+    SkDebugf("rect points min=%d max=%d verbs min=%d max=%d\n", canvas.rectPointsMin, canvas.rectPointsMax,
+            canvas.rectVerbsMin, canvas.rectVerbsMax);
+
+    SkDebugf("\n");
 }
 
 void application_term() {
@@ -46,7 +290,7 @@
 @implementation SimpleNSView
 
 - (id)initWithDefaults {
-    if (self = [super initWithDefaults]) {
+    if ((self = [super initWithDefaults])) {
         fWind = new SkOSWindow(self);
         fWind->setLayout(new FillLayout, false);
         fWind->attachChildToFront(new SkSampleView)->unref();
@@ -59,4 +303,4 @@
     SkCGDrawBitmap(ctx, fWind->getBitmap(), 0, 0);
 }
 
-@end
\ No newline at end of file
+@end
diff --git a/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.h b/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.h
index 7783a67..844d953 100644
--- a/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.h
+++ b/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.h
@@ -15,4 +15,3 @@
 @property (nonatomic, retain) IBOutlet UIWindow *window;
 
 @end
-
diff --git a/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.h b/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.h
index d4f8e4e..e7e7b84 100644
--- a/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.h
+++ b/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.h
@@ -15,4 +15,3 @@
 @property (nonatomic, retain) IBOutlet UIWindow *window;
 
 @end
-
diff --git a/experimental/SkSetPoly3To3.cpp b/experimental/SkSetPoly3To3.cpp
index cf94eb5..750db30 100644
--- a/experimental/SkSetPoly3To3.cpp
+++ b/experimental/SkSetPoly3To3.cpp
@@ -67,4 +67,3 @@
                                     matrix->getSkewY(), matrix->getScaleY()));
     return true;
 }
-
diff --git a/experimental/SkSetPoly3To3_A.cpp b/experimental/SkSetPoly3To3_A.cpp
index c858145..9eb6b33 100644
--- a/experimental/SkSetPoly3To3_A.cpp
+++ b/experimental/SkSetPoly3To3_A.cpp
@@ -97,4 +97,3 @@
                                     matrix->getSkewY(), matrix->getScaleY()));
     return true;
 }
-
diff --git a/experimental/SkSetPoly3To3_D.cpp b/experimental/SkSetPoly3To3_D.cpp
index 283b4e5..2fb99d8 100644
--- a/experimental/SkSetPoly3To3_D.cpp
+++ b/experimental/SkSetPoly3To3_D.cpp
@@ -72,4 +72,3 @@
                                     matrix->getSkewY(), matrix->getScaleY()));
     return true;
 }
-
diff --git a/experimental/StrokePathRenderer/GrStrokePathRenderer.cpp b/experimental/StrokePathRenderer/GrStrokePathRenderer.cpp
new file mode 100644
index 0000000..03c135d
--- /dev/null
+++ b/experimental/StrokePathRenderer/GrStrokePathRenderer.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrStrokePathRenderer.h"
+
+#include "GrDrawTarget.h"
+#include "SkPath.h"
+#include "SkStrokeRec.h"
+
+namespace {
+
+bool is_clockwise(const SkVector& before, const SkVector& after) {
+    return before.cross(after) > 0;
+}
+
+enum IntersectionType {
+    kNone_IntersectionType,
+    kIn_IntersectionType,
+    kOut_IntersectionType
+};
+
+IntersectionType intersection(const SkPoint& p1, const SkPoint& p2,
+                              const SkPoint& p3, const SkPoint& p4,
+                                    SkPoint& res) {
+    // Store the values for fast access and easy
+    // equations-to-code conversion
+    SkScalar x1 = p1.x(), x2 = p2.x(), x3 = p3.x(), x4 = p4.x();
+    SkScalar y1 = p1.y(), y2 = p2.y(), y3 = p3.y(), y4 = p4.y();
+
+    SkScalar d = SkScalarMul(x1 - x2, y3 - y4) - SkScalarMul(y1 - y2, x3 - x4);
+    // If d is zero, there is no intersection
+    if (SkScalarNearlyZero(d)) {
+        return kNone_IntersectionType;
+    }
+
+    // Get the x and y
+    SkScalar pre  = SkScalarMul(x1, y2) - SkScalarMul(y1, x2),
+             post = SkScalarMul(x3, y4) - SkScalarMul(y3, x4);
+    // Compute the point of intersection
+    res.set(SkScalarDiv(SkScalarMul(pre, x3 - x4) - SkScalarMul(x1 - x2, post), d),
+            SkScalarDiv(SkScalarMul(pre, y3 - y4) - SkScalarMul(y1 - y2, post), d));
+
+    // Check if the x and y coordinates are within both lines
+    return (res.x() < GrMin(x1, x2) || res.x() > GrMax(x1, x2) ||
+            res.x() < GrMin(x3, x4) || res.x() > GrMax(x3, x4) ||
+            res.y() < GrMin(y1, y2) || res.y() > GrMax(y1, y2) ||
+            res.y() < GrMin(y3, y4) || res.y() > GrMax(y3, y4)) ?
+            kOut_IntersectionType : kIn_IntersectionType;
+}
+
+} // namespace
+
+GrStrokePathRenderer::GrStrokePathRenderer() {
+}
+
+bool GrStrokePathRenderer::canDrawPath(const SkPath& path,
+                                       const SkStrokeRec& stroke,
+                                       const GrDrawTarget* target,
+                                       bool antiAlias) const {
+    // FIXME : put the proper condition once GrDrawTarget::isOpaque is implemented
+    const bool isOpaque = true; // target->isOpaque();
+
+    // FIXME : remove this requirement once we have AA circles and implement the
+    //         circle joins/caps appropriately in the ::onDrawPath() function.
+    const bool requiresAACircle = (stroke.getCap()  == SkPaint::kRound_Cap) ||
+                                  (stroke.getJoin() == SkPaint::kRound_Join);
+
+    // Indices being stored in uint16, we don't want to overflow the indices capacity
+    static const int maxVBSize = 1 << 16;
+    const int maxNbVerts = (path.countPoints() + 1) * 5;
+
+    // Check that the path contains no curved lines, only straight lines
+    static const uint32_t unsupportedMask = SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask;
+
+    // Must not be filled nor hairline nor semi-transparent
+    // Note : May require a check to path.isConvex() if AA is supported
+    return ((stroke.getStyle() == SkStrokeRec::kStroke_Style) && (maxNbVerts < maxVBSize) &&
+            !path.isInverseFillType() && isOpaque && !requiresAACircle && !antiAlias &&
+            ((path.getSegmentMasks() & unsupportedMask) == 0));
+}
+
+bool GrStrokePathRenderer::onDrawPath(const SkPath& origPath,
+                                      const SkStrokeRec& stroke,
+                                      GrDrawTarget* target,
+                                      bool antiAlias) {
+    if (origPath.isEmpty()) {
+        return true;
+    }
+
+    SkScalar width = stroke.getWidth();
+    if (width <= 0) {
+        return false;
+    }
+
+    // Get the join type
+    SkPaint::Join join = stroke.getJoin();
+    SkScalar miterLimit = stroke.getMiter();
+    SkScalar sqMiterLimit = SkScalarMul(miterLimit, miterLimit);
+    if ((join == SkPaint::kMiter_Join) && (miterLimit <= SK_Scalar1)) {
+        // If the miter limit is small, treat it as a bevel join
+        join = SkPaint::kBevel_Join;
+    }
+    const bool isMiter       = (join == SkPaint::kMiter_Join);
+    const bool isBevel       = (join == SkPaint::kBevel_Join);
+    SkScalar invMiterLimit   = isMiter ? SK_Scalar1 / miterLimit : 0;
+    SkScalar invMiterLimitSq = SkScalarMul(invMiterLimit, invMiterLimit);
+
+    // Allocate vertices
+    const int nbQuads     = origPath.countPoints() + 1; // Could be "-1" if path is not closed
+    GrVertexLayout layout = 0; // Just 3D points
+    const int extraVerts  = isMiter || isBevel ? 1 : 0;
+    const int maxVertexCount = nbQuads * (4 + extraVerts);
+    const int maxIndexCount  = nbQuads * (6 + extraVerts * 3); // Each extra vert adds a triangle
+    GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxVertexCount, maxIndexCount);
+    if (!arg.succeeded()) {
+        return false;
+    }
+    SkPoint* verts = reinterpret_cast<SkPoint*>(arg.vertices());
+    uint16_t* idxs = reinterpret_cast<uint16_t*>(arg.indices());
+    int vCount = 0, iCount = 0;
+
+    // Transform the path into a list of triangles
+    SkPath::Iter iter(origPath, false);
+    SkPoint pts[4];
+    const SkScalar radius = SkScalarMul(width, 0.5);
+    SkPoint *firstPt = verts, *lastPt = NULL;
+    SkVector firstDir, dir;
+    firstDir.set(0, 0);
+    dir.set(0, 0);
+    bool isOpen = true;
+    for(SkPath::Verb v = iter.next(pts); v != SkPath::kDone_Verb; v = iter.next(pts)) {
+        switch(v) {
+            case SkPath::kMove_Verb:
+                // This will already be handled as pts[0] of the 1st line
+                break;
+            case SkPath::kClose_Verb:
+                isOpen = (lastPt == NULL);
+                break;
+            case SkPath::kLine_Verb:
+            {
+                SkVector v0 = dir;
+                dir = pts[1] - pts[0];
+                if (dir.setLength(radius)) {
+                    SkVector dirT;
+                    dirT.set(dir.fY, -dir.fX); // Get perpendicular direction
+                    SkPoint l1a = pts[0]+dirT, l1b = pts[1]+dirT,
+                            l2a = pts[0]-dirT, l2b = pts[1]-dirT;
+                    SkPoint miterPt[2];
+                    bool useMiterPoint = false;
+                    int idx0(-1), idx1(-1);
+                    if (NULL == lastPt) {
+                        firstDir = dir;
+                    } else {
+                        SkVector v1 = dir;
+                        if (v0.normalize() && v1.normalize()) {
+                            SkScalar dotProd = v0.dot(v1);
+                            // No need for bevel or miter join if the angle
+                            // is either 0 or 180 degrees
+                            if (!SkScalarNearlyZero(dotProd + SK_Scalar1) &&
+                                !SkScalarNearlyZero(dotProd - SK_Scalar1)) {
+                                bool ccw = !is_clockwise(v0, v1);
+                                int offset = ccw ? 1 : 0;
+                                idx0 = vCount-2+offset;
+                                idx1 = vCount+offset;
+                                const SkPoint* pt0 = &(lastPt[offset]);
+                                const SkPoint* pt1 = ccw ? &l2a : &l1a;
+                                switch(join) {
+                                    case SkPaint::kMiter_Join:
+                                    {
+                                        // *Note : Logic is from MiterJoiner
+
+                                        // FIXME : Special case if we have a right angle ?
+                                        // if (SkScalarNearlyZero(dotProd)) {...}
+
+                                        SkScalar sinHalfAngleSq =
+                                                SkScalarHalf(SK_Scalar1 + dotProd);
+                                        if (sinHalfAngleSq >= invMiterLimitSq) {
+                                            // Find the miter point (or points if it is further
+                                            // than the miter limit)
+                                            const SkPoint pt2 = *pt0+v0, pt3 = *pt1+v1;
+                                            if (intersection(*pt0, pt2, *pt1, pt3, miterPt[0]) !=
+                                                kNone_IntersectionType) {
+                                                SkPoint miterPt0 = miterPt[0] - *pt0;
+                                                SkPoint miterPt1 = miterPt[0] - *pt1;
+                                                SkScalar sqDist0 = miterPt0.dot(miterPt0);
+                                                SkScalar sqDist1 = miterPt1.dot(miterPt1);
+                                                const SkScalar rSq =
+                                                        SkScalarDiv(SkScalarMul(radius, radius),
+                                                                    sinHalfAngleSq);
+                                                const SkScalar sqRLimit =
+                                                        SkScalarMul(sqMiterLimit, rSq);
+                                                if (sqDist0 > sqRLimit || sqDist1 > sqRLimit) {
+                                                    if (sqDist1 > sqRLimit) {
+                                                        v1.setLength(SkScalarSqrt(sqRLimit));
+                                                        miterPt[1] = *pt1+v1;
+                                                    } else {
+                                                        miterPt[1] = miterPt[0];
+                                                    }
+                                                    if (sqDist0 > sqRLimit) {
+                                                        v0.setLength(SkScalarSqrt(sqRLimit));
+                                                        miterPt[0] = *pt0+v0;
+                                                    }
+                                                } else {
+                                                    miterPt[1] = miterPt[0];
+                                                }
+                                                useMiterPoint = true;
+                                            }
+                                        }
+                                        if (useMiterPoint && (miterPt[1] == miterPt[0])) {
+                                            break;
+                                        }
+                                    }
+                                    default:
+                                    case SkPaint::kBevel_Join:
+                                    {
+                                        // Note : This currently causes some overdraw where both
+                                        //        lines initially intersect. We'd need to add
+                                        //        another line intersection check here if the
+                                        //        overdraw becomes an issue instead of using the
+                                        //        current point directly.
+
+                                        // Add center point
+                                        *verts++ = pts[0]; // Use current point directly
+                                        // This idx is passed the current point so increment it
+                                        ++idx1;
+                                        // Add center triangle
+                                        *idxs++ = idx0;
+                                        *idxs++ = vCount;
+                                        *idxs++ = idx1;
+                                        vCount++;
+                                        iCount += 3;
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    *verts++ = l1a;
+                    *verts++ = l2a;
+                    lastPt   = verts;
+                    *verts++ = l1b;
+                    *verts++ = l2b;
+
+                    if (useMiterPoint && (idx0 >= 0) && (idx1 >= 0)) {
+                        firstPt[idx0] = miterPt[0];
+                        firstPt[idx1] = miterPt[1];
+                    }
+
+                    // 1st triangle
+                    *idxs++  = vCount+0;
+                    *idxs++  = vCount+2;
+                    *idxs++  = vCount+1;
+                    // 2nd triangle
+                    *idxs++  = vCount+1;
+                    *idxs++  = vCount+2;
+                    *idxs++  = vCount+3;
+
+                    vCount += 4;
+                    iCount += 6;
+                }
+            }
+                break;
+            case SkPath::kQuad_Verb:
+            case SkPath::kCubic_Verb:
+                GrAssert(!"Curves not supported!");
+            default:
+                // Unhandled cases
+                GrAssert(false);
+        }
+    }
+
+    if (isOpen) {
+        // Add caps
+        switch (stroke.getCap()) {
+            case SkPaint::kSquare_Cap:
+                firstPt[0] -= firstDir;
+                firstPt[1] -= firstDir;
+                lastPt [0] += dir;
+                lastPt [1] += dir;
+                break;
+            case SkPaint::kRound_Cap:
+                GrAssert(!"Round caps not supported!");
+            default: // No cap
+                break;
+        }
+    }
+
+    GrAssert(vCount <= maxVertexCount);
+    GrAssert(iCount <= maxIndexCount);
+
+    if (vCount > 0) {
+        target->drawIndexed(kTriangles_GrPrimitiveType,
+                            0,        // start vertex
+                            0,        // start index
+                            vCount,
+                            iCount);
+    }
+
+    return true;
+}
diff --git a/experimental/StrokePathRenderer/GrStrokePathRenderer.h b/experimental/StrokePathRenderer/GrStrokePathRenderer.h
new file mode 100644
index 0000000..9cf34d8
--- /dev/null
+++ b/experimental/StrokePathRenderer/GrStrokePathRenderer.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrPathRenderer.h"
+
+// This path renderer is made to create geometry (i.e. primitives) from the original path (before
+// the path is stroked) and render using the GPU directly rather than using any software rendering
+// step. It can be rendered in a single pass for simple cases and use multiple passes for features
+// like AA or opacity support.
+
+class GrStrokePathRenderer : public GrPathRenderer {
+
+public:
+    GrStrokePathRenderer();
+
+    virtual bool canDrawPath(const SkPath& path,
+                             const SkStrokeRec& stroke,
+                             const GrDrawTarget* target,
+                             bool antiAlias) const SK_OVERRIDE;
+
+protected:
+    virtual bool onDrawPath(const SkPath& path,
+                            const SkStrokeRec& stroke,
+                            GrDrawTarget* target,
+                            bool antiAlias) SK_OVERRIDE;
+};
diff --git a/experimental/iOSSampleApp/Shared/SkUIView.h b/experimental/iOSSampleApp/Shared/SkUIView.h
index a0640d4..a3d05e3 100644
--- a/experimental/iOSSampleApp/Shared/SkUIView.h
+++ b/experimental/iOSSampleApp/Shared/SkUIView.h
@@ -44,4 +44,3 @@
 - (void)postInvalWithRect:(const SkIRect*)rectOrNil;
 - (BOOL)onHandleEvent:(const SkEvent&)event;
 @end
-
diff --git a/experimental/iOSSampleApp/SkSampleUIView.h b/experimental/iOSSampleApp/SkSampleUIView.h
index 6797706..9bb1956 100644
--- a/experimental/iOSSampleApp/SkSampleUIView.h
+++ b/experimental/iOSSampleApp/SkSampleUIView.h
@@ -44,4 +44,3 @@
 - (void)setSkTitle:(const char*)title;
 - (void)postInvalWithRect:(const SkIRect*)rectOrNil;
 @end
-
diff --git a/experimental/iOSSampleApp/iPad/AppDelegate_iPad.h b/experimental/iOSSampleApp/iPad/AppDelegate_iPad.h
index fbc3eac..eb54c69 100644
--- a/experimental/iOSSampleApp/iPad/AppDelegate_iPad.h
+++ b/experimental/iOSSampleApp/iPad/AppDelegate_iPad.h
@@ -17,4 +17,3 @@
 @property (nonatomic, retain) IBOutlet SkUISplitViewController* splitViewController;
 
 @end
-
diff --git a/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h b/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h
index b5829b2..e19b0bc 100644
--- a/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h
+++ b/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h
@@ -17,4 +17,3 @@
 @property (nonatomic, retain) IBOutlet SkUINavigationController* fRoot;
 
 @end
-
diff --git a/forth/Forth.cpp b/forth/Forth.cpp
index be240e3..1dd4c73 100644
--- a/forth/Forth.cpp
+++ b/forth/Forth.cpp
@@ -499,4 +499,3 @@
     }
 }
 #endif
-
diff --git a/forth/Forth.h b/forth/Forth.h
index 5e8ca8d..f6df06e 100644
--- a/forth/Forth.h
+++ b/forth/Forth.h
@@ -103,4 +103,3 @@
 };
 
 #endif
-
diff --git a/forth/ForthTests.cpp b/forth/ForthTests.cpp
index 08ab7f3..8dc06c0 100644
--- a/forth/ForthTests.cpp
+++ b/forth/ForthTests.cpp
@@ -405,4 +405,3 @@
         SkDebugf("--- %d failures\n", reporter.fFailureCount);
     }
 }
-
diff --git a/forth/StdWords.cpp b/forth/StdWords.cpp
index 8a781f8..bae00dc 100644
--- a/forth/StdWords.cpp
+++ b/forth/StdWords.cpp
@@ -459,4 +459,3 @@
     this->add("f>", 2, new fgt_ForthWord);
     this->add("f>=", 3, new fge_ForthWord);
 }
-
diff --git a/gm/aaclip.cpp b/gm/aaclip.cpp
index b74f6d3..88e1cd4 100644
--- a/gm/aaclip.cpp
+++ b/gm/aaclip.cpp
@@ -9,6 +9,34 @@
 #include "SkCanvas.h"
 #include "SkPath.h"
 
+#include "SkDashPathEffect.h"
+static void test_giant_dash(SkCanvas* canvas) {
+    SkPaint paint;
+    const SkScalar intervals[] = { SK_Scalar1, SK_Scalar1 };
+
+    paint.setStrokeWidth(2);
+    paint.setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
+
+    SkScalar big = 500 * 1000;
+
+    canvas->drawLine(10, 10, big, 10, paint);
+    canvas->drawLine(-big, 20, 500, 20, paint);
+    canvas->drawLine(-big, 30, big, 30, paint);
+
+    const SkScalar intervals2[] = { 20, 5, 10, 5 };
+    paint.setPathEffect(new SkDashPathEffect(intervals2, 4, 17))->unref();
+
+    canvas->translate(0, 40);
+    SkScalar x = -500;
+    SkScalar width = 3173;
+    for (int i = 0; i < 40; ++i) {
+        if (i > 10)
+        canvas->drawLine(x, 0, x + width, 0, paint);
+        x += 1;
+        canvas->translate(0, 4);
+    }
+}
+
 // Reproduces bug found here: http://jsfiddle.net/R8Cu5/1/
 //
 #include "SkGradientShader.h"
@@ -36,11 +64,14 @@
     return canvas;
 }
 
+#ifdef SK_DEBUG
 static void GetBitmap(const SkCanvas* canvas, SkBitmap* bm) {
     *bm = canvas->getDevice()->accessBitmap(false);
 }
+#endif
 
 static void compare_canvas(const SkCanvas* a, const SkCanvas* b) {
+#ifdef SK_DEBUG
     SkBitmap bma, bmb;
     GetBitmap(a, &bma);
     GetBitmap(b, &bmb);
@@ -60,6 +91,7 @@
             SkASSERT(0xFF000000 == rowb[x]);
         }
     }
+#endif
 }
 
 static void drawRectAsPath(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
@@ -173,7 +205,10 @@
         return make_isize(640, 480);
     }
 
-    virtual void onDraw(SkCanvas* canvas) {
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        if (false) {
+            test_giant_dash(canvas); return;
+        }
         if (false) {
             test_grad(canvas); return;
         }
diff --git a/gm/aarectmodes.cpp b/gm/aarectmodes.cpp
index 5634324..704ddcb 100644
--- a/gm/aarectmodes.cpp
+++ b/gm/aarectmodes.cpp
@@ -187,4 +187,3 @@
     static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/bicubicfilter.cpp b/gm/bicubicfilter.cpp
new file mode 100644
index 0000000..c3f80df
--- /dev/null
+++ b/gm/bicubicfilter.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkColor.h"
+#include "SkBicubicImageFilter.h"
+
+namespace skiagm {
+
+class BicubicGM : public GM {
+public:
+    BicubicGM() : fInitialized(false) {
+        this->setBGColor(0x00000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bicubicfilter");
+    }
+
+    void make_checkerboard(int width, int height) {
+        SkASSERT(width % 2 == 0);
+        SkASSERT(height % 2 == 0);
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+        fCheckerboard.allocPixels();
+        SkAutoLockPixels lock(fCheckerboard);
+        for (int y = 0; y < height; y += 2) {
+            SkPMColor* s = fCheckerboard.getAddr32(0, y);
+            for (int x = 0; x < width; x += 2) {
+                *s++ = 0xFFFFFFFF;
+                *s++ = 0xFF000000;
+            }
+            s = fCheckerboard.getAddr32(0, y + 1);
+            for (int x = 0; x < width; x += 2) {
+                *s++ = 0xFF000000;
+                *s++ = 0xFFFFFFFF;
+            }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(400, 300);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_checkerboard(4, 4);
+            fInitialized = true;
+        }
+        SkScalar sk32 = SkIntToScalar(32);
+        canvas->clear(0x00000000);
+        SkPaint bilinearPaint, bicubicPaint;
+        SkSize scale = SkSize::Make(sk32, sk32);
+        canvas->save();
+        canvas->scale(sk32, sk32);
+        bilinearPaint.setFilterBitmap(true);
+        canvas->drawBitmap(fCheckerboard, 0, 0, &bilinearPaint);
+        canvas->restore();
+        SkAutoTUnref<SkImageFilter> bicubic(SkBicubicImageFilter::CreateMitchell(scale));
+        bicubicPaint.setImageFilter(bicubic);
+        SkRect srcBounds;
+        fCheckerboard.getBounds(&srcBounds);
+        canvas->translate(SkIntToScalar(140), 0);
+        canvas->saveLayer(&srcBounds, &bicubicPaint);
+        canvas->drawBitmap(fCheckerboard, 0, 0);
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new BicubicGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/bigmatrix.cpp b/gm/bigmatrix.cpp
index 31c0a38..d414486 100644
--- a/gm/bigmatrix.cpp
+++ b/gm/bigmatrix.cpp
@@ -41,6 +41,7 @@
 
         bool success = m.invert(&m);
         SkASSERT(success);
+        (void) success; // silence compiler :(
 
         SkPath path;
 
@@ -95,4 +96,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp
index 6419296..d74dbf5 100644
--- a/gm/bitmapfilters.cpp
+++ b/gm/bitmapfilters.cpp
@@ -131,6 +131,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
-
-
diff --git a/gm/bitmapscroll.cpp b/gm/bitmapscroll.cpp
index eee9668..a1c0ce4 100644
--- a/gm/bitmapscroll.cpp
+++ b/gm/bitmapscroll.cpp
@@ -131,10 +131,10 @@
                 // Scroll a new copy of the bitmap, and then draw it.
                 // scrollRect() should always return true, even if it's a no-op
                 SkBitmap scrolledBitmap;
-                bool copyToReturnValue = origBitmap.copyTo(
+                SkDEBUGCODE(bool copyToReturnValue = )origBitmap.copyTo(
                     &scrolledBitmap, origBitmap.config());
                 SkASSERT(copyToReturnValue);
-                bool scrollRectReturnValue = scrolledBitmap.scrollRect(
+                SkDEBUGCODE(bool scrollRectReturnValue = )scrolledBitmap.scrollRect(
                     subset, scrollX * xMult, scrollY * yMult);
                 SkASSERT(scrollRectReturnValue);
                 canvas->drawBitmap(scrolledBitmap, bitmapX, bitmapY);
diff --git a/gm/blend.cpp b/gm/blend.cpp
index 3ba92a7..452eb49 100644
--- a/gm/blend.cpp
+++ b/gm/blend.cpp
@@ -63,6 +63,14 @@
         return make_isize(500, 100);
     }
 
+    void drawClippedBitmap(SkCanvas* canvas, const SkPaint& paint, int x) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), 0,
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), 0, &paint);
+        canvas->restore();
+    }
+
     virtual void onDraw(SkCanvas* canvas) {
         if (!fInitialized) {
             make_bitmap();
@@ -73,15 +81,15 @@
         SkPaint paint;
         SkAutoTUnref<SkImageFilter> background(SkNEW_ARGS(SkBitmapSource, (fCheckerboard)));
         paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kNormal_Mode, background)))->unref();
-        canvas->drawSprite(fBitmap, 0, 0, &paint);
+        drawClippedBitmap(canvas, paint, 0);
         paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kMultiply_Mode, background)))->unref();
-        canvas->drawSprite(fBitmap, 100, 0, &paint);
+        drawClippedBitmap(canvas, paint, 100);
         paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kScreen_Mode, background)))->unref();
-        canvas->drawSprite(fBitmap, 200, 0, &paint);
+        drawClippedBitmap(canvas, paint, 200);
         paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kDarken_Mode, background)))->unref();
-        canvas->drawSprite(fBitmap, 300, 0, &paint);
+        drawClippedBitmap(canvas, paint, 300);
         paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kLighten_Mode, background)))->unref();
-        canvas->drawSprite(fBitmap, 400, 0, &paint);
+        drawClippedBitmap(canvas, paint, 400);
     }
 
 private:
diff --git a/gm/blurrect.cpp b/gm/blurrect.cpp
index c28468a..a5e8cf0 100644
--- a/gm/blurrect.cpp
+++ b/gm/blurrect.cpp
@@ -7,6 +7,7 @@
 
 #include "gm.h"
 #include "SkBlurMaskFilter.h"
+#include "SkBlurMask.h"
 #include "SkCanvas.h"
 #include "SkPath.h"
 
@@ -18,13 +19,6 @@
     canvas->drawRect(r, p);
 }
 
-static void stroke_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
-    SkPaint paint(p);
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeWidth(STROKE_WIDTH);
-    canvas->drawRect(r, paint);
-}
-
 static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
     SkRect  rect;
     SkPath  path;
@@ -63,14 +57,6 @@
 
 typedef void (*PaintProc)(SkPaint*, SkScalar width);
 
-static void setgrad(SkPaint* paint, SkScalar width) {
-    SkPoint pts[] = { { 0, 0 }, { width, 0 } };
-    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
-    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
-                                                 SkShader::kClamp_TileMode);
-    paint->setShader(s)->unref();
-}
-
 static const char* gBlurStyle2Name[] = {
     "normal",
     "solid",
@@ -149,6 +135,87 @@
     typedef GM INHERITED;
 };
 
+class BlurRectCompareGM : public skiagm::GM {
+    SkString  fName;
+    unsigned int fRectWidth, fRectHeight;
+    SkScalar fRadius;
+public:
+    BlurRectCompareGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, float radius)
+        : fName(name)
+        , fRectWidth(rectWidth)
+        , fRectHeight(rectHeight)
+        , fRadius(radius)
+    {}
+
+  int width() const { return fRectWidth; }
+  int height() const { return fRectHeight; }
+  SkScalar radius() const { return fRadius; }
+
+protected:
+    virtual SkString onShortName() {
+        return fName;
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void makeMask(SkMask *m, const SkRect&) = 0;
+
+    virtual void onDraw(SkCanvas* canvas) {
+      SkRect r;
+      r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
+
+      SkMask mask;
+
+      this->makeMask(&mask, r);
+      SkAutoMaskFreeImage amfi(mask.fImage);
+
+      SkBitmap bm;
+      bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height());
+      bm.setPixels(mask.fImage);
+      canvas->drawBitmap(bm, 50, 50, NULL);
+    }
+
+    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
+
+private:
+    typedef GM INHERITED;
+};
+
+class BlurRectFastGM: public BlurRectCompareGM {
+public:
+    BlurRectFastGM(const char name[], unsigned int rect_width,
+                   unsigned int rect_height, float blur_radius) :
+        BlurRectCompareGM(name, rect_width, rect_height, blur_radius) {}
+protected:
+    virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+        SkBlurMask::BlurRect(m, r, radius(), SkBlurMask::kNormal_Style,
+                             SkBlurMask::kHigh_Quality );
+    }
+};
+
+class BlurRectSlowGM: public BlurRectCompareGM {
+public:
+  BlurRectSlowGM(const char name[], unsigned int rect_width, unsigned int rect_height, float blur_radius) :
+    BlurRectCompareGM( name, rect_width, rect_height, blur_radius ) {}
+protected:
+    virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+        SkMask src;
+        r.roundOut(&src.fBounds);
+        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
+        src.fFormat = SkMask::kA8_Format;
+        src.fRowBytes = src.fBounds.width();
+        src.fImage = SkMask::AllocImage( src.computeTotalImageSize() );
+        SkAutoMaskFreeImage amfi(src.fImage);
+
+        memset(src.fImage, 0xff, src.computeTotalImageSize());
+
+        SkBlurMask::BlurSeparable(m, src, radius()/2, SkBlurMask::kNormal_Style, SkBlurMask::kHigh_Quality);
+    }
+};
+
+
 //////////////////////////////////////////////////////////////////////////////
 
 DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kNormal_BlurStyle);)
@@ -156,5 +223,12 @@
 DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kOuter_BlurStyle);)
 DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kInner_BlurStyle);)
 
-DEF_GM(return new BlurRectGM("blurrect_grad_80", setgrad, 0x80, SkBlurMaskFilter::kNormal_BlurStyle);)
+DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_10", 100, 100, 10);)
+DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_2", 100, 100, 2);)
+DEF_GM(return new BlurRectFastGM("blurrect_fast_10_10_100", 10, 10, 100);)
+DEF_GM(return new BlurRectFastGM("blurrect_fast_10_100_10", 10, 100, 10);)
 
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_10", 100, 100, 10);)
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_2", 100, 100, 2);)
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_10_100", 10, 10, 100);)
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_100_10", 10, 100, 10);)
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index bb688b9..22804c7 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -107,4 +107,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/cmykjpeg.cpp b/gm/cmykjpeg.cpp
index 692bc3e..7ac4259 100644
--- a/gm/cmykjpeg.cpp
+++ b/gm/cmykjpeg.cpp
@@ -67,7 +67,7 @@
 void forceLinking();
 
 void forceLinking() {
-    SkImageDecoder *creator = CreateJPEGImageDecoder();
+    SkDEBUGCODE(SkImageDecoder *creator = ) CreateJPEGImageDecoder();
     SkASSERT(creator);
 }
 
diff --git a/gm/colorfilterimagefilter.cpp b/gm/colorfilterimagefilter.cpp
index 60148b3..6323d33 100644
--- a/gm/colorfilterimagefilter.cpp
+++ b/gm/colorfilterimagefilter.cpp
@@ -130,5 +130,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new ColorFilterImageFilterGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
-
diff --git a/gm/composeshader.cpp b/gm/composeshader.cpp
index f74a19e..a6643a6 100644
--- a/gm/composeshader.cpp
+++ b/gm/composeshader.cpp
@@ -80,4 +80,3 @@
 static GMRegistry reg(MyFactory);
 
 } // namespace
-
diff --git a/gm/convexpaths.cpp b/gm/convexpaths.cpp
index 5af843f..2c4753d 100644
--- a/gm/convexpaths.cpp
+++ b/gm/convexpaths.cpp
@@ -263,4 +263,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/dashcubics.cpp b/gm/dashcubics.cpp
index bbce9be..5d874d4 100644
--- a/gm/dashcubics.cpp
+++ b/gm/dashcubics.cpp
@@ -71,4 +71,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new DashCubicsGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
diff --git a/gm/dashing.cpp b/gm/dashing.cpp
index 70fb9b1..7fe3029 100644
--- a/gm/dashing.cpp
+++ b/gm/dashing.cpp
@@ -245,7 +245,13 @@
         // 1on/1off 1x1 squares with phase of 1 and non-integer length - rects fastpath
         canvas->save();
             canvas->translate(332, 0);
-            this->drawDashedLines(canvas, 99.5, SK_ScalarHalf, SK_Scalar1, 1, false);
+            this->drawDashedLines(canvas, 99.5f, SK_ScalarHalf, SK_Scalar1, 1, false);
+        canvas->restore();
+
+        // 255on/255off 1x1 squares with phase of 0 - rects fast path
+        canvas->save();
+            canvas->translate(446, 0);
+            this->drawDashedLines(canvas, 100, 0, SkIntToScalar(255), 1, false);
         canvas->restore();
 
         // 1on/1off 3x3 squares with phase of 0 - points fast path
@@ -302,4 +308,3 @@
 static skiagm::GMRegistry gR0(F0);
 static skiagm::GMRegistry gR1(F1);
 static skiagm::GMRegistry gR2(F2);
-
diff --git a/gm/displacement.cpp b/gm/displacement.cpp
new file mode 100644
index 0000000..5317a31
--- /dev/null
+++ b/gm/displacement.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkDisplacementMapEffect.h"
+#include "SkBitmapSource.h"
+
+namespace skiagm {
+
+class DisplacementMapGM : public GM {
+public:
+    DisplacementMapGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("displacement");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFF884422);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "g";
+        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(55), paint);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fCheckerboard.allocPixels();
+        SkDevice device(fCheckerboard);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint darkPaint;
+        darkPaint.setColor(0xFF804020);
+        SkPaint lightPaint;
+        lightPaint.setColor(0xFF244484);
+        for (int y = 0; y < 80; y += 16) {
+          for (int x = 0; x < 80; x += 16) {
+            canvas.save();
+            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint);
+            canvas.restore();
+          }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(500, 200);
+    }
+
+    void drawClippedBitmap(SkCanvas* canvas, int x, int y, const SkPaint& paint) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            make_checkerboard();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+        SkAutoTUnref<SkImageFilter> displ(SkNEW_ARGS(SkBitmapSource, (fCheckerboard)));
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kG_ChannelSelectorType, 0.0f, displ)))->unref();
+        drawClippedBitmap(canvas, 0, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kB_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.2f, displ)))->unref();
+        drawClippedBitmap(canvas, 100, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kB_ChannelSelectorType, 0.4f, displ)))->unref();
+        drawClippedBitmap(canvas, 200, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kG_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.6f, displ)))->unref();
+        drawClippedBitmap(canvas, 300, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.8f, displ)))->unref();
+        drawClippedBitmap(canvas, 400, 0, paint);
+
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kG_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 0, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kB_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 100, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kB_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 200, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kG_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 300, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 400, 100, paint);
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DisplacementMapGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/drawbitmaprect.cpp b/gm/drawbitmaprect.cpp
index 24c3431..16cd6e4 100644
--- a/gm/drawbitmaprect.cpp
+++ b/gm/drawbitmaprect.cpp
@@ -189,4 +189,3 @@
 static GMRegistry reg(MyFactory);
 #endif
 }
-
diff --git a/gm/drawlooper.cpp b/gm/drawlooper.cpp
index fa92393..58c691c 100644
--- a/gm/drawlooper.cpp
+++ b/gm/drawlooper.cpp
@@ -99,4 +99,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new DrawLooperGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
diff --git a/gm/fatpathfill.cpp b/gm/fatpathfill.cpp
index 4c30949..4498879 100644
--- a/gm/fatpathfill.cpp
+++ b/gm/fatpathfill.cpp
@@ -97,4 +97,3 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 DEF_GM(return new FatPathFillGM;)
-
diff --git a/gm/filltypes.cpp b/gm/filltypes.cpp
index 86f1fbc..7df02ae 100644
--- a/gm/filltypes.cpp
+++ b/gm/filltypes.cpp
@@ -93,4 +93,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/filltypespersp.cpp b/gm/filltypespersp.cpp
index bdd14a7..cbd966c 100644
--- a/gm/filltypespersp.cpp
+++ b/gm/filltypespersp.cpp
@@ -131,4 +131,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/fontscaler.cpp b/gm/fontscaler.cpp
index b331bde..59b1a43 100644
--- a/gm/fontscaler.cpp
+++ b/gm/fontscaler.cpp
@@ -90,4 +90,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/getpostextpath.cpp b/gm/getpostextpath.cpp
index aa65173..39a3fc1 100644
--- a/gm/getpostextpath.cpp
+++ b/gm/getpostextpath.cpp
@@ -71,4 +71,3 @@
 
 static skiagm::GM* F(void*) { return new GetPosTextPathGM; }
 static skiagm::GMRegistry gR(F);
-
diff --git a/gm/giantbitmap.cpp b/gm/giantbitmap.cpp
index 16c01d7..0d342b8 100644
--- a/gm/giantbitmap.cpp
+++ b/gm/giantbitmap.cpp
@@ -114,7 +114,7 @@
 
         canvas->translate(SkIntToScalar(50), SkIntToScalar(50));
 
-        SkRect r = SkRect::MakeXYWH(-50, -50, 32, 16);
+//        SkRect r = SkRect::MakeXYWH(-50, -50, 32, 16);
 //        canvas->drawRect(r, paint); return;
         canvas->drawPaint(paint);
     }
@@ -152,4 +152,3 @@
 static skiagm::GMRegistry reg011(G011);
 static skiagm::GMRegistry reg111(G111);
 static skiagm::GMRegistry reg211(G211);
-
diff --git a/gm/gm.h b/gm/gm.h
index 9460ec0..6ffb029 100644
--- a/gm/gm.h
+++ b/gm/gm.h
@@ -34,11 +34,12 @@
         virtual ~GM();
 
         enum Flags {
-            kSkipPDF_Flag       = 1 << 0,
-            kSkipPicture_Flag   = 1 << 1,
-            kSkipPipe_Flag      = 1 << 2,
-            kSkipTiled_Flag     = 1 << 3,
-            kSkip565_Flag       = 1 << 4,
+            kSkipPDF_Flag           = 1 << 0,
+            kSkipPicture_Flag       = 1 << 1,
+            kSkipPipe_Flag          = 1 << 2,
+            kSkipTiled_Flag         = 1 << 3,
+            kSkip565_Flag           = 1 << 4,
+            kSkipScaledReplay_Flag  = 1 << 5,
         };
 
         void draw(SkCanvas*);
@@ -52,6 +53,13 @@
             return this->onGetFlags();
         }
 
+        SkScalar width() {
+            return SkIntToScalar(this->getISize().width());
+        }
+        SkScalar height() {
+            return SkIntToScalar(this->getISize().width());
+        }
+
         // TODO(vandebo) Instead of exposing this, we should run all the GMs
         // with and without an initial transform.
         // Most GMs will return the identity matrix, but some PDFs tests
diff --git a/gm/gm_expectations.h b/gm/gm_expectations.h
new file mode 100644
index 0000000..07f0e60
--- /dev/null
+++ b/gm/gm_expectations.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef gm_expectations_DEFINED
+#define gm_expectations_DEFINED
+
+#include "gm.h"
+#include "SkBitmap.h"
+#include "SkBitmapChecksummer.h"
+#include "SkImageDecoder.h"
+#include "SkOSFile.h"
+#include "SkRefCnt.h"
+#include "SkTArray.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    // json includes xlocale which generates warning 4530 because we're compiling without
+    // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067
+    #pragma warning(push)
+    #pragma warning(disable : 4530)
+#endif
+#include "json/value.h"
+#ifdef SK_BUILD_FOR_WIN
+    #pragma warning(pop)
+#endif
+
+namespace skiagm {
+
+    // The actual type we use to represent a checksum is hidden in here.
+    typedef Json::UInt64 Checksum;
+    static inline Json::Value asJsonValue(Checksum checksum) {
+        return checksum;
+    }
+
+    static SkString make_filename(const char path[],
+                                  const char renderModeDescriptor[],
+                                  const char *name,
+                                  const char suffix[]) {
+        SkString filename(path);
+        if (filename.endsWith(SkPATH_SEPARATOR)) {
+            filename.remove(filename.size() - 1, 1);
+        }
+        filename.appendf("%c%s%s.%s", SkPATH_SEPARATOR,
+                         name, renderModeDescriptor, suffix);
+        return filename;
+    }
+
+    /**
+     * Test expectations (allowed image checksums, etc.)
+     */
+    class Expectations {
+    public:
+        /**
+         * No expectations at all.
+         *
+         * We set ignoreFailure to false by default, but it doesn't really
+         * matter... the result will always be "no-comparison" anyway.
+         */
+        Expectations(bool ignoreFailure=false) {
+            fIgnoreFailure = ignoreFailure;
+        }
+
+        /**
+         * Expect exactly one image (appropriate for the case when we
+         * are comparing against a single PNG file).
+         *
+         * By default, DO NOT ignore failures.
+         */
+        Expectations(const SkBitmap& bitmap, bool ignoreFailure=false) {
+            fBitmap = bitmap;
+            fIgnoreFailure = ignoreFailure;
+            fAllowedChecksums.push_back() = SkBitmapChecksummer::Compute64(bitmap);
+        }
+
+        /**
+         * Returns true iff we want to ignore failed expectations.
+         */
+        bool ignoreFailure() const { return this->fIgnoreFailure; }
+
+        /**
+         * Returns true iff there are no allowed checksums.
+         */
+        bool empty() const { return this->fAllowedChecksums.empty(); }
+
+        /**
+         * Returns true iff actualChecksum matches any allowedChecksum,
+         * regardless of fIgnoreFailure.  (The caller can check
+         * that separately.)
+         */
+        bool match(Checksum actualChecksum) const {
+            for (int i=0; i < this->fAllowedChecksums.count(); i++) {
+                Checksum allowedChecksum = this->fAllowedChecksums[i];
+                if (allowedChecksum == actualChecksum) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * If this Expectation is based on a single SkBitmap, return a
+         * pointer to that SkBitmap. Otherwise (if the Expectation is
+         * empty, or if it was based on a list of checksums rather
+         * than a single bitmap), returns NULL.
+         */
+        const SkBitmap *asBitmap() const {
+            return (SkBitmap::kNo_Config == fBitmap.config()) ? NULL : &fBitmap;
+        }
+
+        /**
+         * Return a JSON representation of the allowed checksums.
+         * This does NOT include any information about whether to
+         * ignore failures.
+         */
+        Json::Value allowedChecksumsAsJson() const {
+            Json::Value allowedChecksumArray;
+            if (!this->fAllowedChecksums.empty()) {
+                for (int i=0; i < this->fAllowedChecksums.count(); i++) {
+                    Checksum allowedChecksum = this->fAllowedChecksums[i];
+                    allowedChecksumArray.append(asJsonValue(allowedChecksum));
+                }
+            }
+            return allowedChecksumArray;
+        }
+
+    private:
+        SkTArray<Checksum> fAllowedChecksums;
+        bool fIgnoreFailure;
+        SkBitmap fBitmap;
+    };
+
+    /**
+     * Abstract source of Expectations objects for individual tests.
+     */
+    class ExpectationsSource : public SkRefCnt {
+    public:
+        virtual Expectations get(const char *testName) = 0;
+    };
+
+    /**
+     * Return Expectations based on individual image files on disk.
+     */
+    class IndividualImageExpectationsSource : public ExpectationsSource {
+    public:
+        /**
+         * Create an ExpectationsSource that will return Expectations based on
+         * image files found within rootDir.
+         *
+         * rootDir: directory under which to look for image files
+         *          (this string will be copied to storage within this object)
+         * notifyOfMissingFiles: whether to log a message to stderr if an image
+         *                       file cannot be found
+         */
+        IndividualImageExpectationsSource(const char *rootDir,
+                                          bool notifyOfMissingFiles) :
+            fRootDir(rootDir), fNotifyOfMissingFiles(notifyOfMissingFiles) {}
+
+        Expectations get(const char *testName) SK_OVERRIDE {
+            SkString path = make_filename(fRootDir.c_str(), "", testName,
+                                          "png");
+            SkBitmap referenceBitmap;
+            bool decodedReferenceBitmap =
+                SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
+                                           SkBitmap::kARGB_8888_Config,
+                                           SkImageDecoder::kDecodePixels_Mode,
+                                           NULL);
+            if (decodedReferenceBitmap) {
+                return Expectations(referenceBitmap);
+            } else {
+                if (fNotifyOfMissingFiles) {
+                    fprintf(stderr, "FAILED to read %s\n", path.c_str());
+                }
+                return Expectations();
+            }
+        }
+
+    private:
+        const SkString fRootDir;
+        const bool fNotifyOfMissingFiles;
+    };
+
+}
+#endif
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index f66fffe..bbeb443 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -14,7 +14,9 @@
  */
 
 #include "gm.h"
+#include "gm_expectations.h"
 #include "system_preferences.h"
+#include "SkBitmap.h"
 #include "SkBitmapChecksummer.h"
 #include "SkColorPriv.h"
 #include "SkData.h"
@@ -33,7 +35,16 @@
 #include "SkTileGridPicture.h"
 #include "SamplePipeControllers.h"
 
+#ifdef SK_BUILD_FOR_WIN
+    // json includes xlocale which generates warning 4530 because we're compiling without
+    // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067
+    #pragma warning(push)
+    #pragma warning(disable : 4530)
+#endif
 #include "json/value.h"
+#ifdef SK_BUILD_FOR_WIN
+    #pragma warning(pop)
+#endif
 
 #if SK_SUPPORT_GPU
 #include "GrContextFactory.h"
@@ -69,17 +80,24 @@
     #define CAN_IMAGE_PDF   0
 #endif
 
+// TODO(epoger): We created this ErrorBitfield so that we could record
+// multiple error types for the same comparison. But in practice, we
+// process its final value in switch() statements, which inherently
+// assume that only one error type will be set.
+// I think we should probably change this to be an enum, and thus
+// constrain ourselves to a single error type per comparison.
 typedef int ErrorBitfield;
 const static ErrorBitfield ERROR_NONE                    = 0x00;
 const static ErrorBitfield ERROR_NO_GPU_CONTEXT          = 0x01;
-const static ErrorBitfield ERROR_PIXEL_MISMATCH          = 0x02;
-const static ErrorBitfield ERROR_DIMENSION_MISMATCH      = 0x04;
+const static ErrorBitfield ERROR_IMAGE_MISMATCH          = 0x02;
+// const static ErrorBitfield ERROR_DIMENSION_MISMATCH      = 0x04; DEPRECATED in https://codereview.appspot.com/7064047
 const static ErrorBitfield ERROR_READING_REFERENCE_IMAGE = 0x08;
 const static ErrorBitfield ERROR_WRITING_REFERENCE_IMAGE = 0x10;
 
 const static char kJsonKey_ActualResults[]   = "actual-results";
 const static char kJsonKey_ActualResults_Failed[]        = "failed";
 const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
+const static char kJsonKey_ActualResults_NoComparison[]  = "no-comparison";
 const static char kJsonKey_ActualResults_Succeeded[]     = "succeeded";
 const static char kJsonKey_ActualResults_AnyStatus_Checksum[]    = "checksum";
 
@@ -89,24 +107,12 @@
 
 using namespace skiagm;
 
-/*
- *  Return the max of the difference (in absolute value) for any component.
- *  Returns 0 if they are equal.
- */
-static int compute_PMColor_maxDiff(SkPMColor c0, SkPMColor c1) {
-    int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
-    int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
-    int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
-    int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
-    return SkMax32(da, SkMax32(dr, SkMax32(dg, db)));
-}
-
 struct FailRec {
     SkString    fName;
-    int         fMaxPixelError;
+    bool        fIsPixelError;
 
-    FailRec() : fMaxPixelError(0) {}
-    FailRec(const SkString& name) : fName(name), fMaxPixelError(0) {}
+    FailRec() : fIsPixelError(false) {}
+    FailRec(const SkString& name) : fName(name), fIsPixelError(false) {}
 };
 
 class Iter {
@@ -201,8 +207,8 @@
     GMMain() {
         // Set default values of member variables, which tool_main()
         // may override.
-        fNotifyMissingReadReference = true;
         fUseFileHierarchy = false;
+        fMismatchPath = NULL;
     }
 
     SkString make_name(const char shortName[], const char configName[]) {
@@ -217,24 +223,26 @@
         return name;
     }
 
-    static SkString make_filename(const char path[],
-                                  const char pathSuffix[],
-                                  const SkString& name,
-                                  const char suffix[]) {
-        SkString filename(path);
-        if (filename.endsWith(SkPATH_SEPARATOR)) {
-            filename.remove(filename.size() - 1, 1);
-        }
-        filename.appendf("%s%c%s.%s", pathSuffix, SkPATH_SEPARATOR,
-                         name.c_str(), suffix);
-        return filename;
-    }
-
     /* since PNG insists on unpremultiplying our alpha, we take no
        precision chances and force all pixels to be 100% opaque,
        otherwise on compare we may not get a perfect match.
     */
     static void force_all_opaque(const SkBitmap& bitmap) {
+        SkBitmap::Config config = bitmap.config();
+        switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            force_all_opaque_8888(bitmap);
+            break;
+        case SkBitmap::kRGB_565_Config:
+            // nothing to do here; 565 bitmaps are inherently opaque
+            break;
+        default:
+            fprintf(stderr, "unsupported bitmap config %d\n", config);
+            SkDEBUGFAIL("unsupported bitmap config");
+        }
+    }
+
+    static void force_all_opaque_8888(const SkBitmap& bitmap) {
         SkAutoLockPixels lock(bitmap);
         for (int y = 0; y < bitmap.height(); y++) {
             for (int x = 0; x < bitmap.width(); x++) {
@@ -244,133 +252,47 @@
     }
 
     static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
+        // TODO(epoger): Now that we have removed force_all_opaque()
+        // from this method, we should be able to get rid of the
+        // transformation to 8888 format also.
         SkBitmap copy;
         bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
-        force_all_opaque(copy);
         return SkImageEncoder::EncodeFile(path.c_str(), copy,
                                           SkImageEncoder::kPNG_Type, 100);
     }
 
-    static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
-        int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
-        int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
-        int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
-        return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
-    }
-
-    static void compute_diff(const SkBitmap& target, const SkBitmap& base,
-                             SkBitmap* diff) {
-        SkAutoLockPixels alp(*diff);
-
-        const int w = target.width();
-        const int h = target.height();
-        for (int y = 0; y < h; y++) {
-            for (int x = 0; x < w; x++) {
-                SkPMColor c0 = *base.getAddr32(x, y);
-                SkPMColor c1 = *target.getAddr32(x, y);
-                SkPMColor d = 0;
-                if (c0 != c1) {
-                    d = compute_diff_pmcolor(c0, c1);
-                }
-                *diff->getAddr32(x, y) = d;
-            }
-        }
-    }
-
     // Records an error in fFailedTests, if we want to record errors
     // of this type.
     void RecordError(ErrorBitfield errorType, const SkString& name,
-                     const char renderModeDescriptor [], int maxPixelError=0) {
+                     const char renderModeDescriptor []) {
+        bool isPixelError;
         switch (errorType) {
         case ERROR_NONE:
-            break;
+            return;
         case ERROR_READING_REFERENCE_IMAGE:
+            return;
+        case ERROR_IMAGE_MISMATCH:
+            isPixelError = true;
             break;
         default:
-            FailRec& rec = fFailedTests.push_back(make_name(
-                name.c_str(), renderModeDescriptor));
-            rec.fMaxPixelError = maxPixelError;
+            isPixelError = false;
             break;
         }
+
+        FailRec& rec = fFailedTests.push_back(make_name(
+            name.c_str(), renderModeDescriptor));
+        rec.fIsPixelError = isPixelError;
     }
 
     // List contents of fFailedTests via SkDebug.
     void ListErrors() {
         for (int i = 0; i < fFailedTests.count(); ++i) {
-            int pixErr = fFailedTests[i].fMaxPixelError;
-            SkString pixStr;
-            if (pixErr > 0) {
-                pixStr.printf(" pixel_error %d", pixErr);
-            }
-            SkDebugf("\t\t%s%s\n", fFailedTests[i].fName.c_str(),
-                     pixStr.c_str());
-        }
-    }
-
-    // Compares "target" and "base" bitmaps, returning the result
-    // (ERROR_NONE if the two bitmaps are identical).
-    //
-    // If a "diff" bitmap is passed in, pixel diffs (if any) will be written
-    // into it.
-    ErrorBitfield compare(const SkBitmap& target, const SkBitmap& base,
-                          const SkString& name,
-                          const char* renderModeDescriptor,
-                          SkBitmap* diff) {
-        SkBitmap copy;
-        const SkBitmap* bm = &target;
-        if (target.config() != SkBitmap::kARGB_8888_Config) {
-            target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
-            bm = &copy;
-        }
-        SkBitmap baseCopy;
-        const SkBitmap* bp = &base;
-        if (base.config() != SkBitmap::kARGB_8888_Config) {
-            base.copyTo(&baseCopy, SkBitmap::kARGB_8888_Config);
-            bp = &baseCopy;
-        }
-
-        force_all_opaque(*bm);
-        force_all_opaque(*bp);
-
-        const int w = bm->width();
-        const int h = bm->height();
-        if (w != bp->width() || h != bp->height()) {
-            SkDebugf(
-                     "---- %s dimensions mismatch for %s base [%d %d] current [%d %d]\n",
-                     renderModeDescriptor, name.c_str(),
-                     bp->width(), bp->height(), w, h);
-            RecordError(ERROR_DIMENSION_MISMATCH, name, renderModeDescriptor);
-            return ERROR_DIMENSION_MISMATCH;
-        }
-
-        SkAutoLockPixels bmLock(*bm);
-        SkAutoLockPixels baseLock(*bp);
-
-        int maxErr = 0;
-        for (int y = 0; y < h; y++) {
-            for (int x = 0; x < w; x++) {
-                SkPMColor c0 = *bp->getAddr32(x, y);
-                SkPMColor c1 = *bm->getAddr32(x, y);
-                if (c0 != c1) {
-                    maxErr = SkMax32(maxErr, compute_PMColor_maxDiff(c0, c1));
-                }
+            if (fFailedTests[i].fIsPixelError) {
+                SkDebugf("\t\t%s pixel_error\n", fFailedTests[i].fName.c_str());
+            } else {
+                SkDebugf("\t\t%s\n", fFailedTests[i].fName.c_str());
             }
         }
-
-        if (maxErr > 0) {
-            SkDebugf(
-                     "----- %s max pixel mismatch for %s is %d\n",
-                     renderModeDescriptor, name.c_str(), maxErr);
-            if (diff) {
-                diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
-                diff->allocPixels();
-                compute_diff(*bm, *bp, diff);
-            }
-            RecordError(ERROR_PIXEL_MISMATCH, name, renderModeDescriptor,
-                        maxErr);
-            return ERROR_PIXEL_MISMATCH;
-        }
-        return ERROR_NONE;
     }
 
     static bool write_document(const SkString& path,
@@ -380,12 +302,17 @@
         return stream.writeData(data.get());
     }
 
-    /// Returns true if processing should continue, false to skip the
-    /// remainder of this config for this GM.
-    //@todo thudson 22 April 2011 - could refactor this to take in
-    // a factory to generate the context, always call readPixels()
-    // (logically a noop for rasters, if wasted time), and thus collapse the
-    // GPU special case and also let this be used for SkPicture testing.
+    /**
+     * Prepare an SkBitmap to render a GM into.
+     *
+     * After you've rendered the GM into the SkBitmap, you must call
+     * complete_bitmap()!
+     *
+     * @todo thudson 22 April 2011 - could refactor this to take in
+     * a factory to generate the context, always call readPixels()
+     * (logically a noop for rasters, if wasted time), and thus collapse the
+     * GPU special case and also let this be used for SkPicture testing.
+     */
     static void setup_bitmap(const ConfigData& gRec, SkISize& size,
                              SkBitmap* bitmap) {
         bitmap->setConfig(gRec.fConfig, size.width(), size.height());
@@ -393,6 +320,66 @@
         bitmap->eraseColor(SK_ColorTRANSPARENT);
     }
 
+    /**
+     * Any finalization steps we need to perform on the SkBitmap after
+     * we have rendered the GM into it.
+     *
+     * It's too bad that we are throwing away alpha channel data
+     * we could otherwise be examining, but this had always been happening
+     * before... it was buried within the compare() method at
+     * https://code.google.com/p/skia/source/browse/trunk/gm/gmmain.cpp?r=7289#305 .
+     *
+     * Apparently we need this, at least for bitmaps that are either:
+     * (a) destined to be written out as PNG files, or
+     * (b) compared against bitmaps read in from PNG files
+     * for the reasons described just above the force_all_opaque() method.
+     *
+     * Neglecting to do this led to the difficult-to-diagnose
+     * http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating
+     * spurious pixel_error messages as of r7258')
+     *
+     * TODO(epoger): Come up with a better solution that allows us to
+     * compare full pixel data, including alpha channel, while still being
+     * robust in the face of transformations to/from PNG files.
+     * Options include:
+     *
+     * 1. Continue to call force_all_opaque(), but ONLY for bitmaps that
+     *    will be written to, or compared against, PNG files.
+     *    PRO: Preserve/compare alpha channel info for the non-PNG cases
+     *         (comparing different renderModes in-memory)
+     *    CON: The bitmaps (and checksums) for these non-PNG cases would be
+     *         different than those for the PNG-compared cases, and in the
+     *         case of a failed renderMode comparison, how would we write the
+     *         image to disk for examination?
+     *
+     * 2. Always compute image checksums from PNG format (either
+     *    directly from the the bytes of a PNG file, or capturing the
+     *    bytes we would have written to disk if we were writing the
+     *    bitmap out as a PNG).
+     *    PRO: I think this would allow us to never force opaque, and to
+     *         the extent that alpha channel data can be preserved in a PNG
+     *         file, we could observe it.
+     *    CON: If we read a bitmap from disk, we need to take its checksum
+     *         from the source PNG (we can't compute it from the bitmap we
+     *         read out of the PNG, because we will have already premultiplied
+     *         the alpha).
+     *    CON: Seems wasteful to convert a bitmap to PNG format just to take
+     *         its checksum. (Although we're wasting lots of effort already
+     *         calling force_all_opaque().)
+     *
+     * 3. Make the alpha premultiply/unpremultiply routines 100% consistent,
+     *    so we can transform images back and forth without fear of off-by-one
+     *    errors.
+     *    CON: Math is hard.
+     *
+     * 4. Perform a "close enough" comparison of bitmaps (+/- 1 bit in each
+     *    channel), rather than demanding absolute equality.
+     *    CON: Can't do this with checksums.
+     */
+    static void complete_bitmap(SkBitmap* bitmap) {
+        force_all_opaque(*bitmap);
+    }
+
     static void installFilter(SkCanvas* canvas) {
         if (gForceBWtext) {
             canvas->setDrawFilter(new BWTextDrawFilter)->unref();
@@ -451,16 +438,20 @@
             canvas->readPixels(bitmap, 0, 0);
         }
 #endif
+        complete_bitmap(bitmap);
         return ERROR_NONE;
     }
 
     static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
-                                            SkPicture* pict, SkBitmap* bitmap) {
+                                            SkPicture* pict, SkBitmap* bitmap,
+                                            SkScalar scale = SK_Scalar1) {
         SkISize size = gm->getISize();
         setup_bitmap(gRec, size, bitmap);
         SkCanvas canvas(*bitmap);
         installFilter(&canvas);
+        canvas.scale(scale, scale);
         canvas.drawPicture(*pict);
+        complete_bitmap(bitmap);
     }
 
     static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
@@ -527,15 +518,18 @@
             gRec.fBackend == kGPU_Backend ||
             (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) {
 
-            path = make_filename(writePath, renderModeDescriptor, name, "png");
+            path = make_filename(writePath, renderModeDescriptor, name.c_str(),
+                                 "png");
             success = write_bitmap(path, bitmap);
         }
         if (kPDF_Backend == gRec.fBackend) {
-            path = make_filename(writePath, renderModeDescriptor, name, "pdf");
+            path = make_filename(writePath, renderModeDescriptor, name.c_str(),
+                                 "pdf");
             success = write_document(path, *document);
         }
         if (kXPS_Backend == gRec.fBackend) {
-            path = make_filename(writePath, renderModeDescriptor, name, "xps");
+            path = make_filename(writePath, renderModeDescriptor, name.c_str(),
+                                 "xps");
             success = write_document(path, *document);
         }
         if (success) {
@@ -548,140 +542,290 @@
         }
     }
 
-    // Compares bitmap "bitmap" to "referenceBitmap"; if they are
-    // different, writes out "bitmap" (in PNG format) within the
-    // diffPath subdir.
-    //
-    // Returns the ErrorBitfield from compare(), describing any differences
-    // between "bitmap" and "referenceBitmap" (or ERROR_NONE if there are none).
-    ErrorBitfield compare_to_reference_image_in_memory(
-      const SkString& name, SkBitmap &bitmap, const SkBitmap& referenceBitmap,
-      const char diffPath [], const char renderModeDescriptor []) {
-        ErrorBitfield errors;
-        SkBitmap diffBitmap;
-        errors = compare(bitmap, referenceBitmap, name, renderModeDescriptor,
-                         diffPath ? &diffBitmap : NULL);
-        if ((ERROR_NONE != errors) && diffPath) {
-            // write out the generated image
-            SkString genName = make_filename(diffPath, "", name, "png");
-            if (!write_bitmap(genName, bitmap)) {
-                RecordError(ERROR_WRITING_REFERENCE_IMAGE, name,
-                            renderModeDescriptor);
-                errors |= ERROR_WRITING_REFERENCE_IMAGE;
+    /**
+     * Log more detail about the mistmatch between expectedBitmap and
+     * actualBitmap.
+     */
+    void report_bitmap_diffs(const SkBitmap& expectedBitmap, const SkBitmap& actualBitmap,
+                             const char *testName) {
+        const int expectedWidth = expectedBitmap.width();
+        const int expectedHeight = expectedBitmap.height();
+        const int width = actualBitmap.width();
+        const int height = actualBitmap.height();
+        if ((expectedWidth != width) || (expectedHeight != height)) {
+            SkDebugf("---- %s: dimension mismatch -- expected [%d %d], actual [%d %d]\n",
+                     testName, expectedWidth, expectedHeight, width, height);
+            return;
+        }
+
+        if ((SkBitmap::kARGB_8888_Config != expectedBitmap.config()) ||
+            (SkBitmap::kARGB_8888_Config != actualBitmap.config())) {
+            SkDebugf("---- %s: not computing max per-channel pixel mismatch because non-8888\n",
+                     testName);
+            return;
+        }
+
+        SkAutoLockPixels alp0(expectedBitmap);
+        SkAutoLockPixels alp1(actualBitmap);
+        int errR = 0;
+        int errG = 0;
+        int errB = 0;
+        int errA = 0;
+        int differingPixels = 0;
+
+        for (int y = 0; y < height; ++y) {
+            const SkPMColor* expectedPixelPtr = expectedBitmap.getAddr32(0, y);
+            const SkPMColor* actualPixelPtr = actualBitmap.getAddr32(0, y);
+            for (int x = 0; x < width; ++x) {
+                SkPMColor expectedPixel = *expectedPixelPtr++;
+                SkPMColor actualPixel = *actualPixelPtr++;
+                if (expectedPixel != actualPixel) {
+                    differingPixels++;
+                    errR = SkMax32(errR, SkAbs32((int)SkGetPackedR32(expectedPixel) -
+                                                 (int)SkGetPackedR32(actualPixel)));
+                    errG = SkMax32(errG, SkAbs32((int)SkGetPackedG32(expectedPixel) -
+                                                 (int)SkGetPackedG32(actualPixel)));
+                    errB = SkMax32(errB, SkAbs32((int)SkGetPackedB32(expectedPixel) -
+                                                 (int)SkGetPackedB32(actualPixel)));
+                    errA = SkMax32(errA, SkAbs32((int)SkGetPackedA32(expectedPixel) -
+                                                 (int)SkGetPackedA32(actualPixel)));
+                }
             }
         }
-        return errors;
+        SkDebugf("---- %s: %d (of %d) differing pixels, max per-channel mismatch"
+                 " R=%d G=%d B=%d A=%d\n",
+                 testName, differingPixels, width*height, errR, errG, errB, errA);
     }
 
-    // Compares bitmap "bitmap" to a reference bitmap read from disk;
-    // if they are different, writes out "bitmap" (in PNG format)
-    // within the diffPath subdir.
-    //
-    // Returns a description of the difference between "bitmap" and
-    // the reference bitmap, or ERROR_READING_REFERENCE_IMAGE if
-    // unable to read the reference bitmap from disk.
-    ErrorBitfield compare_to_reference_image_on_disk(
-      const char readPath [], const SkString& name, SkBitmap &bitmap,
-      const char diffPath [], const char renderModeDescriptor []) {
+    /**
+     * Compares actual checksum to expectations.
+     * Returns ERROR_NONE if they match, or some particular error code otherwise
+     *
+     * If fMismatchPath has been set, and there are pixel diffs, then the
+     * actual bitmap will be written out to a file within fMismatchPath.
+     *
+     * @param expectations what expectations to compare actualBitmap against
+     * @param actualBitmap the image we actually generated
+     * @param baseNameString name of test without renderModeDescriptor added
+     * @param renderModeDescriptor e.g., "-rtree", "-deferred"
+     * @param addToJsonSummary whether to add these results (both actual and
+     *        expected) to the JSON summary
+     *
+     * TODO: For now, addToJsonSummary is only set to true within
+     * compare_test_results_to_stored_expectations(), so results of our
+     * in-memory comparisons (Rtree vs regular, etc.) are not written to the
+     * JSON summary.  We may wish to change that.
+     */
+    ErrorBitfield compare_to_expectations(Expectations expectations,
+                                          const SkBitmap& actualBitmap,
+                                          const SkString& baseNameString,
+                                          const char renderModeDescriptor[],
+                                          bool addToJsonSummary=false) {
         ErrorBitfield retval;
-        SkString path = make_filename(readPath, "", name, "png");
-        SkBitmap referenceBitmap;
-        Json::Value expectedChecksumsArray;
+        Checksum actualChecksum = SkBitmapChecksummer::Compute64(actualBitmap);
+        SkString completeNameString = baseNameString;
+        completeNameString.append(renderModeDescriptor);
+        const char* completeName = completeNameString.c_str();
 
-        bool decodedReferenceBitmap =
-            SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
-                                       SkBitmap::kARGB_8888_Config,
-                                       SkImageDecoder::kDecodePixels_Mode,
-                                       NULL);
-        if (decodedReferenceBitmap) {
-            expectedChecksumsArray.append(Json::UInt64(
-                SkBitmapChecksummer::Compute64(referenceBitmap)));
-            retval = compare_to_reference_image_in_memory(name, bitmap,
-                                                          referenceBitmap,
-                                                          diffPath,
-                                                          renderModeDescriptor);
-        } else {
-            if (fNotifyMissingReadReference) {
-                fprintf(stderr, "FAILED to read %s\n", path.c_str());
-            }
-            RecordError(ERROR_READING_REFERENCE_IMAGE, name,
-                        renderModeDescriptor);
+        if (expectations.empty()) {
             retval = ERROR_READING_REFERENCE_IMAGE;
-        }
-
-        // Add this result to the appropriate JSON collection of actual results,
-        // depending on status.
-        Json::Value actualResults;
-        actualResults[kJsonKey_ActualResults_AnyStatus_Checksum] = Json::UInt64(
-            SkBitmapChecksummer::Compute64(bitmap));
-        if (decodedReferenceBitmap) {
-            if (ERROR_NONE == retval) {
-                fJsonActualResults_Succeeded[name.c_str()] = actualResults;
-            } else {
-                fJsonActualResults_Failed[name.c_str()] = actualResults;
-            }
+        } else if (expectations.match(actualChecksum)) {
+            retval = ERROR_NONE;
         } else {
-            fJsonActualResults_FailureIgnored[name.c_str()] = actualResults;
+            retval = ERROR_IMAGE_MISMATCH;
+
+            // Write out the "actuals" for any mismatches, if we have
+            // been directed to do so.
+            if (fMismatchPath) {
+                SkString path =
+                    make_filename(fMismatchPath, renderModeDescriptor,
+                                  baseNameString.c_str(), "png");
+                write_bitmap(path, actualBitmap);
+            }
+
+            // If we have access to a single expected bitmap, log more
+            // detail about the mismatch.
+            const SkBitmap *expectedBitmapPtr = expectations.asBitmap();
+            if (NULL != expectedBitmapPtr) {
+                report_bitmap_diffs(*expectedBitmapPtr, actualBitmap, completeName);
+            }
+        }
+        RecordError(retval, baseNameString, renderModeDescriptor);
+
+        if (addToJsonSummary) {
+            add_actual_results_to_json_summary(completeName, actualChecksum,
+                                               retval,
+                                               expectations.ignoreFailure());
+            add_expected_results_to_json_summary(completeName, expectations);
         }
 
-        // Add this test to the JSON collection of expected results.
+        return retval;
+    }
+
+    /**
+     * Add this result to the appropriate JSON collection of actual results,
+     * depending on status.
+     */
+    void add_actual_results_to_json_summary(const char testName[],
+                                            Checksum actualChecksum,
+                                            ErrorBitfield result,
+                                            bool ignoreFailure) {
+        Json::Value actualResults;
+        actualResults[kJsonKey_ActualResults_AnyStatus_Checksum] =
+            asJsonValue(actualChecksum);
+        if (ERROR_NONE == result) {
+            this->fJsonActualResults_Succeeded[testName] = actualResults;
+        } else {
+            if (ignoreFailure) {
+                // TODO: Once we have added the ability to compare
+                // actual results against expectations in a JSON file
+                // (where we can set ignore-failure to either true or
+                // false), add test cases that exercise ignored
+                // failures (both for ERROR_READING_REFERENCE_IMAGE
+                // and ERROR_IMAGE_MISMATCH).
+                this->fJsonActualResults_FailureIgnored[testName] =
+                    actualResults;
+            } else {
+                switch(result) {
+                case ERROR_READING_REFERENCE_IMAGE:
+                    // TODO: What about the case where there IS an
+                    // expected image checksum, but that gm test
+                    // doesn't actually run?  For now, those cases
+                    // will always be ignored, because gm only looks
+                    // at expectations that correspond to gm tests
+                    // that were actually run.
+                    //
+                    // Once we have the ability to express
+                    // expectations as a JSON file, we should fix this
+                    // (and add a test case for which an expectation
+                    // is given but the test is never run).
+                    this->fJsonActualResults_NoComparison[testName] =
+                        actualResults;
+                    break;
+                case ERROR_IMAGE_MISMATCH:
+                    this->fJsonActualResults_Failed[testName] = actualResults;
+                    break;
+                default:
+                    fprintf(stderr, "encountered unexpected result %d\n",
+                            result);
+                    SkDEBUGFAIL("encountered unexpected result");
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Add this test to the JSON collection of expected results.
+     */
+    void add_expected_results_to_json_summary(const char testName[],
+                                              Expectations expectations) {
         // For now, we assume that this collection starts out empty and we
         // just fill it in as we go; once gm accepts a JSON file as input,
         // we'll have to change that.
         Json::Value expectedResults;
-        expectedResults[kJsonKey_ExpectedResults_Checksums] = expectedChecksumsArray;
-        expectedResults[kJsonKey_ExpectedResults_IgnoreFailure] = !decodedReferenceBitmap;
-        fJsonExpectedResults[name.c_str()] = expectedResults;
-
-        return retval;
+        expectedResults[kJsonKey_ExpectedResults_Checksums] =
+            expectations.allowedChecksumsAsJson();
+        expectedResults[kJsonKey_ExpectedResults_IgnoreFailure] =
+            expectations.ignoreFailure();
+        this->fJsonExpectedResults[testName] = expectedResults;
     }
 
-    // NOTE: As far as I can tell, this function is NEVER called with a
-    // non-blank renderModeDescriptor, EXCEPT when readPath and writePath are
-    // both NULL (and thus no images are read from or written to disk).
-    // So I don't trust that the renderModeDescriptor is being used for
-    // anything other than debug output these days.
-    ErrorBitfield handle_test_results(GM* gm,
-                                      const ConfigData& gRec,
-                                      const char writePath [],
-                                      const char readPath [],
-                                      const char diffPath [],
-                                      const char renderModeDescriptor [],
-                                      SkBitmap& bitmap,
-                                      SkDynamicMemoryWStream* pdf,
-                                      const SkBitmap* referenceBitmap) {
+    /**
+     * Compare actualBitmap to expectations stored in this->fExpectationsSource.
+     *
+     * @param gm which test generated the actualBitmap
+     * @param gRec
+     * @param writePath unless this is NULL, write out actual images into this
+     *        directory
+     * @param actualBitmap bitmap generated by this run
+     * @param pdf
+     */
+    ErrorBitfield compare_test_results_to_stored_expectations(
+        GM* gm, const ConfigData& gRec, const char writePath[],
+        SkBitmap& actualBitmap, SkDynamicMemoryWStream* pdf) {
+
         SkString name = make_name(gm->shortName(), gRec.fName);
         ErrorBitfield retval = ERROR_NONE;
 
-        if (readPath && (gRec.fFlags & kRead_ConfigFlag)) {
-            retval |= compare_to_reference_image_on_disk(readPath, name, bitmap,
-                                                         diffPath,
-                                                         renderModeDescriptor);
+        ExpectationsSource *expectationsSource =
+            this->fExpectationsSource.get();
+        if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) {
+            /*
+             * Get the expected results for this test, as one or more allowed
+             * checksums. The current implementation of expectationsSource
+             * get this by computing the checksum of a single PNG file on disk.
+             *
+             * TODO(epoger): This relies on the fact that
+             * force_all_opaque() was called on the bitmap before it
+             * was written to disk as a PNG in the first place. If
+             * not, the checksum returned here may not match the
+             * checksum of actualBitmap, which *has* been run through
+             * force_all_opaque().
+             * See comments above complete_bitmap() for more detail.
+             */
+            Expectations expectations = expectationsSource->get(name.c_str());
+            retval |= compare_to_expectations(expectations, actualBitmap,
+                                              name, "", true);
+        } else {
+            // If we are running without expectations, we still want to
+            // record the actual results.
+            Checksum actualChecksum =
+                SkBitmapChecksummer::Compute64(actualBitmap);
+            add_actual_results_to_json_summary(name.c_str(), actualChecksum,
+                                               ERROR_READING_REFERENCE_IMAGE,
+                                               false);
         }
+
+        // TODO: Consider moving this into compare_to_expectations(),
+        // similar to fMismatchPath... for now, we don't do that, because
+        // we don't want to write out the actual bitmaps for all
+        // renderModes of all tests!  That would be a lot of files.
         if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
-            retval |= write_reference_image(gRec, writePath,
-                                            renderModeDescriptor,
-                                            name, bitmap, pdf);
+            retval |= write_reference_image(gRec, writePath, "",
+                                            name, actualBitmap, pdf);
         }
-        if (referenceBitmap) {
-            retval |= compare_to_reference_image_in_memory(
-              name, bitmap, *referenceBitmap, diffPath, renderModeDescriptor);
-        }
+
         return retval;
     }
 
-    static SkPicture* generate_new_picture(GM* gm, BbhType bbhType) {
+    /**
+     * Compare actualBitmap to referenceBitmap.
+     *
+     * @param gm which test generated the bitmap
+     * @param gRec
+     * @param renderModeDescriptor
+     * @param actualBitmap actual bitmap generated by this run
+     * @param referenceBitmap bitmap we expected to be generated
+     */
+    ErrorBitfield compare_test_results_to_reference_bitmap(
+        GM* gm, const ConfigData& gRec, const char renderModeDescriptor [],
+        SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) {
+
+        SkASSERT(referenceBitmap);
+        SkString name = make_name(gm->shortName(), gRec.fName);
+        Expectations expectations(*referenceBitmap);
+        return compare_to_expectations(expectations, actualBitmap,
+                                       name, renderModeDescriptor);
+    }
+
+    static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t recordFlags,
+                                           SkScalar scale = SK_Scalar1) {
         // Pictures are refcounted so must be on heap
         SkPicture* pict;
-        SkISize size = gm->getISize();
+        int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().width()), scale));
+        int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().height()), scale));
+
         if (kTileGrid_BbhType == bbhType) {
-            pict = new SkTileGridPicture(16, 16, size.width(), size.height());
+            pict = new SkTileGridPicture(16, 16, width, height);
         } else {
             pict = new SkPicture;
         }
-        uint32_t recordFlags = (kNone_BbhType == bbhType) ?
-            0 : SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
-        SkCanvas* cv = pict->beginRecording(size.width(), size.height(), recordFlags);
+        if (kNone_BbhType != bbhType) {
+            recordFlags |= SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
+        }
+        SkCanvas* cv = pict->beginRecording(width, height, recordFlags);
+        cv->scale(scale, scale);
         invokeGM(gm, cv, false, false);
         pict->endRecording();
 
@@ -711,13 +855,10 @@
     }
 
     // Test: draw into a bitmap or pdf.
-    // Depending on flags, possibly compare to an expected image
-    // and possibly output a diff image if it fails to match.
+    // Depending on flags, possibly compare to an expected image.
     ErrorBitfield test_drawing(GM* gm,
                                const ConfigData& gRec,
                                const char writePath [],
-                               const char readPath [],
-                               const char diffPath [],
                                GrContext* context,
                                GrRenderTarget* rt,
                                SkBitmap* bitmap) {
@@ -729,6 +870,9 @@
             ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
                                                   false);
             if (ERROR_NONE != errors) {
+                // TODO: Add a test to exercise what the stdout and
+                // JSON look like if we get an "early error" while
+                // trying to generate the image.
                 return errors;
             }
         } else if (gRec.fBackend == kPDF_Backend) {
@@ -741,14 +885,13 @@
         } else if (gRec.fBackend == kXPS_Backend) {
             generate_xps(gm, document);
         }
-        return handle_test_results(gm, gRec, writePath, readPath, diffPath,
-                                   "", *bitmap, &document, NULL);
+        return compare_test_results_to_stored_expectations(
+            gm, gRec, writePath, *bitmap, &document);
     }
 
     ErrorBitfield test_deferred_drawing(GM* gm,
                                         const ConfigData& gRec,
                                         const SkBitmap& referenceBitmap,
-                                        const char diffPath [],
                                         GrContext* context,
                                         GrRenderTarget* rt) {
         SkDynamicMemoryWStream document;
@@ -761,18 +904,15 @@
             if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
                 return ERROR_NONE;
             }
-            return handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                                       "-deferred", bitmap, NULL,
-                                       &referenceBitmap);
+            return compare_test_results_to_reference_bitmap(
+                gm, gRec, "-deferred", bitmap, &referenceBitmap);
         }
         return ERROR_NONE;
     }
 
     ErrorBitfield test_pipe_playback(GM* gm,
                                      const ConfigData& gRec,
-                                     const SkBitmap& referenceBitmap,
-                                     const char readPath [],
-                                     const char diffPath []) {
+                                     const SkBitmap& referenceBitmap) {
         ErrorBitfield errors = ERROR_NONE;
         for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
             SkBitmap bitmap;
@@ -784,12 +924,12 @@
             SkCanvas* pipeCanvas = writer.startRecording(
               &pipeController, gPipeWritingFlagCombos[i].flags);
             invokeGM(gm, pipeCanvas, false, false);
+            complete_bitmap(&bitmap);
             writer.endRecording();
             SkString string("-pipe");
             string.append(gPipeWritingFlagCombos[i].name);
-            errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                                          string.c_str(), bitmap, NULL,
-                                          &referenceBitmap);
+            errors |= compare_test_results_to_reference_bitmap(
+                gm, gRec, string.c_str(), bitmap, &referenceBitmap);
             if (errors != ERROR_NONE) {
                 break;
             }
@@ -798,8 +938,7 @@
     }
 
     ErrorBitfield test_tiled_pipe_playback(
-      GM* gm, const ConfigData& gRec, const SkBitmap& referenceBitmap,
-      const char readPath [], const char diffPath []) {
+      GM* gm, const ConfigData& gRec, const SkBitmap& referenceBitmap) {
         ErrorBitfield errors = ERROR_NONE;
         for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
             SkBitmap bitmap;
@@ -811,12 +950,12 @@
             SkCanvas* pipeCanvas = writer.startRecording(
               &pipeController, gPipeWritingFlagCombos[i].flags);
             invokeGM(gm, pipeCanvas, false, false);
+            complete_bitmap(&bitmap);
             writer.endRecording();
             SkString string("-tiled pipe");
             string.append(gPipeWritingFlagCombos[i].name);
-            errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                                          string.c_str(), bitmap, NULL,
-                                          &referenceBitmap);
+            errors |= compare_test_results_to_reference_bitmap(
+                gm, gRec, string.c_str(), bitmap, &referenceBitmap);
             if (errors != ERROR_NONE) {
                 break;
             }
@@ -829,17 +968,22 @@
     // They are public for now, to allow easier setting by tool_main().
     //
 
-    // if true, emit a message when we can't find a reference image to compare
-    bool fNotifyMissingReadReference;
-
     bool fUseFileHierarchy;
 
+    const char* fMismatchPath;
+
     // information about all failed tests we have encountered so far
     SkTArray<FailRec> fFailedTests;
 
+    // Where to read expectations (expected image checksums, etc.) from.
+    // If unset, we don't do comparisons.
+    SkAutoTUnref<ExpectationsSource> fExpectationsSource;
+
+    // JSON summaries that we generate as we go (just for output).
     Json::Value fJsonExpectedResults;
     Json::Value fJsonActualResults_Failed;
     Json::Value fJsonActualResults_FailureIgnored;
+    Json::Value fJsonActualResults_NoComparison;
     Json::Value fJsonActualResults_Succeeded;
 
 }; // end of GMMain class definition
@@ -902,16 +1046,18 @@
 // one, e.g.:
 // [--replay|--noreplay]: whether to exercise SkPicture replay; default is yes
 "    [--nodeferred]: skip the deferred rendering test pass\n"
-"    [--diffPath|-d <path>]: write difference images into this directory\n"
 "    [--disable-missing-warning]: don't print a message to stderr if\n"
 "        unable to read a reference image for any tests (NOT default behavior)\n"
 "    [--enable-missing-warning]: print message to stderr (but don't fail) if\n"
 "        unable to read a reference image for any tests (default behavior)\n"
+"    [--exclude-config]: disable this config (may be used multiple times)\n"
 "    [--forceBWtext]: disable text anti-aliasing\n"
 "    [--help|-h]: show this help message\n"
 "    [--hierarchy|--nohierarchy]: whether to use multilevel directory structure\n"
 "        when reading/writing files; default is no\n"
 "    [--match <substring>]: only run tests whose name includes this substring\n"
+"    [--mismatchPath <path>]: write images for tests that failed due to\n"
+"        pixel mismatched into this directory"
 "    [--modulo <remainder> <divisor>]: only run tests for which \n"
 "        testIndex %% divisor == remainder\n"
 "    [--nopdf]: skip the pdf rendering test pass\n"
@@ -925,7 +1071,10 @@
 "    [--notexturecache]: disable the gpu texture cache\n"
 "    [--tiledPipe]: Exercise tiled SkGPipe replay\n"
 "    [--notileGrid]: Do not exercise the tile grid variant of SkPicture\n"
+"    [--tileGridReplayScales <scales>]: Comma separated list of floating-point scale\n"
+"        factors to be used for tileGrid playback testing. Default value: 1.0\n"
 "    [--writeJsonSummary <path>]: write a JSON-formatted result summary to this file\n"
+"    [--verbose] print diagnostics (e.g. list each config to be tested)\n"
 "    [--writePath|-w <path>]: write rendered images into this directory\n"
 "    [--writePicturePath|-wp <path>]: write .skp files into this directory\n"
              );
@@ -995,10 +1144,17 @@
 #endif
 }
 
+template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) {
+    int index = array->find(value);
+    if (index < 0) {
+        *array->append() = value;
+    }
+}
+
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
 
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
     gPrintInstCount = true;
 #endif
 
@@ -1013,9 +1169,11 @@
     const char* writePath = NULL;   // if non-null, where we write the originals
     const char* writePicturePath = NULL;    // if non-null, where we write serialized pictures
     const char* readPath = NULL;    // if non-null, were we read from to compare
-    const char* diffPath = NULL;    // if non-null, where we write our diffs (from compare)
     const char* resourcePath = NULL;// if non-null, where we read from for image resources
 
+    // if true, emit a message when we can't find a reference image to compare
+    bool notifyMissingReadReference = true;
+
     SkTDArray<const char*> fMatches;
 
     bool doPDF = true;
@@ -1026,8 +1184,12 @@
     bool doDeferred = true;
     bool doRTree = true;
     bool doTileGrid = true;
+    bool doVerbose = false;
     bool disableTextureCache = false;
     SkTDArray<size_t> configs;
+    SkTDArray<size_t> excludeConfigs;
+    SkTDArray<SkScalar> tileGridReplayScales;
+    *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scale 1.0
     bool userConfig = false;
 
     int moduloRemainder = -1;
@@ -1041,7 +1203,7 @@
             if (argv < stop) {
                 int index = findConfig(*argv);
                 if (index >= 0) {
-                    *configs.append() = index;
+                    appendUnique<size_t>(&configs, index);
                     userConfig = true;
                 } else {
                     SkString str;
@@ -1055,22 +1217,57 @@
                 usage(commandName);
                 return -1;
             }
+        } else if (strcmp(*argv, "--exclude-config") == 0) {
+            argv++;
+            if (argv < stop) {
+                int index = findConfig(*argv);
+                if (index >= 0) {
+                    *excludeConfigs.append() = index;
+                } else {
+                    SkString str;
+                    str.printf("unrecognized exclude-config %s\n", *argv);
+                    SkDebugf(str.c_str());
+                    usage(commandName);
+                    return -1;
+                }
+            } else {
+                SkDebugf("missing arg for --exclude-config\n");
+                usage(commandName);
+                return -1;
+            }
         } else if (strcmp(*argv, "--nodeferred") == 0) {
             doDeferred = false;
-        } else if ((0 == strcmp(*argv, "--diffPath")) ||
-                   (0 == strcmp(*argv, "-d"))) {
+        } else if (strcmp(*argv, "--disable-missing-warning") == 0) {
+            notifyMissingReadReference = false;
+        } else if (strcmp(*argv, "--mismatchPath") == 0) {
             argv++;
             if (argv < stop && **argv) {
-                diffPath = *argv;
+                gmmain.fMismatchPath = *argv;
             }
-        } else if (strcmp(*argv, "--disable-missing-warning") == 0) {
-            gmmain.fNotifyMissingReadReference = false;
         } else if (strcmp(*argv, "--nortree") == 0) {
             doRTree = false;
         } else if (strcmp(*argv, "--notileGrid") == 0) {
             doTileGrid = false;
+        } else if (strcmp(*argv, "--tileGridReplayScales") == 0) {
+            tileGridReplayScales.reset();
+            ++argv;
+            if (argv < stop) {
+                char* token = strtok(*argv, ",");
+                while (NULL != token) {
+                    double val = atof(token);
+                    if (0 < val) {
+                        *tileGridReplayScales.append() = SkDoubleToScalar(val);
+                    }
+                    token = strtok(NULL, ",");
+                }
+            }
+            if (0 == tileGridReplayScales.count()) {
+                // Should have at least one scale
+                usage(commandName);
+                return -1;
+            }
         } else if (strcmp(*argv, "--enable-missing-warning") == 0) {
-            gmmain.fNotifyMissingReadReference = true;
+            notifyMissingReadReference = true;
         } else if (strcmp(*argv, "--forceBWtext") == 0) {
             gForceBWtext = true;
         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
@@ -1128,6 +1325,8 @@
             disableTextureCache = true;
         } else if (strcmp(*argv, "--tiledPipe") == 0) {
             doTiledPipe = true;
+        } else if (!strcmp(*argv, "--verbose") || !strcmp(*argv, "-v")) {
+            doVerbose = true;
         } else if ((0 == strcmp(*argv, "--writePath")) ||
             (0 == strcmp(*argv, "-w"))) {
             argv++;
@@ -1161,11 +1360,43 @@
             *configs.append() = i;
         }
     }
+    // now remove any explicitly excluded configs
+    for (int i = 0; i < excludeConfigs.count(); ++i) {
+        int index = configs.find(excludeConfigs[i]);
+        if (index >= 0) {
+            configs.remove(index);
+            // now assert that there was only one copy in configs[]
+            SkASSERT(configs.find(excludeConfigs[i]) < 0);
+        }
+    }
+
+    if (doVerbose) {
+        SkString str;
+        str.printf("gm: %d configs:", configs.count());
+        for (int i = 0; i < configs.count(); ++i) {
+            str.appendf(" %s", gRec[configs[i]].fName);
+        }
+        SkDebugf("%s\n", str.c_str());
+    }
 
     GM::SetResourcePath(resourcePath);
 
     if (readPath) {
-        fprintf(stderr, "reading from %s\n", readPath);
+        if (!sk_exists(readPath)) {
+            fprintf(stderr, "readPath %s does not exist!\n", readPath);
+            return -1;
+        }
+        if (sk_isdir(readPath)) {
+            fprintf(stderr, "reading from %s\n", readPath);
+            gmmain.fExpectationsSource.reset(SkNEW_ARGS(
+                IndividualImageExpectationsSource,
+                (readPath, notifyMissingReadReference)));
+        } else {
+            fprintf(stderr, "reading expectations from JSON summary file %s ",
+                    readPath);
+            fprintf(stderr, "BUT WE DON'T KNOW HOW TO DO THIS YET!\n");
+            return -1;
+        }
     }
     if (writePath) {
         fprintf(stderr, "writing to %s\n", writePath);
@@ -1297,7 +1528,7 @@
 
             if (ERROR_NONE == renderErrors) {
                 renderErrors |= gmmain.test_drawing(gm, config, writePath,
-                                                    readPath, diffPath, GetGr(),
+                                                    GetGr(),
                                                     renderTarget,
                                                     &comparisonBitmap);
             }
@@ -1307,7 +1538,7 @@
                  kRaster_Backend == config.fBackend)) {
                 renderErrors |= gmmain.test_deferred_drawing(gm, config,
                                                              comparisonBitmap,
-                                                             diffPath, GetGr(),
+                                                             GetGr(),
                                                              renderTarget);
             }
 
@@ -1325,18 +1556,15 @@
             ErrorBitfield pictErrors = ERROR_NONE;
 
             //SkAutoTUnref<SkPicture> pict(generate_new_picture(gm));
-            SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType);
+            SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0);
             SkAutoUnref aur(pict);
 
             if ((ERROR_NONE == testErrors) && doReplay) {
                 SkBitmap bitmap;
                 gmmain.generate_image_from_picture(gm, compareConfig, pict,
                                                    &bitmap);
-                pictErrors |= gmmain.handle_test_results(gm, compareConfig,
-                                                         NULL, NULL, diffPath,
-                                                         "-replay", bitmap,
-                                                         NULL,
-                                                         &comparisonBitmap);
+                pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                    gm, compareConfig, "-replay", bitmap, &comparisonBitmap);
             }
 
             if ((ERROR_NONE == testErrors) &&
@@ -1348,18 +1576,15 @@
                 SkBitmap bitmap;
                 gmmain.generate_image_from_picture(gm, compareConfig, repict,
                                                    &bitmap);
-                pictErrors |= gmmain.handle_test_results(gm, compareConfig,
-                                                         NULL, NULL, diffPath,
-                                                         "-serialize", bitmap,
-                                                         NULL,
-                                                         &comparisonBitmap);
+                pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                    gm, compareConfig, "-serialize", bitmap, &comparisonBitmap);
             }
 
             if (writePicturePath) {
                 const char* pictureSuffix = "skp";
-                SkString path = gmmain.make_filename(writePicturePath, "",
-                                                     SkString(gm->shortName()),
-                                                     pictureSuffix);
+                SkString path = make_filename(writePicturePath, "",
+                                              gm->shortName(),
+                                              pictureSuffix);
                 SkFILEWStream stream(path.c_str());
                 pict->serialize(&stream);
             }
@@ -1367,30 +1592,45 @@
             testErrors |= pictErrors;
         }
 
+        // TODO: add a test in which the RTree rendering results in a
+        // different bitmap than the standard rendering.  It should
+        // show up as failed in the JSON summary, and should be listed
+        // in the stdout also.
         if (!(gmFlags & GM::kSkipPicture_Flag) && doRTree) {
-            SkPicture* pict = gmmain.generate_new_picture(gm, kRTree_BbhType);
+            SkPicture* pict = gmmain.generate_new_picture(
+                gm, kRTree_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag);
             SkAutoUnref aur(pict);
             SkBitmap bitmap;
             gmmain.generate_image_from_picture(gm, compareConfig, pict,
                                                &bitmap);
-            testErrors |= gmmain.handle_test_results(gm, compareConfig,
-                                                     NULL, NULL, diffPath,
-                                                     "-rtree", bitmap,
-                                                     NULL,
-                                                     &comparisonBitmap);
+            testErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                gm, compareConfig, "-rtree", bitmap, &comparisonBitmap);
         }
 
         if (!(gmFlags & GM::kSkipPicture_Flag) && doTileGrid) {
-            SkPicture* pict = gmmain.generate_new_picture(gm, kTileGrid_BbhType);
-            SkAutoUnref aur(pict);
-            SkBitmap bitmap;
-            gmmain.generate_image_from_picture(gm, compareConfig, pict,
-                                               &bitmap);
-            testErrors |= gmmain.handle_test_results(gm, compareConfig,
-                                                     NULL, NULL, diffPath,
-                                                     "-tilegrid", bitmap,
-                                                     NULL,
-                                                     &comparisonBitmap);
+            for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); ++scaleIndex) {
+                SkScalar replayScale = tileGridReplayScales[scaleIndex];
+                if ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1)
+                    continue;
+                // We record with the reciprocal scale to obtain a replay
+                // result that can be validated against comparisonBitmap.
+                SkScalar recordScale = SkScalarInvert(replayScale);
+                SkPicture* pict = gmmain.generate_new_picture(
+                    gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag,
+                    recordScale);
+                SkAutoUnref aur(pict);
+                SkBitmap bitmap;
+                gmmain.generate_image_from_picture(gm, compareConfig, pict,
+                                                   &bitmap, replayScale);
+                SkString suffix("-tilegrid");
+                if (SK_Scalar1 != replayScale) {
+                    suffix += "-scale-";
+                    suffix.appendScalar(replayScale);
+                }
+                testErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                    gm, compareConfig, suffix.c_str(), bitmap,
+                    &comparisonBitmap);
+            }
         }
 
         // run the pipe centric GM steps
@@ -1400,17 +1640,14 @@
 
             if ((ERROR_NONE == testErrors) && doPipe) {
                 pipeErrors |= gmmain.test_pipe_playback(gm, compareConfig,
-                                                        comparisonBitmap,
-                                                        readPath, diffPath);
+                                                        comparisonBitmap);
             }
 
             if ((ERROR_NONE == testErrors) &&
                 (ERROR_NONE == pipeErrors) &&
                 doTiledPipe && !(gmFlags & GM::kSkipTiled_Flag)) {
                 pipeErrors |= gmmain.test_tiled_pipe_playback(gm, compareConfig,
-                                                              comparisonBitmap,
-                                                              readPath,
-                                                              diffPath);
+                                                              comparisonBitmap);
             }
 
             testErrors |= pipeErrors;
@@ -1419,13 +1656,13 @@
         // Update overall results.
         // We only tabulate the particular error types that we currently
         // care about (e.g., missing reference images). Later on, if we
-        // want to also tabulate pixel mismatches vs dimension mistmatches
-        // (or whatever else), we can do so.
+        // want to also tabulate other error types, we can do so.
         testsRun++;
-        if (ERROR_NONE == testErrors) {
-            testsPassed++;
-        } else if (ERROR_READING_REFERENCE_IMAGE & testErrors) {
+        if (!gmmain.fExpectationsSource.get() ||
+            (ERROR_READING_REFERENCE_IMAGE & testErrors)) {
             testsMissingReferenceImages++;
+        } else if (ERROR_NONE == testErrors) {
+            testsPassed++;
         } else {
             testsFailed++;
         }
@@ -1442,6 +1679,8 @@
             gmmain.fJsonActualResults_Failed;
         actualResults[kJsonKey_ActualResults_FailureIgnored] =
             gmmain.fJsonActualResults_FailureIgnored;
+        actualResults[kJsonKey_ActualResults_NoComparison] =
+            gmmain.fJsonActualResults_NoComparison;
         actualResults[kJsonKey_ActualResults_Succeeded] =
             gmmain.fJsonActualResults_Succeeded;
         Json::Value root;
diff --git a/gm/gradients.cpp b/gm/gradients.cpp
index a2d1de2..004d991 100644
--- a/gm/gradients.cpp
+++ b/gm/gradients.cpp
@@ -367,5 +367,3 @@
 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
 static GMRegistry reg6(MyFactory6);
 }
-
-
diff --git a/gm/gradtext.cpp b/gm/gradtext.cpp
index 67dd11e..d782338 100644
--- a/gm/gradtext.cpp
+++ b/gm/gradtext.cpp
@@ -164,4 +164,3 @@
 static GMRegistry Creg(CMyFactory);
 static GMRegistry Creg2(CMyFactory2);
 }
-
diff --git a/gm/hittestpath.cpp b/gm/hittestpath.cpp
index 4347c9b..0327571 100644
--- a/gm/hittestpath.cpp
+++ b/gm/hittestpath.cpp
@@ -73,5 +73,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new HitTestPathGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
-
diff --git a/gm/image.cpp b/gm/image.cpp
index e54d3c7..974f5f9 100644
--- a/gm/image.cpp
+++ b/gm/image.cpp
@@ -192,4 +192,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new ImageGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
index c7f043e..6879e79 100644
--- a/gm/imagefiltersbase.cpp
+++ b/gm/imagefiltersbase.cpp
@@ -165,6 +165,14 @@
         canvas->drawRect(r, paint);
     }
 
+    virtual uint32_t onGetFlags() const {
+        // Because of the use of drawSprite, this test is excluded
+        // from scaled replay tests because drawSprite ignores the
+        // reciprocal scale that is applied at record time, which is
+        // the intended behavior of drawSprite.
+        return kSkipScaledReplay_Flag;
+    }
+
     virtual void onDraw(SkCanvas* canvas) {
         void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = {
             draw_paint,
@@ -215,5 +223,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new ImageFiltersBaseGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
-
diff --git a/gm/imagefiltersgraph.cpp b/gm/imagefiltersgraph.cpp
index 9c70770..739a18f 100644
--- a/gm/imagefiltersgraph.cpp
+++ b/gm/imagefiltersgraph.cpp
@@ -92,5 +92,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new ImageFiltersGraphGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
-
diff --git a/gm/lighting.cpp b/gm/lighting.cpp
index 9db34fd..de5e330 100644
--- a/gm/lighting.cpp
+++ b/gm/lighting.cpp
@@ -42,6 +42,14 @@
         return make_isize(WIDTH, HEIGHT);
     }
 
+    void drawClippedBitmap(SkCanvas* canvas, const SkPaint& paint, int x, int y) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+        canvas->restore();
+    }
+
     virtual void onDraw(SkCanvas* canvas) {
         if (!fInitialized) {
             make_bitmap();
@@ -76,17 +84,17 @@
         SkColor white(0xFFFFFFFF);
         SkPaint paint;
         paint.setImageFilter(SkLightingImageFilter::CreatePointLitDiffuse(pointLocation, white, surfaceScale, kd))->unref();
-        canvas->drawSprite(fBitmap, 0, 0, &paint);
+        drawClippedBitmap(canvas, paint, 0, 0);
         paint.setImageFilter(SkLightingImageFilter::CreateDistantLitDiffuse(distantDirection, white, surfaceScale, kd))->unref();
-        canvas->drawSprite(fBitmap, 110, 0, &paint);
+        drawClippedBitmap(canvas, paint, 110, 0);
         paint.setImageFilter(SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, kd))->unref();
-        canvas->drawSprite(fBitmap, 220, 0, &paint);
+        drawClippedBitmap(canvas, paint, 220, 0);
         paint.setImageFilter(SkLightingImageFilter::CreatePointLitSpecular(pointLocation, white, surfaceScale, ks, shininess))->unref();
-        canvas->drawSprite(fBitmap, 0, 110, &paint);
+        drawClippedBitmap(canvas, paint, 0, 110);
         paint.setImageFilter(SkLightingImageFilter::CreateDistantLitSpecular(distantDirection, white, surfaceScale, ks, shininess))->unref();
-        canvas->drawSprite(fBitmap, 110, 110, &paint);
+        drawClippedBitmap(canvas, paint, 110, 110);
         paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, ks, shininess))->unref();
-        canvas->drawSprite(fBitmap, 220, 110, &paint);
+        drawClippedBitmap(canvas, paint, 220, 110);
     }
 
 private:
diff --git a/gm/matrixconvolution.cpp b/gm/matrixconvolution.cpp
index 6bb7d99..59bd380 100644
--- a/gm/matrixconvolution.cpp
+++ b/gm/matrixconvolution.cpp
@@ -58,7 +58,11 @@
         SkPaint paint;
         SkAutoTUnref<SkImageFilter> filter(SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias, target, tileMode, convolveAlpha)));
         paint.setImageFilter(filter);
-        canvas->drawSprite(fBitmap, x, y, &paint);
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+        canvas->restore();
     }
 
     virtual void onDraw(SkCanvas* canvas) {
diff --git a/gm/modecolorfilters.cpp b/gm/modecolorfilters.cpp
index fa0c091..3e1ccb8 100644
--- a/gm/modecolorfilters.cpp
+++ b/gm/modecolorfilters.cpp
@@ -116,7 +116,7 @@
             SkXfermode::kDstATop_Mode,
             SkXfermode::kXor_Mode,
             SkXfermode::kPlus_Mode,
-            SkXfermode::kMultiply_Mode,
+            SkXfermode::kModulate_Mode,
         };
 
         SkPaint paint;
diff --git a/gm/ninepatchstretch.cpp b/gm/ninepatchstretch.cpp
index 773a692..c4c5291 100644
--- a/gm/ninepatchstretch.cpp
+++ b/gm/ninepatchstretch.cpp
@@ -115,6 +115,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
-
-
diff --git a/gm/pathfill.cpp b/gm/pathfill.cpp
index bfaabc9..993917a 100644
--- a/gm/pathfill.cpp
+++ b/gm/pathfill.cpp
@@ -220,4 +220,3 @@
 
 static skiagm::GM* F1(void*) { return new PathInverseFillGM; }
 static skiagm::GMRegistry gR1(F1);
-
diff --git a/gm/pathinterior.cpp b/gm/pathinterior.cpp
index f7e0b5e..559fb89 100644
--- a/gm/pathinterior.cpp
+++ b/gm/pathinterior.cpp
@@ -111,4 +111,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new PathInteriorGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
diff --git a/gm/points.cpp b/gm/points.cpp
index 0646d61..0a83acf 100644
--- a/gm/points.cpp
+++ b/gm/points.cpp
@@ -71,4 +71,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/poly2poly.cpp b/gm/poly2poly.cpp
index 9ad5bdc..20c117f 100644
--- a/gm/poly2poly.cpp
+++ b/gm/poly2poly.cpp
@@ -106,4 +106,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/rrect.cpp b/gm/rrect.cpp
index b07e3c0..29d0300 100644
--- a/gm/rrect.cpp
+++ b/gm/rrect.cpp
@@ -167,4 +167,3 @@
 };
 
 DEF_GM( return new RRectGM; )
-
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index cccece9..299e8d8 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -77,7 +77,7 @@
 
         // The first complex case needs special handling since it is a square
         fRRects[kNumSimpleCases].setRectRadii(SkRect::MakeWH(kTileY-2, kTileY-2), gRadii[0]);
-        for (int i = 1; i < SK_ARRAY_COUNT(gRadii); ++i) {
+        for (size_t i = 1; i < SK_ARRAY_COUNT(gRadii); ++i) {
             fRRects[kNumSimpleCases+i].setRectRadii(SkRect::MakeWH(kTileX-2, kTileY-2), gRadii[i]);
         }
     }
@@ -146,5 +146,3 @@
 DEF_GM( return new RRectGM(true, true); )
 
 }
-
-
diff --git a/gm/spritebitmap.cpp b/gm/spritebitmap.cpp
new file mode 100644
index 0000000..8aaa6ea
--- /dev/null
+++ b/gm/spritebitmap.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkBlurImageFilter.h"
+
+static void make_bm(SkBitmap* bm) {
+    bm->setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    bm->allocPixels();
+    bm->eraseColor(SK_ColorBLUE);
+
+    SkCanvas canvas(*bm);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setColor(SK_ColorRED);
+    canvas.drawCircle(50, 50, 50, paint);
+}
+
+static void draw_2_bitmaps(SkCanvas* canvas, const SkBitmap& bm, bool doClip,
+                           int dx, int dy, SkImageFilter* filter = NULL) {
+    SkAutoCanvasRestore acr(canvas, true);
+    SkPaint paint;
+
+    SkRect clipR = SkRect::MakeXYWH(SkIntToScalar(dx),
+                                    SkIntToScalar(dy),
+                                    SkIntToScalar(bm.width()),
+                                    SkIntToScalar(bm.height()));
+
+    paint.setImageFilter(filter);
+    clipR.inset(5, 5);
+
+    if (doClip) {
+        canvas->save();
+        canvas->clipRect(clipR);
+    }
+    canvas->drawSprite(bm, dx, dy, &paint);
+    if (doClip) {
+        canvas->restore();
+    }
+
+    canvas->translate(SkIntToScalar(bm.width() + 20), 0);
+
+    if (doClip) {
+        canvas->save();
+        canvas->clipRect(clipR);
+    }
+    canvas->drawBitmap(bm, SkIntToScalar(dx), SkIntToScalar(dy), &paint);
+    if (doClip) {
+        canvas->restore();
+    }
+}
+
+/**
+ *  Compare output of drawSprite and drawBitmap (esp. clipping and imagefilters)
+ */
+class SpriteBitmapGM : public skiagm::GM {
+public:
+    SpriteBitmapGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("spritebitmap");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkBitmap bm;
+        make_bm(&bm);
+
+        int dx = 10;
+        int dy = 10;
+
+        SkScalar sigma = 8;
+        SkAutoTUnref<SkImageFilter> filter(new SkBlurImageFilter(sigma, sigma));
+
+        draw_2_bitmaps(canvas, bm, false, dx, dy);
+        dy += bm.height() + 20;
+        draw_2_bitmaps(canvas, bm, false, dx, dy, filter);
+        dy += bm.height() + 20;
+        draw_2_bitmaps(canvas, bm, true, dx, dy);
+        dy += bm.height() + 20;
+        draw_2_bitmaps(canvas, bm, true, dx, dy, filter);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new SpriteBitmapGM; )
diff --git a/gm/srcmode.cpp b/gm/srcmode.cpp
index c99a6b2..66729e2 100644
--- a/gm/srcmode.cpp
+++ b/gm/srcmode.cpp
@@ -149,4 +149,3 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 DEF_GM(return new SrcModeGM;)
-
diff --git a/gm/strokerect.cpp b/gm/strokerect.cpp
index 99a50ef..489f7b4 100644
--- a/gm/strokerect.cpp
+++ b/gm/strokerect.cpp
@@ -110,4 +110,3 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 DEF_GM(return new StrokeRectGM;)
-
diff --git a/gm/strokerects.cpp b/gm/strokerects.cpp
index 62a9639..61ee9e0 100644
--- a/gm/strokerects.cpp
+++ b/gm/strokerects.cpp
@@ -81,4 +81,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/strokes.cpp b/gm/strokes.cpp
index c479d69..49a7fc6 100644
--- a/gm/strokes.cpp
+++ b/gm/strokes.cpp
@@ -248,5 +248,3 @@
 static skiagm::GMRegistry R0(F0);
 static skiagm::GMRegistry R1(F1);
 static skiagm::GMRegistry R2(F2);
-
-
diff --git a/gm/techtalk1.cpp b/gm/techtalk1.cpp
index 00c4956..442d73b 100644
--- a/gm/techtalk1.cpp
+++ b/gm/techtalk1.cpp
@@ -395,5 +395,3 @@
 
 //static GM* MyFactory(void*) { return new TalkGM(0, false); }
 //static GMRegistry reg(MyFactory);
-
-
diff --git a/gm/testimagefilters.cpp b/gm/testimagefilters.cpp
index 65d1be4..73581ba 100644
--- a/gm/testimagefilters.cpp
+++ b/gm/testimagefilters.cpp
@@ -142,5 +142,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new TestImageFiltersGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
-
diff --git a/gm/tests/inputs/different-pixels/8888/dashing2.png b/gm/tests/inputs/different-pixels/8888/dashing2.png
deleted file mode 100644
index 3a0bc2e..0000000
--- a/gm/tests/inputs/different-pixels/8888/dashing2.png
+++ /dev/null
Binary files differ
diff --git a/gm/tests/inputs/empty-dir/README b/gm/tests/inputs/empty-dir/README
deleted file mode 100644
index 4d39134..0000000
--- a/gm/tests/inputs/empty-dir/README
+++ /dev/null
@@ -1 +0,0 @@
-This directory intentionally left empty. Except for this file.
diff --git a/gm/tests/inputs/identical-bytes/8888/dashing2.png b/gm/tests/inputs/identical-bytes/8888/dashing2.png
deleted file mode 100644
index 465c019..0000000
--- a/gm/tests/inputs/identical-bytes/8888/dashing2.png
+++ /dev/null
Binary files differ
diff --git a/gm/tests/inputs/identical-pixels/8888/dashing2.png b/gm/tests/inputs/identical-pixels/8888/dashing2.png
deleted file mode 100644
index 2d05b01..0000000
--- a/gm/tests/inputs/identical-pixels/8888/dashing2.png
+++ /dev/null
Binary files differ
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/command_line b/gm/tests/outputs/aaclip-readback/output-expected/command_line
new file mode 100644
index 0000000..db66e2e
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --match simpleaaclip_path --config 8888 --config 565 -r gm/tests/tempfiles/aaclip-images --writeJsonSummary gm/tests/outputs/aaclip-readback/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/json-summary.txt b/gm/tests/outputs/aaclip-readback/output-expected/json-summary.txt
new file mode 100644
index 0000000..83d2e77
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/json-summary.txt
@@ -0,0 +1,25 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : null,
+      "succeeded" : {
+         "simpleaaclip_path_565" : {
+            "checksum" : FAKE
+         },
+         "simpleaaclip_path_8888" : {
+            "checksum" : FAKE
+         }
+      }
+   },
+   "expected-results" : {
+      "simpleaaclip_path_565" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
+      "simpleaaclip_path_8888" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      }
+   }
+}
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/return_value b/gm/tests/outputs/aaclip-readback/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/stdout b/gm/tests/outputs/aaclip-readback/output-expected/stdout
new file mode 100644
index 0000000..52931ca
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/stdout
@@ -0,0 +1,3 @@
+reading from gm/tests/tempfiles/aaclip-images
+drawing... simpleaaclip_path [640 480]
+Ran 1 tests: 1 passed, 0 failed, 0 missing reference images
diff --git a/gm/tests/outputs/aaclip-write/output-expected/command_line b/gm/tests/outputs/aaclip-write/output-expected/command_line
new file mode 100644
index 0000000..db030e1
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --match simpleaaclip_path --config 8888 --config 565 -w gm/tests/tempfiles/aaclip-images --writeJsonSummary gm/tests/outputs/aaclip-write/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/aaclip-write/output-expected/json-summary.txt b/gm/tests/outputs/aaclip-write/output-expected/json-summary.txt
new file mode 100644
index 0000000..832f828
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/json-summary.txt
@@ -0,0 +1,16 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : {
+         "simpleaaclip_path_565" : {
+            "checksum" : FAKE
+         },
+         "simpleaaclip_path_8888" : {
+            "checksum" : FAKE
+         }
+      },
+      "succeeded" : null
+   },
+   "expected-results" : null
+}
diff --git a/gm/tests/outputs/aaclip-write/output-expected/return_value b/gm/tests/outputs/aaclip-write/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/aaclip-write/output-expected/stdout b/gm/tests/outputs/aaclip-write/output-expected/stdout
new file mode 100644
index 0000000..5211664
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/stdout
@@ -0,0 +1,3 @@
+writing to gm/tests/tempfiles/aaclip-images
+drawing... simpleaaclip_path [640 480]
+Ran 1 tests: 0 passed, 0 failed, 1 missing reference images
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/command_line b/gm/tests/outputs/compared-against-different-pixels/output-expected/command_line
index 4253e73..d887baa 100644
--- a/gm/tests/outputs/compared-against-different-pixels/output-expected/command_line
+++ b/gm/tests/outputs/compared-against-different-pixels/output-expected/command_line
@@ -1 +1 @@
-out/Debug/gm --hierarchy --match dashing2 --config 8888 -r gm/tests/inputs/different-pixels --writeJsonSummary gm/tests/outputs/compared-against-different-pixels/output-actual/json-summary.txt -w gm/tests/outputs/compared-against-different-pixels/output-actual/images
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/different-pixels --writeJsonSummary gm/tests/outputs/compared-against-different-pixels/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/images/8888/dashing2.png b/gm/tests/outputs/compared-against-different-pixels/output-expected/images/8888/dashing2.png
deleted file mode 100644
index 465c019..0000000
--- a/gm/tests/outputs/compared-against-different-pixels/output-expected/images/8888/dashing2.png
+++ /dev/null
Binary files differ
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-different-pixels/output-expected/json-summary.txt
index 179c60c..37709e1 100644
--- a/gm/tests/outputs/compared-against-different-pixels/output-expected/json-summary.txt
+++ b/gm/tests/outputs/compared-against-different-pixels/output-expected/json-summary.txt
@@ -1,16 +1,24 @@
 {
    "actual-results" : {
       "failed" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
          "8888/dashing2" : {
-            "checksum" : 2675870163990933333
+            "checksum" : FAKE
          }
       },
       "failure-ignored" : null,
+      "no-comparison" : null,
       "succeeded" : null
    },
    "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
       "8888/dashing2" : {
-         "checksums" : [ 15161495552186645995 ],
+         "checksums" : [ FAKE ],
          "ignore-failure" : false
       }
    }
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/stdout b/gm/tests/outputs/compared-against-different-pixels/output-expected/stdout
index 3833cbe..885dfd7 100644
--- a/gm/tests/outputs/compared-against-different-pixels/output-expected/stdout
+++ b/gm/tests/outputs/compared-against-different-pixels/output-expected/stdout
@@ -1,6 +1,3 @@
 reading from gm/tests/inputs/different-pixels
-writing to gm/tests/outputs/compared-against-different-pixels/output-actual/images
 drawing... dashing2 [640 480]
------  max pixel mismatch for 8888/dashing2 is 51
 Ran 1 tests: 0 passed, 1 failed, 0 missing reference images
-		8888/dashing2 pixel_error 51
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/command_line b/gm/tests/outputs/compared-against-empty-dir/output-expected/command_line
index 0ee96be..d00ec60 100644
--- a/gm/tests/outputs/compared-against-empty-dir/output-expected/command_line
+++ b/gm/tests/outputs/compared-against-empty-dir/output-expected/command_line
@@ -1 +1 @@
-out/Debug/gm --hierarchy --match dashing2 --config 8888 -r gm/tests/inputs/empty-dir --writeJsonSummary gm/tests/outputs/compared-against-empty-dir/output-actual/json-summary.txt -w gm/tests/outputs/compared-against-empty-dir/output-actual/images
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/empty-dir --writeJsonSummary gm/tests/outputs/compared-against-empty-dir/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/images/8888/dashing2.png b/gm/tests/outputs/compared-against-empty-dir/output-expected/images/8888/dashing2.png
deleted file mode 100644
index 465c019..0000000
--- a/gm/tests/outputs/compared-against-empty-dir/output-expected/images/8888/dashing2.png
+++ /dev/null
Binary files differ
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-empty-dir/output-expected/json-summary.txt
index 935ba50..6ffaf35 100644
--- a/gm/tests/outputs/compared-against-empty-dir/output-expected/json-summary.txt
+++ b/gm/tests/outputs/compared-against-empty-dir/output-expected/json-summary.txt
@@ -1,17 +1,25 @@
 {
    "actual-results" : {
       "failed" : null,
-      "failure-ignored" : {
+      "failure-ignored" : null,
+      "no-comparison" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
          "8888/dashing2" : {
-            "checksum" : 2675870163990933333
+            "checksum" : FAKE
          }
       },
       "succeeded" : null
    },
    "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : null,
+         "ignore-failure" : false
+      },
       "8888/dashing2" : {
          "checksums" : null,
-         "ignore-failure" : true
+         "ignore-failure" : false
       }
    }
 }
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/stdout b/gm/tests/outputs/compared-against-empty-dir/output-expected/stdout
index 436554d..246c05b 100644
--- a/gm/tests/outputs/compared-against-empty-dir/output-expected/stdout
+++ b/gm/tests/outputs/compared-against-empty-dir/output-expected/stdout
@@ -1,5 +1,5 @@
 reading from gm/tests/inputs/empty-dir
-writing to gm/tests/outputs/compared-against-empty-dir/output-actual/images
 drawing... dashing2 [640 480]
 FAILED to read gm/tests/inputs/empty-dir/8888/dashing2.png
+FAILED to read gm/tests/inputs/empty-dir/565/dashing2.png
 Ran 1 tests: 0 passed, 0 failed, 1 missing reference images
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/command_line b/gm/tests/outputs/compared-against-identical-bytes/output-expected/command_line
index 556498a..d065b13 100644
--- a/gm/tests/outputs/compared-against-identical-bytes/output-expected/command_line
+++ b/gm/tests/outputs/compared-against-identical-bytes/output-expected/command_line
@@ -1 +1 @@
-out/Debug/gm --hierarchy --match dashing2 --config 8888 -r gm/tests/inputs/identical-bytes --writeJsonSummary gm/tests/outputs/compared-against-identical-bytes/output-actual/json-summary.txt -w gm/tests/outputs/compared-against-identical-bytes/output-actual/images
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/identical-bytes --writeJsonSummary gm/tests/outputs/compared-against-identical-bytes/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/images/8888/dashing2.png b/gm/tests/outputs/compared-against-identical-bytes/output-expected/images/8888/dashing2.png
deleted file mode 100644
index 465c019..0000000
--- a/gm/tests/outputs/compared-against-identical-bytes/output-expected/images/8888/dashing2.png
+++ /dev/null
Binary files differ
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-identical-bytes/output-expected/json-summary.txt
index cbb28e1..bed3b2d 100644
--- a/gm/tests/outputs/compared-against-identical-bytes/output-expected/json-summary.txt
+++ b/gm/tests/outputs/compared-against-identical-bytes/output-expected/json-summary.txt
@@ -2,15 +2,23 @@
    "actual-results" : {
       "failed" : null,
       "failure-ignored" : null,
+      "no-comparison" : null,
       "succeeded" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
          "8888/dashing2" : {
-            "checksum" : 2675870163990933333
+            "checksum" : FAKE
          }
       }
    },
    "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
       "8888/dashing2" : {
-         "checksums" : [ 2675870163990933333 ],
+         "checksums" : [ FAKE ],
          "ignore-failure" : false
       }
    }
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/stdout b/gm/tests/outputs/compared-against-identical-bytes/output-expected/stdout
index 301f17f..03153e6 100644
--- a/gm/tests/outputs/compared-against-identical-bytes/output-expected/stdout
+++ b/gm/tests/outputs/compared-against-identical-bytes/output-expected/stdout
@@ -1,4 +1,3 @@
 reading from gm/tests/inputs/identical-bytes
-writing to gm/tests/outputs/compared-against-identical-bytes/output-actual/images
 drawing... dashing2 [640 480]
 Ran 1 tests: 1 passed, 0 failed, 0 missing reference images
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/command_line b/gm/tests/outputs/compared-against-identical-pixels/output-expected/command_line
index ecdef27..086f984 100644
--- a/gm/tests/outputs/compared-against-identical-pixels/output-expected/command_line
+++ b/gm/tests/outputs/compared-against-identical-pixels/output-expected/command_line
@@ -1 +1 @@
-out/Debug/gm --hierarchy --match dashing2 --config 8888 -r gm/tests/inputs/identical-pixels --writeJsonSummary gm/tests/outputs/compared-against-identical-pixels/output-actual/json-summary.txt -w gm/tests/outputs/compared-against-identical-pixels/output-actual/images
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/identical-pixels --writeJsonSummary gm/tests/outputs/compared-against-identical-pixels/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/images/8888/dashing2.png b/gm/tests/outputs/compared-against-identical-pixels/output-expected/images/8888/dashing2.png
deleted file mode 100644
index 465c019..0000000
--- a/gm/tests/outputs/compared-against-identical-pixels/output-expected/images/8888/dashing2.png
+++ /dev/null
Binary files differ
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-identical-pixels/output-expected/json-summary.txt
index cbb28e1..bed3b2d 100644
--- a/gm/tests/outputs/compared-against-identical-pixels/output-expected/json-summary.txt
+++ b/gm/tests/outputs/compared-against-identical-pixels/output-expected/json-summary.txt
@@ -2,15 +2,23 @@
    "actual-results" : {
       "failed" : null,
       "failure-ignored" : null,
+      "no-comparison" : null,
       "succeeded" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
          "8888/dashing2" : {
-            "checksum" : 2675870163990933333
+            "checksum" : FAKE
          }
       }
    },
    "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
       "8888/dashing2" : {
-         "checksums" : [ 2675870163990933333 ],
+         "checksums" : [ FAKE ],
          "ignore-failure" : false
       }
    }
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/stdout b/gm/tests/outputs/compared-against-identical-pixels/output-expected/stdout
index 00868ac..a60b4ef 100644
--- a/gm/tests/outputs/compared-against-identical-pixels/output-expected/stdout
+++ b/gm/tests/outputs/compared-against-identical-pixels/output-expected/stdout
@@ -1,4 +1,3 @@
 reading from gm/tests/inputs/identical-pixels
-writing to gm/tests/outputs/compared-against-identical-pixels/output-actual/images
 drawing... dashing2 [640 480]
 Ran 1 tests: 1 passed, 0 failed, 0 missing reference images
diff --git a/gm/tests/outputs/no-readpath/output-expected/command_line b/gm/tests/outputs/no-readpath/output-expected/command_line
new file mode 100644
index 0000000..1609465
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 --writeJsonSummary gm/tests/outputs/no-readpath/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/no-readpath/output-expected/json-summary.txt b/gm/tests/outputs/no-readpath/output-expected/json-summary.txt
new file mode 100644
index 0000000..81db3a2
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/json-summary.txt
@@ -0,0 +1,16 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
+         "8888/dashing2" : {
+            "checksum" : FAKE
+         }
+      },
+      "succeeded" : null
+   },
+   "expected-results" : null
+}
diff --git a/gm/tests/outputs/no-readpath/output-expected/return_value b/gm/tests/outputs/no-readpath/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/no-readpath/output-expected/stdout b/gm/tests/outputs/no-readpath/output-expected/stdout
new file mode 100644
index 0000000..effa9e7
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/stdout
@@ -0,0 +1,2 @@
+drawing... dashing2 [640 480]
+Ran 1 tests: 0 passed, 0 failed, 1 missing reference images
diff --git a/gm/tests/rebaseline.sh b/gm/tests/rebaseline.sh
index 77f8711..71af20f 100755
--- a/gm/tests/rebaseline.sh
+++ b/gm/tests/rebaseline.sh
@@ -5,34 +5,70 @@
 # Use with caution: are you sure the new results are actually correct?
 #
 # YOU MUST RE-RUN THIS UNTIL THE SELF-TESTS SUCCEED!
-# (It takes one run for each call to gm_test in run.sh)
+# (It takes one run for each failing call to gm_test in run.sh)
+#
+# TODO: currently, this must be run on Linux to generate baselines that match
+# the ones on the housekeeper bot (which runs on Linux... see
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1417/steps/RunGmSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
+
+function replace_expected_with_actual {
+  # Delete all the expected output files
+  EXPECTED_FILES=$(find outputs/*/output-expected -type f | grep -v /\.svn/)
+  for EXPECTED_FILE in $EXPECTED_FILES; do
+    rm $EXPECTED_FILE
+  done
+
+  # Copy all the actual output files into the "expected" directories,
+  # creating new subdirs as we go.
+  ACTUAL_FILES=$(find outputs/*/output-actual -type f | grep -v /\.svn/)
+  for ACTUAL_FILE in $ACTUAL_FILES; do
+    EXPECTED_FILE=${ACTUAL_FILE//actual/expected}
+    mkdir -p $(dirname $EXPECTED_FILE)
+    cp $ACTUAL_FILE $EXPECTED_FILE
+  done
+}
+
+function svn_add_new_files {
+  # Delete all the "actual" directories, so we can svn-add any new "expected"
+  # directories without adding the "actual" ones.
+  rm -rf outputs/*/output-actual
+  FILES=$(svn stat outputs/* | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+  FILES=$(svn stat outputs/*/output-expected | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+}
+
+function svn_delete_old_files {
+  FILES=$(svn stat outputs/*/output-expected | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+  FILES=$(svn stat outputs/* | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+}
+
 
 # cd into the gm self-test dir
 cd $(dirname $0)
 
 ./run.sh
+SELFTEST_RESULT=$?
+echo
+if [ "$SELFTEST_RESULT" != "0" ]; then
+  replace_expected_with_actual
+  echo "Self-tests still failing, you should probably run this again..."
+else
+  svn_add_new_files
+  svn_delete_old_files
+  echo "Self-tests succeeded this time, you should be done!"
+fi
+exit $SELFTEST_RESULT
 
-# Delete all the expected output files
-EXPECTED_FILES=$(find outputs/*/output-expected -type f | grep -v /\.svn/)
-for EXPECTED_FILE in $EXPECTED_FILES; do
-  rm $EXPECTED_FILE
-done
-
-# Copy all the actual output files into the "expected" directories,
-# creating new subdirs as we go.
-ACTUAL_FILES=$(find outputs/*/output-actual -type f | grep -v /\.svn/)
-for ACTUAL_FILE in $ACTUAL_FILES; do
-  EXPECTED_FILE=${ACTUAL_FILE//actual/expected}
-  mkdir -p $(dirname $EXPECTED_FILE)
-  cp $ACTUAL_FILE $EXPECTED_FILE
-done
-
-# "svn add" any newly expected files/dirs, and "svn rm" any that are gone now
-FILES=$(svn stat outputs/*/output-expected | grep ^\? | awk '{print $2}')
-for FILE in $FILES; do
-  svn add $FILE
-done
-FILES=$(svn stat outputs/*/output-expected | grep ^\! | awk '{print $2}')
-for FILE in $FILES; do
-  svn rm $FILE
-done
diff --git a/gm/tests/run.sh b/gm/tests/run.sh
index 2bb9441..717262e 100755
--- a/gm/tests/run.sh
+++ b/gm/tests/run.sh
@@ -5,8 +5,11 @@
 # These tests are run by the Skia_PerCommit_House_Keeping bot at every commit,
 # so make sure that they still pass when you make changes to gm!
 #
-# TODO: Even though these tests are passing on the Skia_PerCommit_House_Keeping
-# bot (which runs on Linux), they fail when I run them on my Mac.
+# TODO: currently, this only passes on Linux (which is the platform that
+# the housekeeper bot runs on, e.g.
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1417/steps/RunGmSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
 # Ideally, these tests should pass on all development platforms...
 # otherwise, how can developers be expected to test them before committing a
 # change?
@@ -17,6 +20,10 @@
 # TODO(epoger): make it look in Release and/or Debug
 GM_BINARY=out/Debug/gm
 
+OUTPUT_ACTUAL_SUBDIR=output-actual
+OUTPUT_EXPECTED_SUBDIR=output-expected
+CONFIGS="--config 8888 --config 565"
+
 # Compare contents of all files within directories $1 and $2,
 # EXCEPT for any dotfiles.
 # If there are any differences, a description is written to stdout and
@@ -36,44 +43,120 @@
 
 # Run gm...
 # - with the arguments in $1
-# - writing resulting images into $2/output-actual/images
-# - writing stdout into $2/output-actual/stdout
-# - writing json summary into $2/output-actual/json-summary.txt
-# - writing return value into $2/output-actual/return_value
-# Then compare all of those against $2/output-expected .
+# - writing stdout into $2/$OUTPUT_ACTUAL_SUBDIR/stdout
+# - writing json summary into $2/$OUTPUT_ACTUAL_SUBDIR/json-summary.txt
+# - writing return value into $2/$OUTPUT_ACTUAL_SUBDIR/return_value
+# Then compare all of those against $2/$OUTPUT_EXPECTED_SUBDIR .
 function gm_test {
   if [ $# != 2 ]; then
     echo "gm_test requires exactly 2 parameters, got $#"
     exit 1
   fi
   GM_ARGS="$1"
-  ACTUAL_OUTPUT_DIR="$2/output-actual"
-  EXPECTED_OUTPUT_DIR="$2/output-expected"
+  ACTUAL_OUTPUT_DIR="$2/$OUTPUT_ACTUAL_SUBDIR"
+  EXPECTED_OUTPUT_DIR="$2/$OUTPUT_EXPECTED_SUBDIR"
+  JSON_SUMMARY_FILE="$ACTUAL_OUTPUT_DIR/json-summary.txt"
 
   rm -rf $ACTUAL_OUTPUT_DIR
   mkdir -p $ACTUAL_OUTPUT_DIR
-  COMMAND="$GM_BINARY $GM_ARGS --writeJsonSummary $ACTUAL_OUTPUT_DIR/json-summary.txt -w $ACTUAL_OUTPUT_DIR/images"
+  COMMAND="$GM_BINARY $GM_ARGS --writeJsonSummary $JSON_SUMMARY_FILE"
   echo "$COMMAND" >$ACTUAL_OUTPUT_DIR/command_line
   $COMMAND &>$ACTUAL_OUTPUT_DIR/stdout
   echo $? >$ACTUAL_OUTPUT_DIR/return_value
 
+  # Only compare selected lines in the stdout, to ignore any spurious lines
+  # as noted in http://code.google.com/p/skia/issues/detail?id=1068 .
+  #
+  # TODO(epoger): This is still hacky... we need to rewrite this script in
+  # Python soon, and make stuff like this more maintainable.
+  grep --regexp=^reading --regexp=^writing --regexp=^drawing \
+    --regexp=^FAILED --regexp=^Ran $ACTUAL_OUTPUT_DIR/stdout \
+    >$ACTUAL_OUTPUT_DIR/stdout-tmp
+  mv $ACTUAL_OUTPUT_DIR/stdout-tmp $ACTUAL_OUTPUT_DIR/stdout
+
+  # Replace particular checksums in json summary with a placeholder, so
+  # we don't need to rebaseline these json files when our drawing routines
+  # change.
+  sed -e 's/"checksum" : [0-9]*/"checksum" : FAKE/g' \
+    --in-place $JSON_SUMMARY_FILE
+  sed -e 's/"checksums" : \[ [0-9]* \]/"checksums" : [ FAKE ]/g' \
+    --in-place $JSON_SUMMARY_FILE
+
   compare_directories $EXPECTED_OUTPUT_DIR $ACTUAL_OUTPUT_DIR
 }
 
+# Create input dir (at path $1) with images that match or mismatch
+# as appropriate.
+#
+# We used to check these files into SVN, but then we needed to rebasline them
+# when our drawing changed at all... so, as proposed in
+# http://code.google.com/p/skia/issues/detail?id=1068 , we generate them
+# new each time.
+function create_inputs_dir {
+  if [ $# != 1 ]; then
+    echo "create_inputs_dir requires exactly 1 parameter, got $#"
+    exit 1
+  fi
+  INPUTS_DIR="$1"
+  mkdir -p $INPUTS_DIR
+
+  mkdir -p $INPUTS_DIR/identical-bytes
+  $GM_BINARY --hierarchy --match dashing2 $CONFIGS \
+    -w $INPUTS_DIR/identical-bytes
+
+  mkdir -p $INPUTS_DIR/identical-pixels
+  $GM_BINARY --hierarchy --match dashing2 $CONFIGS \
+    -w $INPUTS_DIR/identical-pixels
+  echo "more bytes that do not change the image pixels" \
+    >> $INPUTS_DIR/identical-pixels/8888/dashing2.png
+  echo "more bytes that do not change the image pixels" \
+    >> $INPUTS_DIR/identical-pixels/565/dashing2.png
+
+  mkdir -p $INPUTS_DIR/different-pixels
+  $GM_BINARY --hierarchy --match dashing3 $CONFIGS \
+    -w $INPUTS_DIR/different-pixels
+  mv $INPUTS_DIR/different-pixels/8888/dashing3.png \
+    $INPUTS_DIR/different-pixels/8888/dashing2.png
+  mv $INPUTS_DIR/different-pixels/565/dashing3.png \
+    $INPUTS_DIR/different-pixels/565/dashing2.png
+
+  mkdir -p $INPUTS_DIR/empty-dir
+}
+
 GM_TESTDIR=gm/tests
 GM_INPUTS=$GM_TESTDIR/inputs
 GM_OUTPUTS=$GM_TESTDIR/outputs
+GM_TEMPFILES=$GM_TESTDIR/tempfiles
+
+create_inputs_dir $GM_INPUTS
 
 # Compare generated image against an input image file with identical bytes.
-gm_test "--hierarchy --match dashing2 --config 8888 -r $GM_INPUTS/identical-bytes" "$GM_OUTPUTS/compared-against-identical-bytes"
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/identical-bytes" "$GM_OUTPUTS/compared-against-identical-bytes"
 
 # Compare generated image against an input image file with identical pixels but different PNG encoding.
-gm_test "--hierarchy --match dashing2 --config 8888 -r $GM_INPUTS/identical-pixels" "$GM_OUTPUTS/compared-against-identical-pixels"
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/identical-pixels" "$GM_OUTPUTS/compared-against-identical-pixels"
 
 # Compare generated image against an input image file with different pixels.
-gm_test "--hierarchy --match dashing2 --config 8888 -r $GM_INPUTS/different-pixels" "$GM_OUTPUTS/compared-against-different-pixels"
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/different-pixels" "$GM_OUTPUTS/compared-against-different-pixels"
 
 # Compare generated image against an empty "expected image" dir.
-gm_test "--hierarchy --match dashing2 --config 8888 -r $GM_INPUTS/empty-dir" "$GM_OUTPUTS/compared-against-empty-dir"
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/empty-dir" "$GM_OUTPUTS/compared-against-empty-dir"
+
+# If run without "-r", the JSON's "actual-results" section should contain
+# actual checksums marked as "failure-ignored", but the "expected-results"
+# section should be empty.
+gm_test "--hierarchy --match dashing2 $CONFIGS" "$GM_OUTPUTS/no-readpath"
+
+# Run a test which generates partially transparent images, write out those
+# images, and read them back in.
+#
+# This test would have caught
+# http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating
+# spurious pixel_error messages as of r7258').
+IMAGEDIR=$GM_TEMPFILES/aaclip-images
+rm -rf $IMAGEDIR
+mkdir -p $IMAGEDIR
+gm_test "--match simpleaaclip_path $CONFIGS -w $IMAGEDIR" "$GM_OUTPUTS/aaclip-write"
+gm_test "--match simpleaaclip_path $CONFIGS -r $IMAGEDIR" "$GM_OUTPUTS/aaclip-readback"
 
 echo "All tests passed."
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index e36feef..da52aad 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -12,7 +12,7 @@
 
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
-#include "effects/GrSingleTextureEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
 #include "SkColorPriv.h"
 #include "SkDevice.h"
 
@@ -113,8 +113,7 @@
                 SkMatrix tm;
                 tm = vm;
                 tm.postIDiv(2*S, 2*S);
-                paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect,
-                                                          (texture, tm)))->unref();
+                paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(texture, tm))->unref();
 
                 ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
 
diff --git a/gm/texteffects.cpp b/gm/texteffects.cpp
index 3436018..782850e 100644
--- a/gm/texteffects.cpp
+++ b/gm/texteffects.cpp
@@ -215,4 +215,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new TextEffectsGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
diff --git a/gm/tilemodes.cpp b/gm/tilemodes.cpp
index d51fce2..b0594e4 100644
--- a/gm/tilemodes.cpp
+++ b/gm/tilemodes.cpp
@@ -174,14 +174,6 @@
     return NULL;
 }
 
-static SkShader* make_radial(SkShader::TileMode tx, SkShader::TileMode ty) {
-    SkPoint center = { SkIntToScalar(gWidth)/2, SkIntToScalar(gHeight)/2 };
-    SkScalar rad = SkIntToScalar(gWidth)/2;
-    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
-
-    return SkGradientShader::CreateRadial(center, rad, colors, NULL, SK_ARRAY_COUNT(colors), tx);
-}
-
 typedef SkShader* (*ShaderProc)(SkShader::TileMode, SkShader::TileMode);
 
 class Tiling2GM : public skiagm::GM {
@@ -265,5 +257,3 @@
 
 static skiagm::GM* MyFactory3(void*) { return new Tiling2GM(make_grad, "gradient"); }
 static skiagm::GMRegistry reg3(MyFactory3);
-
-
diff --git a/gm/twopointradial.cpp b/gm/twopointradial.cpp
index 40712e8..a10f5af 100644
--- a/gm/twopointradial.cpp
+++ b/gm/twopointradial.cpp
@@ -109,4 +109,3 @@
 static skiagm::GM* F(void*) { return new TwoPointRadialGM; }
 
 static skiagm::GMRegistry gR(F);
-
diff --git a/gm/xfermodes.cpp b/gm/xfermodes.cpp
index 8934bc7..9eb2f87 100644
--- a/gm/xfermodes.cpp
+++ b/gm/xfermodes.cpp
@@ -43,10 +43,11 @@
     }
 }
 
+static uint16_t gData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
 class XfermodesGM : public GM {
     SkBitmap    fBG;
     SkBitmap    fSrcB, fDstB;
-    bool        fOnce;
 
     void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
                    SkScalar x, SkScalar y) {
@@ -58,25 +59,18 @@
         canvas->drawBitmap(fDstB, x, y, &p);
     }
 
-    void init() {
-        if (!fOnce) {
-            // Do all this work in a temporary so we get a deep copy
-            uint16_t localData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
-            SkBitmap scratchBitmap;
-            scratchBitmap.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
-            scratchBitmap.setPixels(localData);
-            scratchBitmap.setIsOpaque(true);
-            scratchBitmap.copyTo(&fBG, SkBitmap::kARGB_4444_Config);
+    virtual void onOnceBeforeDraw() SK_OVERRIDE {
+        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+        fBG.setPixels(gData);
+        fBG.setIsOpaque(true);
 
-            make_bitmaps(W, H, &fSrcB, &fDstB);
-            fOnce = true;
-        }
+        make_bitmaps(W, H, &fSrcB, &fDstB);
     }
 
 public:
     const static int W = 64;
     const static int H = 64;
-    XfermodesGM() : fOnce(false) {}
+    XfermodesGM() {}
 
 protected:
     virtual SkString onShortName() {
@@ -88,8 +82,6 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        this->init();
-
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         const struct {
@@ -110,7 +102,7 @@
             { SkXfermode::kXor_Mode,      "Xor"       },
 
             { SkXfermode::kPlus_Mode,         "Plus"          },
-            { SkXfermode::kMultiply_Mode,     "Multiply"      },
+            { SkXfermode::kModulate_Mode,     "Modulate"      },
             { SkXfermode::kScreen_Mode,       "Screen"        },
             { SkXfermode::kOverlay_Mode,      "Overlay"       },
             { SkXfermode::kDarken_Mode,       "Darken"        },
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index 715ef3b..8d833dd 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -5,7 +5,8 @@
       'type': 'executable',
       'mac_bundle' : 1,
       'include_dirs' : [
-        '../src/core', # needed to get SkConcaveToTriangle, maybe this should be moved to include dir?
+        '../src/core',
+        '../src/effects', #needed for BlurMask.h
         '../gm',       # needed to pull gm.h
         '../samplecode', # To pull SampleApp.h and SampleCode.h
         '../src/pipe/utils', # For TiledPipeController
@@ -66,6 +67,7 @@
         '../samplecode/SampleLayers.cpp',
         '../samplecode/SampleLCD.cpp',
         '../samplecode/SampleLines.cpp',
+        '../samplecode/SampleManyRects.cpp',
         '../samplecode/SampleMeasure.cpp',
         '../samplecode/SampleMipMap.cpp',
         '../samplecode/SampleMovie.cpp',
@@ -81,6 +83,7 @@
         '../samplecode/SamplePolyToPoly.cpp',
         '../samplecode/SampleRegion.cpp',
         '../samplecode/SampleRepeatTile.cpp',
+        '../samplecode/SampleRotateCircles.cpp',
         '../samplecode/SampleShaders.cpp',
         '../samplecode/SampleShaderText.cpp',
         '../samplecode/SampleSkLayer.cpp',
@@ -95,7 +98,6 @@
         '../samplecode/SampleTextureDomain.cpp',
         '../samplecode/SampleTiling.cpp',
         '../samplecode/SampleTinyBitmap.cpp',
-        '../samplecode/SampleTriangles.cpp',
         '../samplecode/SampleTypeface.cpp',
         '../samplecode/SampleUnitMapper.cpp',
         '../samplecode/SampleVertices.cpp',
@@ -116,14 +118,6 @@
         #'../experimental/Networking/SkSockets.cpp',
         #'../experimental/Networking/SkSockets.h',
 
-        # Debugger
-        '../experimental/Debugger/DebuggerViews.h',
-        '../experimental/Debugger/DebuggerContentView.cpp',
-        '../experimental/Debugger/DebuggerCommandsView.cpp',
-        '../experimental/Debugger/DebuggerStateView.cpp',
-        '../experimental/Debugger/SkDebugDumper.cpp',
-        '../experimental/Debugger/SkDebugDumper.h',
-
         # TiledPipeController
         '../src/pipe/utils/SamplePipeControllers.h',
         '../src/pipe/utils/SamplePipeControllers.cpp',
diff --git a/gyp/angle.gyp b/gyp/angle.gyp
index 70ab5c2..088895b 100644
--- a/gyp/angle.gyp
+++ b/gyp/angle.gyp
@@ -10,6 +10,7 @@
       },
       'variables': {
         'component': 'static_library',
+            'skia_building_angle': 1, # See comment in common_conditions.gypi.
       },
       'includes': [
         '../third_party/externals/angle/src/build_angle.gypi',
diff --git a/gyp/bench.gyp b/gyp/bench.gyp
index 1524b72..be768f0 100644
--- a/gyp/bench.gyp
+++ b/gyp/bench.gyp
@@ -10,6 +10,8 @@
       'type': 'executable',
       'include_dirs' : [
         '../src/core',
+        '../src/effects',
+        '../src/utils',
       ],
       'includes': [
         'bench.gypi'
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index dbb30b6..cf5c02e 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -10,6 +10,7 @@
     '../bench/BitmapBench.cpp',
     '../bench/BitmapRectBench.cpp',
     '../bench/BlurBench.cpp',
+    '../bench/BlurRectBench.cpp',
     '../bench/ChecksumBench.cpp',
     '../bench/ChromeBench.cpp',
     '../bench/DashBench.cpp',
@@ -35,12 +36,15 @@
     '../bench/RectBench.cpp',
     '../bench/RefCntBench.cpp',
     '../bench/RegionBench.cpp',
+    '../bench/RegionContainBench.cpp',
     '../bench/RepeatTileBench.cpp',
     '../bench/RTreeBench.cpp',
     '../bench/ScalarBench.cpp',
     '../bench/ShaderMaskBench.cpp',
+    '../bench/SortBench.cpp',
     '../bench/TableBench.cpp',
     '../bench/TextBench.cpp',
+    '../bench/TileBench.cpp',
     '../bench/VertBench.cpp',
     '../bench/WriterBench.cpp',
 
diff --git a/gyp/common.gypi b/gyp/common.gypi
index 7dc27da..810a0f8 100644
--- a/gyp/common.gypi
+++ b/gyp/common.gypi
@@ -82,6 +82,7 @@
         'defines': [
           'SK_DEBUG',
           'GR_DEBUG=1',
+          'SK_DEVELOPER=1',
         ],
       },
       'Release': {
@@ -90,6 +91,12 @@
           'GR_RELEASE=1',
         ],
       },
+      'Release_Developer': {
+        'inherit_from': ['Release'],
+        'defines': [
+          'SK_DEVELOPER=1',
+        ],
+      },
     },
   }, # end 'target_defaults'
 }
diff --git a/gyp/common_conditions.gypi b/gyp/common_conditions.gypi
index 332d342..d61cc94 100644
--- a/gyp/common_conditions.gypi
+++ b/gyp/common_conditions.gypi
@@ -4,7 +4,6 @@
   'defines': [
     'SK_ALLOW_STATIC_GLOBAL_INITIALIZERS=<(skia_static_initializers)',
 #    'SK_SUPPORT_HINTING_SCALE_FACTOR',
-     'SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX',
   ],
   'conditions' : [
     ['skia_gpu == 1',
@@ -30,7 +29,6 @@
         'msvs_settings': {
           'VCCLCompilerTool': {
             'WarningLevel': '1',
-            'WarnAsError': 'false',
             'DebugInformationFormat': '3',
             'AdditionalOptions': [ '/MP' ],
           },
@@ -89,10 +87,38 @@
         },
         'conditions' : [
           ['skia_arch_width == 64', {
-            'msvs_configuration_platform': 'x64'
+            'msvs_configuration_platform': 'x64',
+            'msvs_settings': {
+              'VCCLCompilerTool': {
+                'WarnAsError': 'false',
+              },
+            },
           }],
           ['skia_arch_width == 32', {
-            'msvs_configuration_platform': 'Win32',
+            # This gypi file will be included directly into the gyp(i) files in the angle repo by
+            # our gyp_skia script. We don't want force WarnAsError on angle. So angle.gyp defines
+            # skia_building_angle=1 and here we select whether to enable WarnAsError based on that
+            # var's value. Here it defaults to 0.
+            'variables' : {
+              'skia_building_angle%': 0,
+            },
+            'conditions' : [
+              ['skia_building_angle', {
+                'msvs_configuration_platform': 'Win32',
+                'msvs_settings': {
+                  'VCCLCompilerTool': {
+                    'WarnAsError': 'false',
+                  },
+                },
+              },{ # not angle
+                'msvs_configuration_platform': 'Win32',
+                'msvs_settings': {
+                  'VCCLCompilerTool': {
+                    'WarnAsError': 'true',
+                  },
+                },
+              }],
+            ],
           }],
         ],
       },
@@ -181,6 +207,9 @@
           ['skia_arch_width == 32', {
             'xcode_settings': {
               'ARCHS': 'i386',
+              'OTHER_CPLUSPLUSFLAGS': [
+                '-Werror',
+              ],
             },
           }],
         ],
@@ -285,14 +314,6 @@
           '-fno-exceptions',
           '-fno-rtti',
           '-fuse-ld=gold',
-          '--sysroot=<(android_base)/toolchains/<(android_toolchain)/sysroot',
-        ],
-        'include_dirs' : [
-          '<(android_base)/toolchains/<(android_toolchain)/lib/gcc/arm-linux-androideabi/4.6.x-google/include',
-          '<(android_base)/toolchains/<(android_toolchain)/lib/gcc/arm-linux-androideabi/4.6.x-google/include-fixed',
-          '<(android_base)/toolchains/<(android_toolchain)/arm-linux-androideabi/include/c++/4.6',
-          '<(android_base)/toolchains/<(android_toolchain)/arm-linux-androideabi/include/c++/4.6/arm-linux-androideabi',
-          '<(android_base)/toolchains/<(android_toolchain)/sysroot/usr/include',
         ],
         'conditions': [
           [ 'skia_warnings_as_errors == 1', {
@@ -303,11 +324,6 @@
           [ 'skia_profile_enabled == 1', {
             'cflags': ['-g', '-fno-omit-frame-pointer', '-marm', '-mapcs'],
           }],
-          [ 'skia_arch_type == "arm"', {
-            'ldflags': [
-              '-Wl',
-            ],
-          }],
           [ 'skia_arch_type == "arm" and arm_thumb == 1', {
             'cflags': [
               '-mthumb',
@@ -332,6 +348,10 @@
                 'cflags': [
                   '-mfpu=neon',
                 ],
+                'ldflags': [
+                  '-march=armv7-a',
+                  '-Wl,--fix-cortex-a8',
+                ],
               }],
               [ 'arm_neon_optional == 1', {
                 'defines': [
diff --git a/gyp/common_variables.gypi b/gyp/common_variables.gypi
index 042340b..5aca19c 100644
--- a/gyp/common_variables.gypi
+++ b/gyp/common_variables.gypi
@@ -81,6 +81,8 @@
       'skia_scalar%': 'float',
       'skia_mesa%': 0,
       'skia_nv_path_rendering%': 0,
+      'skia_stroke_path_rendering%': 0,
+      'skia_android_path_rendering%': 0,
       'skia_texture_cache_mb_limit%': 0,
       'skia_angle%': 0,
       'skia_directwrite%': 0,
@@ -101,6 +103,8 @@
     'skia_scalar%': '<(skia_scalar)',
     'skia_mesa%': '<(skia_mesa)',
     'skia_nv_path_rendering%': '<(skia_nv_path_rendering)',
+    'skia_stroke_path_rendering%': '<(skia_stroke_path_rendering)',
+    'skia_android_path_rendering%': '<(skia_android_path_rendering)',
     'skia_texture_cache_mb_limit%': '<(skia_texture_cache_mb_limit)',
     'skia_angle%': '<(skia_angle)',
     'skia_arch_width%': '<(skia_arch_width)',
diff --git a/gyp/core.gyp b/gyp/core.gyp
index 14b14b0..7e7640c 100644
--- a/gyp/core.gyp
+++ b/gyp/core.gyp
@@ -17,6 +17,7 @@
         '../include/core',
         '../include/pipe',
         '../include/ports',
+        '../include/utils',
         '../include/xml',
         '../src/core',
         '../src/image',
@@ -93,6 +94,12 @@
             '../src/core/SkUtilsArm.h',
           ],
         }],
+        ['skia_gpu == 1', {
+          'include_dirs': [
+              '../include/gpu',
+              '../src/gpu',
+          ],
+        }],
       ],
       'direct_dependent_settings': {
         'include_dirs': [
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 4830645..d5f76a3 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -57,8 +57,6 @@
         '<(skia_src_path)/core/SkColorFilter.cpp',
         '<(skia_src_path)/core/SkColorTable.cpp',
         '<(skia_src_path)/core/SkComposeShader.cpp',
-        '<(skia_src_path)/core/SkConcaveToTriangles.cpp',
-        '<(skia_src_path)/core/SkConcaveToTriangles.h',
         '<(skia_src_path)/core/SkConfig8888.cpp',
         '<(skia_src_path)/core/SkConfig8888.h',
         '<(skia_src_path)/core/SkCordic.cpp',
@@ -87,6 +85,8 @@
         '<(skia_src_path)/core/SkFloat.h',
         '<(skia_src_path)/core/SkFloatBits.cpp',
         '<(skia_src_path)/core/SkFontHost.cpp',
+        '<(skia_src_path)/core/SkFontDescriptor.cpp',
+        '<(skia_src_path)/core/SkFontDescriptor.h',
         '<(skia_src_path)/core/SkGeometry.cpp',
         '<(skia_src_path)/core/SkGlyphCache.cpp',
         '<(skia_src_path)/core/SkGlyphCache.h',
@@ -107,6 +107,8 @@
         '<(skia_src_path)/core/SkOrderedWriteBuffer.cpp',
         '<(skia_src_path)/core/SkPackBits.cpp',
         '<(skia_src_path)/core/SkPaint.cpp',
+        '<(skia_src_path)/core/SkPaintPriv.cpp',
+        '<(skia_src_path)/core/SkPaintPriv.h',
         '<(skia_src_path)/core/SkPath.cpp',
         '<(skia_src_path)/core/SkPathEffect.cpp',
         '<(skia_src_path)/core/SkPathHeap.cpp',
@@ -141,6 +143,7 @@
         '<(skia_src_path)/core/SkRTree.cpp',
         '<(skia_src_path)/core/SkScalar.cpp',
         '<(skia_src_path)/core/SkScalerContext.cpp',
+        '<(skia_src_path)/core/SkScalerContext.h',
         '<(skia_src_path)/core/SkScan.cpp',
         '<(skia_src_path)/core/SkScan.h',
         '<(skia_src_path)/core/SkScanPriv.h',
@@ -156,6 +159,7 @@
         '<(skia_src_path)/core/SkSpriteBlitterTemplate.h',
         '<(skia_src_path)/core/SkStream.cpp',
         '<(skia_src_path)/core/SkString.cpp',
+        '<(skia_src_path)/core/SkStringUtils.cpp',
         '<(skia_src_path)/core/SkStroke.h',
         '<(skia_src_path)/core/SkStroke.cpp',
         '<(skia_src_path)/core/SkStrokeRec.cpp',
@@ -210,6 +214,7 @@
         '<(skia_include_path)/core/SkData.h',
         '<(skia_include_path)/core/SkDeque.h',
         '<(skia_include_path)/core/SkDevice.h',
+        '<(skia_include_path)/core/SkDeviceProperties.h',
         '<(skia_include_path)/core/SkDither.h',
         '<(skia_include_path)/core/SkDraw.h',
         '<(skia_include_path)/core/SkDrawFilter.h',
@@ -240,7 +245,6 @@
         '<(skia_include_path)/core/SkPicture.h',
         '<(skia_include_path)/core/SkPixelRef.h',
         '<(skia_include_path)/core/SkPoint.h',
-        '<(skia_include_path)/core/SkRandom.h',
         '<(skia_include_path)/core/SkRasterizer.h',
         '<(skia_include_path)/core/SkReader32.h',
         '<(skia_include_path)/core/SkRect.h',
@@ -252,6 +256,7 @@
         '<(skia_include_path)/core/SkShader.h',
         '<(skia_include_path)/core/SkStream.h',
         '<(skia_include_path)/core/SkString.h',
+        '<(skia_include_path)/core/SkStringUtils.h',
         '<(skia_include_path)/core/SkStrokeRec.h',
         '<(skia_include_path)/core/SkTArray.h',
         '<(skia_include_path)/core/SkTDArray.h',
diff --git a/gyp/debugger.gyp b/gyp/debugger.gyp
index 5c760db..40ce090 100644
--- a/gyp/debugger.gyp
+++ b/gyp/debugger.gyp
@@ -81,6 +81,7 @@
         '../debugger',      # To pull SkDebugger.h
         '../debugger/QT',   # For all the QT UI Goodies
         '../src/gpu',       # To pull gl/GrGLUtil.h
+        '../src/ports',     # To pull SkFontDescriptor.h
         '../bench',
         '../tools',
         '<@(qt_includes)',
diff --git a/gyp/effects.gypi b/gyp/effects.gypi
index d981cd1..44d846a 100644
--- a/gyp/effects.gypi
+++ b/gyp/effects.gypi
@@ -11,6 +11,7 @@
     '<(skia_src_path)/effects/Sk2DPathEffect.cpp',
     '<(skia_src_path)/effects/SkAvoidXfermode.cpp',
     '<(skia_src_path)/effects/SkArithmeticMode.cpp',
+    '<(skia_src_path)/effects/SkBicubicImageFilter.cpp',
     '<(skia_src_path)/effects/SkBitmapSource.cpp',
     '<(skia_src_path)/effects/SkBlendImageFilter.cpp',
     '<(skia_src_path)/effects/SkBlurDrawLooper.cpp',
@@ -25,10 +26,12 @@
     '<(skia_src_path)/effects/SkCornerPathEffect.cpp',
     '<(skia_src_path)/effects/SkDashPathEffect.cpp',
     '<(skia_src_path)/effects/SkDiscretePathEffect.cpp',
+    '<(skia_src_path)/effects/SkDisplacementMapEffect.cpp',
     '<(skia_src_path)/effects/SkEmbossMask.cpp',
     '<(skia_src_path)/effects/SkEmbossMask.h',
     '<(skia_src_path)/effects/SkEmbossMask_Table.h',
     '<(skia_src_path)/effects/SkEmbossMaskFilter.cpp',
+    '<(skia_src_path)/effects/SkImageFilterUtils.cpp',
     '<(skia_src_path)/effects/SkKernel33MaskFilter.cpp',
     '<(skia_src_path)/effects/SkLayerDrawLooper.cpp',
     '<(skia_src_path)/effects/SkLayerRasterizer.cpp',
@@ -81,9 +84,11 @@
     '<(skia_include_path)/effects/SkCornerPathEffect.h',
     '<(skia_include_path)/effects/SkDashPathEffect.h',
     '<(skia_include_path)/effects/SkDiscretePathEffect.h',
+    '<(skia_include_path)/effects/SkDisplacementMapEffect.h',
     '<(skia_include_path)/effects/SkDrawExtraPathEffect.h',
     '<(skia_include_path)/effects/SkEmbossMaskFilter.h',
     '<(skia_include_path)/effects/SkGradientShader.h',
+    '<(skia_include_path)/effects/SkImageFilterUtils.h',
     '<(skia_include_path)/effects/SkKernel33MaskFilter.h',
     '<(skia_include_path)/effects/SkLayerDrawLooper.h',
     '<(skia_include_path)/effects/SkLayerRasterizer.h',
diff --git a/gyp/experimental.gyp b/gyp/experimental.gyp
index 09d5944..09711ac 100644
--- a/gyp/experimental.gyp
+++ b/gyp/experimental.gyp
@@ -6,13 +6,11 @@
       'include_dirs': [
         '../include/config',
         '../include/core',
-        '../experimental/AndroidPathRenderer',
       ],
       'sources': [
         '../experimental/SkSetPoly3To3.cpp',
         '../experimental/SkSetPoly3To3_A.cpp',
         '../experimental/SkSetPoly3To3_D.cpp',
-        '../experimental/AndroidPathRenderer/AndroidPathRenderer.cpp',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index 4f8637f..4b2b1c4 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -9,6 +9,7 @@
       'type': 'executable',
       'include_dirs' : [
         '../src/core',
+        '../src/effects',
         '../src/pipe/utils/',
         '../src/utils/',
       ],
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 1553e74..8d772f3 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -4,6 +4,7 @@
     '../gm/aaclip.cpp',
     '../gm/aarectmodes.cpp',
     '../gm/arithmode.cpp',
+    '../gm/bicubicfilter.cpp',
     '../gm/bigmatrix.cpp',
     '../gm/bitmapcopy.cpp',
     '../gm/bitmapmatrix.cpp',
@@ -26,6 +27,7 @@
     '../gm/dashcubics.cpp',
     '../gm/dashing.cpp',
     '../gm/distantclip.cpp',
+    '../gm/displacement.cpp',
     '../gm/drawbitmaprect.cpp',
     '../gm/drawlooper.cpp',
     '../gm/extractbitmap.cpp',
@@ -71,6 +73,7 @@
     '../gm/shadertext3.cpp',
     '../gm/shadows.cpp',
     '../gm/simpleaaclip.cpp',
+    '../gm/spritebitmap.cpp',
     '../gm/srcmode.cpp',
     '../gm/strokefill.cpp',
     '../gm/strokerect.cpp',
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index d5223e4..6c599e1 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -127,6 +127,7 @@
       'include_dirs': [
         '../include/config',
         '../include/core',
+        '../include/utils',
         '../src/core',
         '../include/gpu',
         '../src/gpu',
@@ -177,6 +178,7 @@
      'include_dirs': [
         '../include/core',
         '../include/config',
+        '../include/utils',
         '../include/gpu',
         '../src/core', # SkRasterClip.h
         '../src/gpu'
@@ -205,6 +207,27 @@
             'GR_GL_USE_NV_PATH_RENDERING=1',
           ],
         }],
+        [ 'skia_stroke_path_rendering', {
+          'sources': [
+            '../experimental/StrokePathRenderer/GrStrokePathRenderer.h',
+            '../experimental/StrokePathRenderer/GrStrokePathRenderer.cpp',
+          ],
+          'defines': [
+            'GR_STROKE_PATH_RENDERING=1',
+          ],
+        }],
+        [ 'skia_android_path_rendering', {
+          'sources': [
+            '../experimental/AndroidPathRenderer/GrAndroidPathRenderer.cpp',
+            '../experimental/AndroidPathRenderer/GrAndroidPathRenderer.h',
+            '../experimental/AndroidPathRenderer/AndroidPathRenderer.cpp',
+            '../experimental/AndroidPathRenderer/AndroidPathRenderer.h',
+            '../experimental/AndroidPathRenderer/Vertex.h',
+          ],
+          'defines': [
+            'GR_ANDROID_PATH_RENDERING=1',
+          ],
+        }],
         [ 'skia_os == "linux"', {
           'sources!': [
             '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 7d3c0f2..bf301bb 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -10,7 +10,6 @@
     'gr_sources': [
       '<(skia_include_path)/gpu/GrAARectRenderer.h',
       '<(skia_include_path)/gpu/GrBackendEffectFactory.h',
-      '<(skia_include_path)/gpu/GrCacheID.h',
       '<(skia_include_path)/gpu/GrClipData.h',
       '<(skia_include_path)/gpu/GrColor.h',
       '<(skia_include_path)/gpu/GrConfig.h',
@@ -21,7 +20,6 @@
       '<(skia_include_path)/gpu/GrEffectUnitTest.h',
       '<(skia_include_path)/gpu/GrFontScaler.h',
       '<(skia_include_path)/gpu/GrGlyph.h',
-      '<(skia_include_path)/gpu/GrInstanceCounter.h',
       '<(skia_include_path)/gpu/GrKey.h',
       '<(skia_include_path)/gpu/GrNoncopyable.h',
       '<(skia_include_path)/gpu/GrPaint.h',
@@ -107,7 +105,6 @@
       '<(skia_src_path)/gpu/GrStencilBuffer.cpp',
       '<(skia_src_path)/gpu/GrStencilBuffer.h',
       '<(skia_src_path)/gpu/GrTBSearch.h',
-      '<(skia_src_path)/gpu/GrTDArray.h',
       '<(skia_src_path)/gpu/GrSWMaskHelper.cpp',
       '<(skia_src_path)/gpu/GrSWMaskHelper.h',
       '<(skia_src_path)/gpu/GrSoftwarePathRenderer.cpp',
@@ -131,6 +128,8 @@
       '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.h',
       '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.h',
       '<(skia_src_path)/gpu/effects/GrTextureDomainEffect.cpp',
diff --git a/gyp/images.gyp b/gyp/images.gyp
index 1bfd2d3..8c4690e 100644
--- a/gyp/images.gyp
+++ b/gyp/images.gyp
@@ -44,6 +44,8 @@
         '../src/images/SkImageRef.cpp',
         '../src/images/SkImageRefPool.cpp',
         '../src/images/SkImageRefPool.h',
+        '../src/images/SkImageRef_ashmem.h',
+        '../src/images/SkImageRef_ashmem.cpp',
         '../src/images/SkImageRef_GlobalPool.cpp',
         '../src/images/SkImages.cpp',
         '../src/images/SkJpegUtility.cpp',
@@ -109,7 +111,8 @@
           # end libpng stuff
         }],
         [ 'skia_os == "android"', {
-          'sources!': [
+          'include_dirs': [
+             '../src/utils',
           ],
           'dependencies': [
              'android_deps.gyp:gif',
@@ -118,6 +121,11 @@
           'defines': [
             'SK_ENABLE_LIBPNG',
           ],
+        },{ #else if skia_os != android
+          'sources!': [
+            '../src/images/SkImageRef_ashmem.h',
+            '../src/images/SkImageRef_ashmem.cpp',
+          ],
         }],
         [ 'skia_os == "ios"', {
            'include_dirs': [
diff --git a/gyp/jsoncpp.gyp b/gyp/jsoncpp.gyp
index 71815a7..1ee33fd 100644
--- a/gyp/jsoncpp.gyp
+++ b/gyp/jsoncpp.gyp
@@ -3,12 +3,13 @@
 # found in the LICENSE file.
 
 # TODO: This file was copied from the external dependency
-# third_party/externals/jsoncpp/jsoncpp.gyp , at revision 125399,
+# third_party/externals/jsoncpp-chromium/jsoncpp.gyp , at revision 125399,
 # with directory paths modified to work at this level.
 #
 # It would be better for us to depend on that gypfile within the external
 # dependency, but so far we have been unable to make that work reliably.
 # See https://code.google.com/p/skia/issues/detail?id=1023
+# and https://code.google.com/p/skia/source/detail?r=7115
 
 {
   'targets': [
@@ -19,32 +20,48 @@
         'JSON_USE_EXCEPTION=0',
       ],
       'sources': [
-        '../third_party/externals/jsoncpp/source/include/json/assertions.h',
-        '../third_party/externals/jsoncpp/source/include/json/autolink.h',
-        '../third_party/externals/jsoncpp/source/include/json/config.h',
-        '../third_party/externals/jsoncpp/source/include/json/features.h',
-        '../third_party/externals/jsoncpp/source/include/json/forwards.h',
-        '../third_party/externals/jsoncpp/source/include/json/json.h',
-        '../third_party/externals/jsoncpp/source/include/json/reader.h',
-        '../third_party/externals/jsoncpp/overrides/include/json/value.h',
-        '../third_party/externals/jsoncpp/source/include/json/writer.h',
-        '../third_party/externals/jsoncpp/source/src/lib_json/json_batchallocator.h',
-        '../third_party/externals/jsoncpp/source/src/lib_json/json_reader.cpp',
-        '../third_party/externals/jsoncpp/source/src/lib_json/json_tool.h',
-        '../third_party/externals/jsoncpp/overrides/src/lib_json/json_value.cpp',
-        '../third_party/externals/jsoncpp/source/src/lib_json/json_writer.cpp',
+        '../third_party/externals/jsoncpp/include/json/assertions.h',
+        '../third_party/externals/jsoncpp/include/json/autolink.h',
+        '../third_party/externals/jsoncpp/include/json/config.h',
+        '../third_party/externals/jsoncpp/include/json/features.h',
+        '../third_party/externals/jsoncpp/include/json/forwards.h',
+        '../third_party/externals/jsoncpp/include/json/json.h',
+        '../third_party/externals/jsoncpp/include/json/reader.h',
+        '../third_party/externals/jsoncpp-chromium/overrides/include/json/value.h',
+        '../third_party/externals/jsoncpp/include/json/writer.h',
+        '../third_party/externals/jsoncpp/src/lib_json/json_batchallocator.h',
+        '../third_party/externals/jsoncpp/src/lib_json/json_reader.cpp',
+        '../third_party/externals/jsoncpp/src/lib_json/json_tool.h',
+        '../third_party/externals/jsoncpp-chromium/overrides/src/lib_json/json_value.cpp',
+        '../third_party/externals/jsoncpp/src/lib_json/json_writer.cpp',
       ],
       'include_dirs': [
-        '../third_party/externals/jsoncpp/overrides/include/',
-        '../third_party/externals/jsoncpp/source/include/',
-        '../third_party/externals/jsoncpp/source/src/lib_json/',
+        '../third_party/externals/jsoncpp-chromium/overrides/include/',
+        '../third_party/externals/jsoncpp/include/',
+        '../third_party/externals/jsoncpp/src/lib_json/',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
-          '../third_party/externals/jsoncpp/overrides/include/',
-          '../third_party/externals/jsoncpp/source/include/',
+          '../third_party/externals/jsoncpp-chromium/overrides/include/',
+          '../third_party/externals/jsoncpp/include/',
         ],
       },
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'xcode_settings': {
+            'OTHER_CPLUSPLUSFLAGS!': [
+              '-Werror',
+            ]
+          },
+        }],
+        [ 'skia_os == "win"', {
+          'msvs_settings': {
+            'VCCLCompilerTool': {
+              'WarnAsError': 'false',
+            },
+          },
+        }],
+      ],
     },
   ],
 }
diff --git a/gyp/opts.gyp b/gyp/opts.gyp
index de27b42..30f464f 100644
--- a/gyp/opts.gyp
+++ b/gyp/opts.gyp
@@ -37,6 +37,11 @@
                 '-msse2',
               ],
             }],
+            [ 'skia_os != "android"', {
+              'dependencies': [
+                'opts_ssse3',
+              ],
+            }],
           ],
           'sources': [
             '../src/opts/opts_check_SSE2.cpp',
@@ -45,9 +50,6 @@
             '../src/opts/SkBlitRect_opts_SSE2.cpp',
             '../src/opts/SkUtils_opts_SSE2.cpp',
           ],
-          'dependencies': [
-            'opts_ssse3',
-          ],
         }],
         [ 'skia_arch_type == "arm" and armv7 == 1', {
           # The assembly uses the frame pointer register (r7 in Thumb/r11 in
@@ -156,6 +158,10 @@
         '-mfpu=neon',
         '-fomit-frame-pointer',
       ],
+      'ldflags': [
+        '-march=armv7-a',
+        '-Wl,--fix-cortex-a8',
+      ],
       'sources': [
         '../src/opts/memset16_neon.S',
         '../src/opts/memset32_neon.S',
diff --git a/gyp/ports.gyp b/gyp/ports.gyp
index a84f788..219bf3e 100644
--- a/gyp/ports.gyp
+++ b/gyp/ports.gyp
@@ -23,8 +23,6 @@
         '../src/ports/SkDebug_nacl.cpp',
         '../src/ports/SkDebug_stdio.cpp',
         '../src/ports/SkDebug_win.cpp',
-        '../src/ports/SkFontDescriptor.h',
-        '../src/ports/SkFontDescriptor.cpp',
         '../src/ports/SkFontHost_sandbox_none.cpp',
         '../src/ports/SkFontHost_win.cpp',
         '../src/ports/SkFontHost_win_dw.cpp',
@@ -163,7 +161,6 @@
             '../src/ports/SkFontHost_FreeType.cpp',
             '../src/ports/SkFontHost_FreeType_common.cpp',
             '../src/ports/FontHostConfiguration_android.cpp',
-            '../src/ports/SkImageRef_ashmem.cpp',
           ],
           'dependencies': [
              'freetype.gyp:freetype',
diff --git a/gyp/shapeops_demo.gyp b/gyp/shapeops_demo.gyp
index 1279294..6a06f86 100644
--- a/gyp/shapeops_demo.gyp
+++ b/gyp/shapeops_demo.gyp
@@ -19,6 +19,7 @@
         '../experimental/Intersection/CubicIntersection.cpp',
         '../experimental/Intersection/CubicReduceOrder.cpp',
         '../experimental/Intersection/CubicSubDivide.cpp',
+        '../experimental/Intersection/CubicToQuadratics.cpp',
         '../experimental/Intersection/CubicUtilities.cpp',
         '../experimental/Intersection/DataTypes.cpp',
         '../experimental/Intersection/EdgeDemo.cpp',
@@ -26,6 +27,7 @@
         '../experimental/Intersection/EdgeWalker.cpp',
         '../experimental/Intersection/EdgeWalker_TestUtility.cpp',
         '../experimental/Intersection/Extrema.cpp',
+        '../experimental/Intersection/Intersections.cpp',
         '../experimental/Intersection/LineCubicIntersection.cpp',
         '../experimental/Intersection/LineIntersection.cpp',
         '../experimental/Intersection/LineParameterization.cpp',
@@ -69,11 +71,7 @@
         'effects.gyp:effects',
         'images.gyp:images',
         'views.gyp:views',
-        'animator.gyp:animator',
         'xml.gyp:xml',
-        'svg.gyp:svg',
-        'experimental.gyp:experimental',
-        'pdf.gyp:pdf',
       ],
       'conditions' : [
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
diff --git a/gyp/shapeops_edge.gyp b/gyp/shapeops_edge.gyp
index 8542876..34d60f4 100644
--- a/gyp/shapeops_edge.gyp
+++ b/gyp/shapeops_edge.gyp
@@ -29,6 +29,8 @@
         '../experimental/Intersection/CubicReduceOrder.cpp',
         '../experimental/Intersection/CubicReduceOrder_Test.cpp',
         '../experimental/Intersection/CubicSubDivide.cpp',
+        '../experimental/Intersection/CubicToQuadratics.cpp',
+        '../experimental/Intersection/CubicToQuadratics_Test.cpp',
         '../experimental/Intersection/CubicUtilities.cpp',
         '../experimental/Intersection/DataTypes.cpp',
         '../experimental/Intersection/EdgeMain.cpp',
@@ -73,6 +75,7 @@
         '../experimental/Intersection/QuarticRoot.cpp',
         '../experimental/Intersection/QuarticRoot_Test.cpp',
         '../experimental/Intersection/ShapeOps.cpp',
+        '../experimental/Intersection/ShapeOpCubic4x4_Test.cpp',
         '../experimental/Intersection/ShapeOpRect4x4_Test.cpp',
         '../experimental/Intersection/Simplify.cpp',
         '../experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp',
@@ -111,9 +114,7 @@
       'dependencies': [
         'skia_base_libs.gyp:skia_base_libs',
         'effects.gyp:effects',
-        'experimental.gyp:experimental',
         'images.gyp:images',
-        'pdf.gyp:pdf',
       ],
       'conditions': [
         [ 'skia_gpu == 1', {
diff --git a/gyp/skia_base_libs.gyp b/gyp/skia_base_libs.gyp
index a611d30..245babb 100644
--- a/gyp/skia_base_libs.gyp
+++ b/gyp/skia_base_libs.gyp
@@ -8,7 +8,7 @@
       'utils.gyp:utils',
     ],
     'conditions': [
-      [ 'skia_arch_type == "x86"', {
+      [ 'skia_arch_type == "x86" and skia_os != "android"', {
         'component_libs': [
           'opts.gyp:opts_ssse3',
         ],
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index 066435c..10e99d2 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -58,6 +58,7 @@
         '../tests/HashCacheTest.cpp',
         '../tests/InfRectTest.cpp',
         '../tests/LListTest.cpp',
+        '../tests/MD5Test.cpp',
         '../tests/MathTest.cpp',
         '../tests/MatrixTest.cpp',
         '../tests/Matrix44Test.cpp',
@@ -84,6 +85,7 @@
         '../tests/RegionTest.cpp',
         '../tests/RoundRectTest.cpp',
         '../tests/RTreeTest.cpp',
+        '../tests/SHA1Test.cpp',
         '../tests/ScalarTest.cpp',
         '../tests/ShaderOpacityTest.cpp',
         '../tests/Sk64Test.cpp',
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 0cdb319..8148358 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -15,13 +15,14 @@
       'target_name': 'tools',
       'type': 'none',
       'dependencies': [
+        'bench_pictures',
+        'filter',
+        'pinspect',
+        'render_pdfs',
+        'render_pictures',
         'skdiff',
         'skhello',
         'skimage',
-        'render_pictures',
-        'bench_pictures',
-        'pinspect',
-        'filter',
       ],
     },
     {
@@ -196,11 +197,18 @@
       'type': 'executable',
       'include_dirs' : [
         '../src/core',
+        '../debugger',
       ],
       'sources': [
         '../tools/filtermain.cpp',
-        '../tools/path_utils.cpp',
         '../tools/path_utils.h',
+        '../tools/path_utils.cpp',
+        '../debugger/SkDrawCommand.h',
+        '../debugger/SkDrawCommand.cpp',
+        '../debugger/SkDebugCanvas.h',
+        '../debugger/SkDebugCanvas.cpp',
+        '../debugger/SkObjectParser.h',
+        '../debugger/SkObjectParser.cpp',
       ],
       'dependencies': [
         'skia_base_libs.gyp:skia_base_libs',
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 4e3f6d4..c62657d 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -18,6 +18,7 @@
         '../include/utils/unix',
         '../include/utils/win',
         '../include/xml',
+        '../src/core',
         '../src/utils',
       ],
       'sources': [
@@ -47,6 +48,8 @@
         '../include/utils/SkParsePaint.h',
         '../include/utils/SkParsePath.h',
         '../include/utils/SkPictureUtils.h',
+        '../include/utils/SkRandom.h',
+        '../include/utils/SkRTConf.h',
         '../include/utils/SkProxyCanvas.h',
         '../include/utils/SkUnitMappers.h',
         '../include/utils/SkWGL.h',
@@ -71,6 +74,8 @@
         '../src/utils/SkInterpolator.cpp',
         '../src/utils/SkLayer.cpp',
         '../src/utils/SkMatrix44.cpp',
+        '../src/utils/SkMD5.cpp',
+        '../src/utils/SkMD5.h',
         '../src/utils/SkMeshUtils.cpp',
         '../src/utils/SkNinePatch.cpp',
         '../src/utils/SkNWayCanvas.cpp',
@@ -81,6 +86,9 @@
         '../src/utils/SkParsePath.cpp',
         '../src/utils/SkPictureUtils.cpp',
         '../src/utils/SkProxyCanvas.cpp',
+        '../src/utils/SkSHA1.cpp',
+        '../src/utils/SkSHA1.h',
+        '../src/utils/SkRTConf.cpp',
         '../src/utils/SkThreadUtils.h',
         '../src/utils/SkThreadUtils_pthread.cpp',
         '../src/utils/SkThreadUtils_pthread.h',
@@ -219,6 +227,22 @@
           '../third_party/externals/cityhash/src',
         ],
       },
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'xcode_settings': {
+            'OTHER_CPLUSPLUSFLAGS!': [
+              '-Werror',
+            ]
+          },
+        }],
+        [ 'skia_os == "win"', {
+          'msvs_settings': {
+            'VCCLCompilerTool': {
+              'WarnAsError': 'false',
+            },
+          },
+        }],
+      ],
     },
   ],
 }
diff --git a/include/animator/SkAnimator.h b/include/animator/SkAnimator.h
index 924c595..9bb3642 100644
--- a/include/animator/SkAnimator.h
+++ b/include/animator/SkAnimator.h
@@ -498,4 +498,3 @@
 };
 
 #endif
-
diff --git a/include/animator/SkAnimatorView.h b/include/animator/SkAnimatorView.h
index 940dd26..2b2c61b 100644
--- a/include/animator/SkAnimatorView.h
+++ b/include/animator/SkAnimatorView.h
@@ -37,4 +37,3 @@
 };
 
 #endif
-
diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h
index 2b1be9f..63fc90d 100644
--- a/include/config/SkUserConfig.h
+++ b/include/config/SkUserConfig.h
@@ -79,7 +79,7 @@
     allow instance count tracking in either debug or release builds. By
     default it is enabled in debug but disabled in release.
  */
-//#define SK_ENABLE_INST_COUNT
+//#define SK_ENABLE_INST_COUNT 1
 
 /*  If, in debugging mode, Skia needs to stop (presumably to invoke a debugger)
     it will call SK_CRASH(). If this is not defined it, it is defined in
diff --git a/include/core/Sk64.h b/include/core/Sk64.h
index d232d82..6db3001 100644
--- a/include/core/Sk64.h
+++ b/include/core/Sk64.h
@@ -228,4 +228,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index 7ea950b..4ec3ea4 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -21,6 +21,7 @@
 class SkPaint;
 class SkPixelRef;
 class SkRegion;
+class SkString;
 
 // This is an opaque class, not interpreted by skia
 class SkGpuTexture;
@@ -625,6 +626,8 @@
         int       fHeight;
     };
 
+    SkDEVCODE(void toString(SkString* str) const;)
+
 private:
     struct MipMap;
     mutable MipMap* fMipMap;
diff --git a/include/core/SkBounder.h b/include/core/SkBounder.h
index 1ede548..7368d09 100644
--- a/include/core/SkBounder.h
+++ b/include/core/SkBounder.h
@@ -91,4 +91,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 3b3dbc4..1e7e828 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -803,10 +803,7 @@
 
     /** Draw the picture into this canvas. This method effective brackets the
         playback of the picture's draw calls with save/restore, so the state
-        of this canvas will be unchanged after this call. This contrasts with
-        the more immediate method SkPicture::draw(), which does not bracket
-        the canvas with save/restore, thus the canvas may be left in a changed
-        state after the call.
+        of this canvas will be unchanged after this call.
         @param picture The recorded drawing commands to playback into this
                        canvas.
     */
@@ -830,7 +827,7 @@
         @param xmode Used if both texs and colors are present. In this
                     case the colors are combined with the texture using mode,
                     before being drawn using the paint. If mode is null, then
-                    kMultiply_Mode is used.
+                    kModulate_Mode is used.
         @param indices If not null, array of indices to reference into the
                     vertex (texs, colors) array.
         @param indexCount number of entries in the indices array (if not null)
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index b39c5af..256f603 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -464,4 +464,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkColor.h b/include/core/SkColor.h
index f5055d0..dafc596 100644
--- a/include/core/SkColor.h
+++ b/include/core/SkColor.h
@@ -164,4 +164,3 @@
 typedef uint16_t (*SkXfermodeProc16)(SkPMColor src, uint16_t dst);
 
 #endif
-
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index 4f67c8d..65a8cf3 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -15,7 +15,7 @@
 #include "SkXfermode.h"
 
 class SkBitmap;
-class GrEffect;
+class GrEffectRef;
 class GrContext;
 
 class SK_API SkColorFilter : public SkFlattenable {
@@ -118,7 +118,7 @@
     /** A subclass may implement this factory function to work with the GPU backend. If the return
         is non-NULL then the caller owns a ref on the returned object.
      */
-    virtual GrEffect* asNewEffect(GrContext*) const;
+    virtual GrEffectRef* asNewEffect(GrContext*) const;
 
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 protected:
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
index 21743bd..46f7d27 100644
--- a/include/core/SkColorPriv.h
+++ b/include/core/SkColorPriv.h
@@ -861,4 +861,3 @@
 }
 
 #endif
-
diff --git a/include/core/SkColorShader.h b/include/core/SkColorShader.h
index 0823b70..c379068 100644
--- a/include/core/SkColorShader.h
+++ b/include/core/SkColorShader.h
@@ -48,6 +48,7 @@
 
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
 
 protected:
diff --git a/include/core/SkComposeShader.h b/include/core/SkComposeShader.h
index b0790bf..524161b 100644
--- a/include/core/SkComposeShader.h
+++ b/include/core/SkComposeShader.h
@@ -39,6 +39,7 @@
     virtual void endContext() SK_OVERRIDE;
     virtual void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader)
 
 protected:
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 33be2f6..81bbf43 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -14,6 +14,7 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
+#include "SkDeviceProperties.h"
 
 class SkClipStack;
 class SkDraw;
@@ -37,6 +38,13 @@
     SkDevice(const SkBitmap& bitmap);
 
     /**
+     *  Construct a new device with the specified bitmap as its backend. It is
+     *  valid for the bitmap to have no pixels associated with it. In that case,
+     *  any drawing to this device will have no effect.
+    */
+    SkDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties);
+
+    /**
      *  Create a new raster device and have the pixels be automatically
      *  allocated. The rowBytes of the device will be computed automatically
      *  based on the config and the width.
@@ -51,6 +59,23 @@
      */
     SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque = false);
 
+    /**
+     *  Create a new raster device and have the pixels be automatically
+     *  allocated. The rowBytes of the device will be computed automatically
+     *  based on the config and the width.
+     *
+     *  @param config   The desired config for the pixels. If the request cannot
+     *                  be met, the closest matching support config will be used.
+     *  @param width    width (in pixels) of the device
+     *  @param height   height (in pixels) of the device
+     *  @param isOpaque Set to true if it is known that all of the pixels will
+     *                  be drawn to opaquely. Used as an accelerator when drawing
+     *                  these pixels to another device.
+     *  @param deviceProperties Properties which affect compositing.
+     */
+    SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
+             const SkDeviceProperties& deviceProperties);
+
     virtual ~SkDevice();
 
     /**
@@ -84,6 +109,12 @@
     */
     virtual int height() const { return fBitmap.height(); }
 
+    /** Return the image properties of the device. */
+    virtual const SkDeviceProperties& getDeviceProperties() const {
+        //Currently, all the properties are leaky.
+        return fLeakyProperties;
+    }
+
     /**
      *  Return the bounds of the device in the coordinate space of the root
      *  canvas. The root device will have its top-left at 0,0, but other devices
@@ -226,6 +257,8 @@
                             const SkPoint[], const SkPaint& paint);
     virtual void drawRect(const SkDraw&, const SkRect& r,
                           const SkPaint& paint);
+    virtual void drawOval(const SkDraw&, const SkRect& oval,
+                          const SkPaint& paint);
     /**
      *  If pathIsMutable, then the implementation is allowed to cast path to a
      *  non-const pointer and modify it in place (as an optimization). Canvas
@@ -416,6 +449,13 @@
     SkBitmap    fBitmap;
     SkIPoint    fOrigin;
     SkMetaData* fMetaData;
+    /**
+     *  Leaky properties are those which the device should be applying but it isn't.
+     *  These properties will be applied by the draw, when and as it can.
+     *  If the device does handle a property, that property should be set to the identity value
+     *  for that property, effectively making it non-leaky.
+     */
+    SkDeviceProperties fLeakyProperties;
 
 #ifdef SK_DEBUG
     bool        fAttachedToCanvas;
diff --git a/include/core/SkDeviceProperties.h b/include/core/SkDeviceProperties.h
new file mode 100644
index 0000000..2c2e952
--- /dev/null
+++ b/include/core/SkDeviceProperties.h
@@ -0,0 +1,112 @@
+#ifndef SkDeviceProperties_DEFINED
+#define SkDeviceProperties_DEFINED
+
+#ifndef SK_GAMMA_EXPONENT
+    #define SK_GAMMA_EXPONENT (2.2f)
+#endif
+
+#ifdef SK_GAMMA_SRGB
+    #undef SK_GAMMA_EXPONENT
+    #define SK_GAMMA_EXPONENT (0.0f)
+#endif
+
+//TODO: get everyone to stop using SkFontHost::SetSubpixel* and remove this import.
+#include "SkFontHost.h"
+
+struct SkDeviceProperties {
+    struct Geometry {
+        /** The orientation of the pixel specifies the interpretation of the
+        *  layout. If the orientation is horizontal, the layout is interpreted as
+        *  left to right. It the orientation is vertical, the layout is
+        *  interpreted top to bottom (rotated 90deg cw from horizontal).
+        */
+        enum Orientation {
+            kUnknown_Orientation      = 0x0,
+            kKnown_Orientation        = 0x2,
+
+            kHorizontal_Orientation   = 0x2,  //!< this is the default
+            kVertical_Orientation     = 0x3,
+
+            kOrientationMask          = 0x3,
+        };
+
+        /** The layout of the pixel specifies its subpixel geometry.
+        *
+        *  kUnknown_Layout means that the subpixel elements are not spatially
+        *  separated in any known or usable fashion.
+        */
+        enum Layout {
+            kUnknown_Layout   = 0x0,
+            kKnown_Layout     = 0x8,
+
+            kRGB_Layout       = 0x8,  //!< this is the default
+            kBGR_Layout       = 0xC,
+
+            kLayoutMask       = 0xC,
+        };
+
+        Orientation getOrientation() {
+            return static_cast<Orientation>(fGeometry | kOrientationMask);
+        }
+        Layout getLayout() {
+            return static_cast<Layout>(fGeometry | kLayoutMask);
+        }
+
+        bool isOrientationKnown() {
+            return SkToBool(fGeometry & kKnown_Orientation);
+        }
+        bool isLayoutKnown() {
+            return SkToBool(fGeometry & kKnown_Layout);
+        }
+
+    private:
+        //TODO: get everyone to stop using SkFontHost::SetSubpixel* and replace these calls with constants.
+        static Orientation fromOldOrientation(SkFontHost::LCDOrientation orientation) {
+            switch (orientation) {
+            case SkFontHost::kHorizontal_LCDOrientation: return kHorizontal_Orientation;
+            case SkFontHost::kVertical_LCDOrientation: return kVertical_Orientation;
+            default: return kUnknown_Orientation;
+            }
+        }
+        static Layout fromOldLayout(SkFontHost::LCDOrder order) {
+            switch (order) {
+            case SkFontHost::kRGB_LCDOrder: return kRGB_Layout;
+            case SkFontHost::kBGR_LCDOrder: return kBGR_Layout;
+            default: return kUnknown_Layout;
+            }
+        }
+    public:
+        static Geometry MakeDefault() {
+            Orientation orientation = fromOldOrientation(SkFontHost::GetSubpixelOrientation()); //kHorizontal_Orientation
+            Layout layout = fromOldLayout(SkFontHost::GetSubpixelOrder()); //kRGB_Layout
+            Geometry ret = { SkToU8(orientation | layout) };
+            return ret;
+        }
+
+        static Geometry Make(Orientation orientation, Layout layout) {
+            Geometry ret = { SkToU8(orientation | layout) };
+            return ret;
+        }
+
+        uint8_t fGeometry;
+    };
+
+    static SkDeviceProperties MakeDefault() {
+        SkDeviceProperties ret = { Geometry::MakeDefault(), SK_GAMMA_EXPONENT };
+        return ret;
+    }
+
+    static SkDeviceProperties Make(Geometry geometry, SkScalar gamma) {
+        SkDeviceProperties ret = { geometry, gamma };
+        return ret;
+    }
+
+    /** Each pixel of an image will have some number of channels.
+     *  Can the layout of those channels be exploited? */
+    Geometry fGeometry;
+
+    /** Represents the color space of the image. This is a woefully inadequate beginning. */
+    SkScalar fGamma;
+};
+
+#endif
diff --git a/include/core/SkDraw.h b/include/core/SkDraw.h
index 4ac7cf9..6829f8e 100644
--- a/include/core/SkDraw.h
+++ b/include/core/SkDraw.h
@@ -104,6 +104,17 @@
     void    drawDevMask(const SkMask& mask, const SkPaint&) const;
     void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
 
+    /**
+     *  Return the current clip bounds, in local coordinates, with slop to account
+     *  for antialiasing or hairlines (i.e. device-bounds outset by 1, and then
+     *  run through the inverse of the matrix).
+     *
+     *  If the matrix cannot be inverted, or the current clip is empty, return
+     *  false and ignore bounds parameter.
+     */
+    bool SK_WARN_UNUSED_RESULT
+    computeConservativeLocalClipBounds(SkRect* bounds) const;
+
 public:
     const SkBitmap* fBitmap;        // required
     const SkMatrix* fMatrix;        // required
@@ -123,5 +134,3 @@
 };
 
 #endif
-
-
diff --git a/include/core/SkDrawFilter.h b/include/core/SkDrawFilter.h
index 3944257..6a50ca7 100644
--- a/include/core/SkDrawFilter.h
+++ b/include/core/SkDrawFilter.h
@@ -31,6 +31,7 @@
         kLine_Type,
         kBitmap_Type,
         kRect_Type,
+        kOval_Type,
         kPath_Type,
         kText_Type,
     };
diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h
index 8a218c3..2faf28b 100644
--- a/include/core/SkDrawLooper.h
+++ b/include/core/SkDrawLooper.h
@@ -15,6 +15,7 @@
 class SkCanvas;
 class SkPaint;
 struct SkRect;
+class SkString;
 
 /** \class SkDrawLooper
     Subclasses of SkDrawLooper can be attached to a SkPaint. Where they are,
@@ -62,6 +63,8 @@
     virtual void computeFastBounds(const SkPaint& paint,
                                    const SkRect& src, SkRect* dst);
 
+    SkDEVCODE(virtual void toString(SkString* str) const = 0;)
+
 protected:
     SkDrawLooper() {}
     SkDrawLooper(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
diff --git a/include/core/SkEmptyShader.h b/include/core/SkEmptyShader.h
index 13da457..08c131d 100644
--- a/include/core/SkEmptyShader.h
+++ b/include/core/SkEmptyShader.h
@@ -30,6 +30,7 @@
     virtual void shadeSpan16(int x, int y, uint16_t span[], int count) SK_OVERRIDE;
     virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader)
 
 protected:
diff --git a/include/core/SkEndian.h b/include/core/SkEndian.h
index 910cf1e..01d2195 100644
--- a/include/core/SkEndian.h
+++ b/include/core/SkEndian.h
@@ -149,4 +149,3 @@
 #endif
 
 #endif
-
diff --git a/include/core/SkFloatBits.h b/include/core/SkFloatBits.h
index 3c725fe..a1196ca 100644
--- a/include/core/SkFloatBits.h
+++ b/include/core/SkFloatBits.h
@@ -136,4 +136,3 @@
 #endif
 
 #endif
-
diff --git a/include/core/SkFloatingPoint.h b/include/core/SkFloatingPoint.h
index d388cdb..96270b0 100644
--- a/include/core/SkFloatingPoint.h
+++ b/include/core/SkFloatingPoint.h
@@ -89,8 +89,9 @@
 
 extern const uint32_t gIEEENotANumber;
 extern const uint32_t gIEEEInfinity;
+extern const uint32_t gIEEENegativeInfinity;
 
 #define SK_FloatNaN                 (*reinterpret_cast<const float*>(&gIEEENotANumber))
 #define SK_FloatInfinity            (*reinterpret_cast<const float*>(&gIEEEInfinity))
-
+#define SK_FloatNegativeInfinity    (*reinterpret_cast<const float*>(&gIEEENegativeInfinity))
 #endif
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h
index 737b2eb..3c2ed09 100644
--- a/include/core/SkFontHost.h
+++ b/include/core/SkFontHost.h
@@ -236,13 +236,17 @@
 
         Note, if you change this after startup, you'll need to flush the glyph
         cache because it'll have the wrong type of masks cached.
+
+        @deprecated use SkPixelGeometry instead.
     */
     enum LCDOrientation {
         kHorizontal_LCDOrientation = 0,    //!< this is the default
         kVertical_LCDOrientation   = 1
     };
 
+    /** @deprecated set on Device creation. */
     static void SetSubpixelOrientation(LCDOrientation orientation);
+    /** @deprecated get from Device. */
     static LCDOrientation GetSubpixelOrientation();
 
     /** LCD color elements can vary in order. For subpixel text we need to know
@@ -254,6 +258,8 @@
 
         kNONE_LCDOrder means that the subpixel elements are not spatially
         separated in any usable fashion.
+
+        @deprecated use SkPixelGeometry instead.
      */
     enum LCDOrder {
         kRGB_LCDOrder = 0,    //!< this is the default
@@ -261,7 +267,9 @@
         kNONE_LCDOrder = 2
     };
 
+    /** @deprecated set on Device creation. */
     static void SetSubpixelOrder(LCDOrder order);
+    /** @deprecated get from Device. */
     static LCDOrder GetSubpixelOrder();
 
 #ifdef SK_BUILD_FOR_ANDROID
diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h
index c9a0f53..87d66ad 100644
--- a/include/core/SkGraphics.h
+++ b/include/core/SkGraphics.h
@@ -109,4 +109,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 6831f2b..5066c7b 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -16,8 +16,7 @@
 class SkMatrix;
 struct SkIPoint;
 struct SkIRect;
-struct SkRect;
-class GrEffect;
+class GrEffectRef;
 class GrTexture;
 
 /**
@@ -93,7 +92,7 @@
      *  The effect can assume its vertexCoords space maps 1-to-1 with texels
      *  in the texture.
      */
-    virtual bool asNewEffect(GrEffect** effect, GrTexture*) const;
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture*) const;
 
     /**
      *  Returns true if the filter can be processed on the GPU.  This is most
@@ -104,13 +103,13 @@
     virtual bool canFilterImageGPU() const;
 
     /**
-     *  Process this image filter on the GPU.  texture is the source texture
-     *  for processing, and rect is the effect region to process.  The
-     *  function must allocate a new texture of at least rect width/height
-     *  size, and return it to the caller.  The default implementation returns
-     *  NULL.
+     *  Process this image filter on the GPU.  src is the source image for
+     *  processing, as a texture-backed bitmap.  result is the destination
+     *  bitmap, which should contain a texture-backed pixelref on success.
+     *  The default implementation returns returns false and ignores the
+     *  result parameter.
      */
-    virtual GrTexture* onFilterImageGPU(Proxy*, GrTexture* texture, const SkRect& rect);
+    virtual bool filterImageGPU(Proxy*, const SkBitmap& src, SkBitmap* result);
 
     /**
      *  Returns this image filter as a color filter if possible,
diff --git a/include/core/SkInstCnt.h b/include/core/SkInstCnt.h
index 02b63db..e7f752e 100644
--- a/include/core/SkInstCnt.h
+++ b/include/core/SkInstCnt.h
@@ -18,12 +18,9 @@
  * At the end of an application a call to all the "root" objects'
  * CheckInstanceCount methods should be made
  */
-//#if defined SK_DEBUG && !defined SK_ENABLE_INST_COUNT
-//#define SK_ENABLE_INST_COUNT
-//#endif
+#include "SkTypes.h"
 
-#ifdef SK_ENABLE_INST_COUNT
-#include <stdlib.h>
+#if SK_ENABLE_INST_COUNT
 #include "SkTArray.h"
 #include "SkThread_platform.h"
 
diff --git a/include/core/SkLineClipper.h b/include/core/SkLineClipper.h
index 5827dbe..8026890 100644
--- a/include/core/SkLineClipper.h
+++ b/include/core/SkLineClipper.h
@@ -45,4 +45,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkMask.h b/include/core/SkMask.h
index 5dadb63..4265595 100644
--- a/include/core/SkMask.h
+++ b/include/core/SkMask.h
@@ -146,4 +146,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
index 245de29..a874e62 100644
--- a/include/core/SkMaskFilter.h
+++ b/include/core/SkMaskFilter.h
@@ -145,4 +145,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkMath.h b/include/core/SkMath.h
index 35f7eda..314311f 100644
--- a/include/core/SkMath.h
+++ b/include/core/SkMath.h
@@ -159,4 +159,3 @@
 }
 
 #endif
-
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 2d3786c..87599d4 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -85,6 +85,11 @@
                         kPerspective_Mask);
     }
 
+    /** Returns true if the matrix contains only translation, rotation or uniform scale
+        Returns false if other transformation types are included or is degenerate
+     */
+    bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const;
+
     enum {
         kMScaleX,
         kMSkewX,
@@ -527,8 +532,8 @@
     // return the number of bytes read
     uint32_t readFromMemory(const void* buffer);
 
-    void dump() const;
-    void toDumpString(SkString*) const;
+    SkDEVCODE(void dump() const;)
+    SkDEVCODE(void toString(SkString*) const;)
 
     /**
      * Calculates the maximum stretching factor of the matrix. If the matrix has
diff --git a/include/core/SkMetaData.h b/include/core/SkMetaData.h
index 86f186f..5db437c 100644
--- a/include/core/SkMetaData.h
+++ b/include/core/SkMetaData.h
@@ -173,4 +173,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkOSFile.h b/include/core/SkOSFile.h
index 79551ae..257b66a 100644
--- a/include/core/SkOSFile.h
+++ b/include/core/SkOSFile.h
@@ -18,6 +18,8 @@
     #include <dirent.h>
 #endif
 
+#include <stddef.h> // ptrdiff_t
+
 struct SkFILE;
 
 enum SkFILE_Flags {
@@ -41,10 +43,13 @@
 
 size_t  sk_fread(void* buffer, size_t byteCount, SkFILE*);
 size_t  sk_fwrite(const void* buffer, size_t byteCount, SkFILE*);
+
+char*   sk_fgets(char* str, int size, SkFILE* f);
+
 void    sk_fflush(SkFILE*);
 
-int     sk_fseek( SkFILE*, size_t, int );
-size_t  sk_ftell( SkFILE* );
+int     sk_fseek(SkFILE*, size_t, int);
+size_t  sk_ftell(SkFILE*);
 
 // Returns true if something (file, directory, ???) exists at this path.
 bool    sk_exists(const char *path);
@@ -52,6 +57,10 @@
 // Returns true if a directory exists at this path.
 bool    sk_isdir(const char *path);
 
+// Have we reached the end of the file?
+int sk_feof(SkFILE *);
+
+
 // Create a new directory at this path; returns true if successful.
 // If the directory already existed, this will return true.
 // Description of the error, if any, will be written to stderr.
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index 16b1b84..a64c89d 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -19,6 +19,7 @@
 class SkAutoGlyphCache;
 class SkColorFilter;
 class SkDescriptor;
+struct SkDeviceProperties;
 class SkFlattenableReadBuffer;
 class SkFlattenableWriteBuffer;
 struct SkGlyph;
@@ -425,16 +426,20 @@
     */
     void setStrokeJoin(Join join);
 
-    /** Applies any/all effects (patheffect, stroking) to src, returning the
-        result in dst. The result is that drawing src with this paint will be
-        the same as drawing dst with a default paint (at least from the
-        geometric perspective).
-        @param src  input path
-        @param dst  output path (may be the same as src)
-        @return     true if the path should be filled, or false if it should be
-                    drawn with a hairline (width == 0)
-    */
-    bool getFillPath(const SkPath& src, SkPath* dst) const;
+    /**
+     *  Applies any/all effects (patheffect, stroking) to src, returning the
+     *  result in dst. The result is that drawing src with this paint will be
+     *  the same as drawing dst with a default paint (at least from the
+     *  geometric perspective).
+     *
+     *  @param src  input path
+     *  @param dst  output path (may be the same as src)
+     *  @param cullRect If not null, the dst path may be culled to this rect.
+     *  @return     true if the path should be filled, or false if it should be
+     *              drawn with a hairline (width == 0)
+     */
+    bool getFillPath(const SkPath& src, SkPath* dst,
+                     const SkRect* cullRect = NULL) const;
 
     /** Get the paint's shader object.
         <p />
@@ -962,9 +967,9 @@
     SkScalar measure_text(SkGlyphCache*, const char* text, size_t length,
                           int* count, SkRect* bounds) const;
 
-    SkGlyphCache*   detachCache(const SkMatrix*) const;
+    SkGlyphCache* detachCache(const SkDeviceProperties* deviceProperties, const SkMatrix*) const;
 
-    void descriptorProc(const SkMatrix* deviceMatrix,
+    void descriptorProc(const SkDeviceProperties* deviceProperties, const SkMatrix* deviceMatrix,
                         void (*proc)(const SkDescriptor*, void*),
                         void* context, bool ignoreGamma = false) const;
 
@@ -988,4 +993,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkPathEffect.h b/include/core/SkPathEffect.h
index f9495cd..3b4541d 100644
--- a/include/core/SkPathEffect.h
+++ b/include/core/SkPathEffect.h
@@ -49,7 +49,7 @@
      *  resulting stroke-rec to dst and then draw.
      */
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const = 0;
+                            SkStrokeRec*, const SkRect* cullR) const = 0;
 
     /**
      *  Compute a conservative bounds for its effect, given the src bounds.
@@ -103,7 +103,8 @@
      *  optionally return the points in 'results'.
      */
     virtual bool asPoints(PointData* results, const SkPath& src,
-                          const SkStrokeRec&, const SkMatrix&) const;
+                          const SkStrokeRec&, const SkMatrix&,
+                          const SkRect* cullR) const;
 
 protected:
     SkPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
@@ -154,7 +155,7 @@
         : INHERITED(outer, inner) {}
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposePathEffect)
 
@@ -185,7 +186,7 @@
         : INHERITED(first, second) {}
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSumPathEffect)
 
@@ -201,4 +202,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index 3525695..44e4f86 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -142,11 +142,14 @@
      */
     void serialize(SkWStream*, SkSerializationHelpers::EncodeBitmap encoder = NULL) const;
 
+#ifdef SK_BUILD_FOR_ANDROID
     /** Signals that the caller is prematurely done replaying the drawing
         commands. This can be called from a canvas virtual while the picture
         is drawing. Has no effect if the picture is not drawing.
+        @deprecated preserving for legacy purposes
     */
     void abortPlayback();
+#endif
 
 protected:
     // V2 : adds SkPixelRef's generation ID.
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index 618cc71..89ce69a 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -216,13 +216,6 @@
     // Performance tweak to avoid those calls (esp. in multi-thread use case).
     void setPreLocked(void* pixels, SkColorTable* ctable);
 
-    /**
-     *  If a subclass passed a particular mutex to the base constructor, it can
-     *  override that to go back to the default mutex by calling this. However,
-     *  this should only be called from within the subclass' constructor.
-     */
-    void useDefaultMutex() { this->setMutex(NULL); }
-
 private:
 
     SkBaseMutex*    fMutex; // must remain in scope for the life of this object
diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h
index 1ba710d..a6a8e73 100644
--- a/include/core/SkPoint.h
+++ b/include/core/SkPoint.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkPoint_DEFINED
 #define SkPoint_DEFINED
 
@@ -337,9 +335,12 @@
 #endif
     }
 
-    /** Returns true if the point's coordinates equal (x,y)
-    */
-    bool equals(SkScalar x, SkScalar y) const { return fX == x && fY == y; }
+    /**
+     *  Returns true if the point's coordinates equal (x,y)
+     */
+    bool equals(SkScalar x, SkScalar y) const {
+        return fX == x && fY == y;
+    }
 
     friend bool operator==(const SkPoint& a, const SkPoint& b) {
         return a.fX == b.fX && a.fY == b.fY;
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index 12fe87d..14a4a45 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -76,10 +76,8 @@
  */
 #if !defined(SkNO_RETURN_HINT)
     #if SK_HAS_COMPILER_FEATURE(attribute_analyzer_noreturn)
-        namespace {
-            inline void SkNO_RETURN_HINT() __attribute__((analyzer_noreturn));
-            inline void SkNO_RETURN_HINT() {}
-        }
+        static inline void SkNO_RETURN_HINT() __attribute__((analyzer_noreturn));
+        static inline void SkNO_RETURN_HINT() {}
     #else
         #define SkNO_RETURN_HINT() do {} while (false)
     #endif
@@ -94,14 +92,14 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifndef SkNEW
-    #define SkNEW(type_name)                new type_name
-    #define SkNEW_ARGS(type_name, args)     new type_name args
-    #define SkNEW_ARRAY(type_name, count)   new type_name[count]
-    #define SkNEW_PLACEMENT(buf, type_name) new (buf) type_name
+    #define SkNEW(type_name)                (new type_name)
+    #define SkNEW_ARGS(type_name, args)     (new type_name args)
+    #define SkNEW_ARRAY(type_name, count)   (new type_name[(count)])
+    #define SkNEW_PLACEMENT(buf, type_name) (new (buf) type_name)
     #define SkNEW_PLACEMENT_ARGS(buf, type_name, args) \
-                                            new (buf) type_name args
-    #define SkDELETE(obj)                   delete obj
-    #define SkDELETE_ARRAY(array)           delete[] array
+                                            (new (buf) type_name args)
+    #define SkDELETE(obj)                   (delete (obj))
+    #define SkDELETE_ARRAY(array)           (delete[] (array))
 #endif
 
 #ifndef SK_CRASH
@@ -114,6 +112,17 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// SK_ENABLE_INST_COUNT defaults to 1 in DEBUG and 0 in RELEASE
+#ifndef SK_ENABLE_INST_COUNT
+    #ifdef SK_DEBUG
+        #define SK_ENABLE_INST_COUNT 1
+    #else
+        #define SK_ENABLE_INST_COUNT 0
+    #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
 #if defined(SK_SOFTWARE_FLOAT) && defined(SK_SCALAR_IS_FLOAT)
     // if this is defined, we convert floats to 2scompliment ints for compares
     #ifndef SK_SCALAR_SLOW_COMPARES
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
index 393a348..4f977ea 100644
--- a/include/core/SkPreConfig.h
+++ b/include/core/SkPreConfig.h
@@ -55,6 +55,12 @@
     #define SK_BUILD_FOR_ANDROID
 #endif
 
+// USE_CHROMIUM_SKIA is defined when building Skia for the Chromium
+// browser.
+#if defined(USE_CHROMIUM_SKIA)
+    #define SK_BUILD_FOR_CHROMIUM
+#endif
+
 //////////////////////////////////////////////////////////////////////
 
 #if !defined(SK_DEBUG) && !defined(SK_RELEASE)
diff --git a/include/core/SkRRect.h b/include/core/SkRRect.h
index b09d27a..d6187f4 100644
--- a/include/core/SkRRect.h
+++ b/include/core/SkRRect.h
@@ -220,18 +220,34 @@
      */
     bool contains(SkScalar x, SkScalar y) const;
 
-#if 0
+    /**
+     *  Call inset on the bounds, and adjust the radii to reflect what happens
+     *  in stroking: If the corner is sharp (no curvature), leave it alone,
+     *  otherwise we grow/shrink the radii by the amount of the inset. If a
+     *  given radius becomes negative, it is pinned to 0.
+     *
+     *  It is valid for dst == this.
+     */
     void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
+
     void inset(SkScalar dx, SkScalar dy) {
         this->inset(dx, dy, this);
     }
+
+    /**
+     *  Call outset on the bounds, and adjust the radii to reflect what happens
+     *  in stroking: If the corner is sharp (no curvature), leave it alone,
+     *  otherwise we grow/shrink the radii by the amount of the inset. If a
+     *  given radius becomes negative, it is pinned to 0.
+     *
+     *  It is valid for dst == this.
+     */
     void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
         this->inset(-dx, -dy, dst);
     }
     void outset(SkScalar dx, SkScalar dy) {
         this->inset(-dx, -dy, this);
     }
-#endif
 
     SkDEBUGCODE(void validate() const;)
 
diff --git a/include/core/SkRandom.h b/include/core/SkRandom.h
deleted file mode 100644
index 73dc491..0000000
--- a/include/core/SkRandom.h
+++ /dev/null
@@ -1,150 +0,0 @@
-
-/*
- * Copyright 2006 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.
- */
-
-
-#ifndef SkRandom_DEFINED
-#define SkRandom_DEFINED
-
-#include "Sk64.h"
-#include "SkScalar.h"
-
-/** \class SkRandom
-
-    Utility class that implements pseudo random 32bit numbers using a fast
-    linear equation. Unlike rand(), this class holds its own seed (initially
-    set to 0), so that multiple instances can be used with no side-effects.
-*/
-class SkRandom {
-public:
-    SkRandom() : fSeed(0) {}
-    SkRandom(uint32_t seed) : fSeed(seed) {}
-
-    /** Return the next pseudo random number as an unsigned 32bit value.
-    */
-    uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
-
-    /** Return the next pseudo random number as a signed 32bit value.
-    */
-    int32_t nextS() { return (int32_t)this->nextU(); }
-
-    /** Return the next pseudo random number as an unsigned 16bit value.
-    */
-    U16CPU nextU16() { return this->nextU() >> 16; }
-
-    /** Return the next pseudo random number as a signed 16bit value.
-    */
-    S16CPU nextS16() { return this->nextS() >> 16; }
-
-    /**
-     *  Returns value [0...1) as a float
-     */
-    float nextF() {
-        // const is 1 / (2^32 - 1)
-        return (float)(this->nextU() * 2.32830644e-10);
-    }
-
-    /**
-     *  Returns value [min...max) as a float
-     */
-    float nextRangeF(float min, float max) {
-        return min + this->nextF() * (max - min);
-    }
-
-    /** Return the next pseudo random number, as an unsigned value of
-        at most bitCount bits.
-        @param bitCount The maximum number of bits to be returned
-    */
-    uint32_t nextBits(unsigned bitCount) {
-        SkASSERT(bitCount > 0 && bitCount <= 32);
-        return this->nextU() >> (32 - bitCount);
-    }
-
-    /** Return the next pseudo random unsigned number, mapped to lie within
-        [min, max] inclusive.
-    */
-    uint32_t nextRangeU(uint32_t min, uint32_t max) {
-        SkASSERT(min <= max);
-        return min + this->nextU() % (max - min + 1);
-    }
-
-    /** Return the next pseudo random unsigned number, mapped to lie within
-        [0, count).
-     */
-    uint32_t nextULessThan(uint32_t count) {
-        SkASSERT(count > 0);
-        return this->nextRangeU(0, count - 1);
-    }
-
-    /** Return the next pseudo random number expressed as an unsigned SkFixed
-        in the range [0..SK_Fixed1).
-    */
-    SkFixed nextUFixed1() { return this->nextU() >> 16; }
-
-    /** Return the next pseudo random number expressed as a signed SkFixed
-        in the range (-SK_Fixed1..SK_Fixed1).
-    */
-    SkFixed nextSFixed1() { return this->nextS() >> 15; }
-
-    /** Return the next pseudo random number expressed as a SkScalar
-        in the range [0..SK_Scalar1).
-    */
-    SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); }
-
-    /** Return the next pseudo random number expressed as a SkScalar
-        in the range [min..max).
-    */
-    SkScalar nextRangeScalar(SkScalar min, SkScalar max) {
-        return SkScalarMul(this->nextUScalar1(), (max - min)) + min;
-    }
-
-    /** Return the next pseudo random number expressed as a SkScalar
-        in the range (-SK_Scalar1..SK_Scalar1).
-    */
-    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
-
-    /** Return the next pseudo random number as a bool.
-    */
-    bool nextBool() { return this->nextU() >= 0x80000000; }
-
-    /** A biased version of nextBool().
-     */
-    bool nextBiasedBool(SkScalar fractionTrue) {
-        SkASSERT(fractionTrue >= 0 && fractionTrue <= SK_Scalar1);
-        return this->nextUScalar1() <= fractionTrue;
-    }
-
-    /** Return the next pseudo random number as a signed 64bit value.
-    */
-    void next64(Sk64* a) {
-        SkASSERT(a);
-        a->set(this->nextS(), this->nextU());
-    }
-
-    /**
-     *  Return the current seed. This allows the caller to later reset to the
-     *  same seed (using setSeed) so it can generate the same sequence.
-     */
-    int32_t getSeed() const { return fSeed; }
-
-    /** Set the seed of the random object. The seed is initialized to 0 when the
-        object is first created, and is updated each time the next pseudo random
-        number is requested.
-    */
-    void setSeed(int32_t seed) { fSeed = (uint32_t)seed; }
-
-private:
-    //  See "Numerical Recipes in C", 1992 page 284 for these constants
-    enum {
-        kMul = 1664525,
-        kAdd = 1013904223
-    };
-    uint32_t fSeed;
-};
-
-#endif
-
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 21ea84f..e6efed5 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -384,6 +384,7 @@
         return r;
     }
 
+    // DEPRECATED: call Make(r)
     static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
         SkRect r;
         r.set(SkIntToScalar(irect.fLeft),
@@ -393,6 +394,15 @@
         return r;
     }
 
+    static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
+        SkRect r;
+        r.set(SkIntToScalar(irect.fLeft),
+              SkIntToScalar(irect.fTop),
+              SkIntToScalar(irect.fRight),
+              SkIntToScalar(irect.fBottom));
+        return r;
+    }
+
     /**
      *  Return true if the rectangle's width or height are <= 0
      */
@@ -481,6 +491,16 @@
         fBottom = SkIntToScalar(bottom);
     }
 
+    /**
+     *  Set this rectangle to be left/top at 0,0, and have the specified width
+     *  and height (automatically converted to SkScalar).
+     */
+    void isetWH(int width, int height) {
+        fLeft = fTop = 0;
+        fRight = SkIntToScalar(width);
+        fBottom = SkIntToScalar(height);
+    }
+
     /** Set this rectangle to be the bounds of the array of points.
         If the array is empty (count == 0), then set this rectangle
         to the empty rectangle (0,0,0,0)
@@ -752,4 +772,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h
index 21ed0ef..0330133 100644
--- a/include/core/SkRefCnt.h
+++ b/include/core/SkRefCnt.h
@@ -262,4 +262,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
index ea97a79..b775472 100644
--- a/include/core/SkScalar.h
+++ b/include/core/SkScalar.h
@@ -39,6 +39,9 @@
     /** SK_ScalarInfinity is defined to be infinity as an SkScalar
     */
     #define SK_ScalarInfinity       SK_FloatInfinity
+    /** SK_ScalarNegativeInfinity is defined to be negative infinity as an SkScalar
+    */
+    #define SK_ScalarNegativeInfinity       SK_FloatNegativeInfinity
     /** SK_ScalarMax is defined to be the largest value representable as an SkScalar
     */
     #define SK_ScalarMax            (3.402823466e+38f)
@@ -220,7 +223,8 @@
 
     #define SK_Scalar1              SK_Fixed1
     #define SK_ScalarHalf           SK_FixedHalf
-    #define SK_ScalarInfinity   SK_FixedMax
+    #define SK_ScalarInfinity           SK_FixedMax
+    #define SK_ScalarNegativeInfinity   SK_FixedMin
     #define SK_ScalarMax            SK_FixedMax
     #define SK_ScalarMin            SK_FixedMin
     #define SK_ScalarNaN            SK_FixedNaN
diff --git a/include/core/SkScalarCompare.h b/include/core/SkScalarCompare.h
index 0842fdd..5361294 100644
--- a/include/core/SkScalarCompare.h
+++ b/include/core/SkScalarCompare.h
@@ -36,4 +36,3 @@
 #endif
 
 #endif
-
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index 389be35..126602d 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -18,8 +18,7 @@
 
 class SkPath;
 class GrContext;
-class GrEffect;
-class GrEffectStage;
+class GrEffectRef;
 
 /** \class SkShader
  *
@@ -319,11 +318,10 @@
 
     /**
      *  If the shader subclass has a GrEffect implementation, this installs an effect on the stage.
-     *  A GrContext pointer is required since effects may need to create textures. The stage
-     *  parameter is necessary to set a texture matrix. It will eventually be removed and this
-     *  function will operate as a GrEffect factory.
+     *  The GrContext may be used by the effect to create textures. The GPU device does not call
+     *  setContext. Instead we pass the paint here in case the shader needs paint info.
      */
-    virtual bool asNewEffect(GrContext* context, GrEffectStage* stage) const;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint& paint) const;
 
     //////////////////////////////////////////////////////////////////////////
     //  Factory methods for stock shaders
@@ -342,6 +340,8 @@
     static SkShader* CreateBitmapShader(const SkBitmap& src,
                                         TileMode tmx, TileMode tmy);
 
+    SkDEVCODE(virtual void toString(SkString* str) const;)
+
 protected:
     enum MatrixClass {
         kLinear_MatrixClass,            // no perspective
@@ -374,4 +374,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkStream.h b/include/core/SkStream.h
index 08d0878..7ec3117 100644
--- a/include/core/SkStream.h
+++ b/include/core/SkStream.h
@@ -188,6 +188,13 @@
     /** if copyData is true, the stream makes a private copy of the data
     */
     SkMemoryStream(const void* data, size_t length, bool copyData = false);
+
+    /**
+     *  Use the specified data as the memory for this stream. The stream will
+     *  call ref() on the data (assuming it is not null).
+     */
+    SkMemoryStream(SkData*);
+
     virtual ~SkMemoryStream();
 
     /** Resets the stream to the specified data and length,
diff --git a/include/core/SkString.h b/include/core/SkString.h
index 94dcf8b..0049402 100644
--- a/include/core/SkString.h
+++ b/include/core/SkString.h
@@ -30,16 +30,33 @@
 
 int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
 
+static int SkStrFind(const char string[], const char substring[]) {
+    const char *first = strstr(string, substring);
+    if (NULL == first) return -1;
+    return first - &(string[0]);
+}
+
 static bool SkStrContains(const char string[], const char substring[]) {
     SkASSERT(string);
     SkASSERT(substring);
-    return (NULL != strstr(string, substring));
+    return (-1 != SkStrFind(string, substring));
 }
 static bool SkStrContains(const char string[], const char subchar) {
     SkASSERT(string);
-    return (NULL != strchr(string, subchar));
+    char tmp[2];
+    tmp[0] = subchar;
+    tmp[1] = '\0';
+    return (-1 != SkStrFind(string, tmp));
 }
 
+static inline char *SkStrDup(const char string[]) {
+    char *ret = (char *) sk_malloc_throw(strlen(string)+1);
+    memcpy(ret,string,strlen(string));
+    return ret;
+}
+
+
+
 #define SkStrAppendS32_MaxSize  11
 char*   SkStrAppendS32(char buffer[], int32_t);
 #define SkStrAppendS64_MaxSize  20
@@ -112,6 +129,9 @@
     bool contains(const char subchar) const {
         return SkStrContains(fRec->data(), subchar);
     }
+    int find(const char substring[]) const {
+        return SkStrFind(fRec->data(), substring);
+    }
 
     friend bool operator==(const SkString& a, const SkString& b) {
         return a.equals(b);
diff --git a/include/core/SkStringUtils.h b/include/core/SkStringUtils.h
new file mode 100644
index 0000000..aa5c809
--- /dev/null
+++ b/include/core/SkStringUtils.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkStringUtils_DEFINED
+#define SkStringUtils_DEFINED
+
+class SkString;
+
+/**
+ * Add 'flagStr' to 'string' and set 'needSeparator' to true only if 'flag' is
+ * true. If 'needSeparator' is true append a '|' before 'flagStr'. This method
+ * is used to streamline the creation of ASCII flag strings within the toString
+ * methods.
+ */
+void SkAddFlagToString(SkString* string, bool flag,
+                       const char* flagStr, bool* needSeparator);
+
+
+#endif
diff --git a/include/core/SkTArray.h b/include/core/SkTArray.h
index 3ca6679..b264482 100644
--- a/include/core/SkTArray.h
+++ b/include/core/SkTArray.h
@@ -410,4 +410,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTDArray.h b/include/core/SkTDArray.h
index 997a070..ee77889 100644
--- a/include/core/SkTDArray.h
+++ b/include/core/SkTDArray.h
@@ -109,6 +109,10 @@
         return fArray[index];
     }
 
+    T&  getAt(int index) const {
+        return (*this)[index];
+    }
+
     void reset() {
         if (fArray) {
             sk_free(fArray);
@@ -302,6 +306,15 @@
         this->reset();
     }
 
+    void visitAll(void visitor(T&)) const {
+        T* stop = this->end();
+        for (T* curr = this->begin(); curr < stop; curr++) {
+            if (*curr) {
+                visitor(*curr);
+            }
+        }
+    }
+
 #ifdef SK_DEBUG
     void validate() const {
         SkASSERT((fReserve == 0 && fArray == NULL) ||
diff --git a/include/core/SkTDStack.h b/include/core/SkTDStack.h
index be34e01..e286e4a 100644
--- a/include/core/SkTDStack.h
+++ b/include/core/SkTDStack.h
@@ -108,4 +108,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTDict.h b/include/core/SkTDict.h
index 3620899..b704a48 100644
--- a/include/core/SkTDict.h
+++ b/include/core/SkTDict.h
@@ -159,4 +159,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTLazy.h b/include/core/SkTLazy.h
index 315bd95..b9052f0 100644
--- a/include/core/SkTLazy.h
+++ b/include/core/SkTLazy.h
@@ -154,4 +154,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTSearch.h b/include/core/SkTSearch.h
index 2541634..eaf5ee5 100644
--- a/include/core/SkTSearch.h
+++ b/include/core/SkTSearch.h
@@ -237,4 +237,3 @@
 #define SkCastForQSort(compare) reinterpret_cast<int (*)(const void*, const void*)>(compare)
 
 #endif
-
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
index b390a19..42e9943 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -350,4 +350,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkThread_platform.h b/include/core/SkThread_platform.h
index ce7a871..535a5c7 100644
--- a/include/core/SkThread_platform.h
+++ b/include/core/SkThread_platform.h
@@ -157,7 +157,7 @@
 
 // A normal mutex that requires to be initialized through normal C++ construction,
 // i.e. when it's a member of another class, or allocated on the heap.
-class SkMutex : public SkBaseMutex, SkNoncopyable {
+class SK_API SkMutex : public SkBaseMutex, SkNoncopyable {
 public:
     SkMutex();
     ~SkMutex();
@@ -168,7 +168,7 @@
 // In the generic case, SkBaseMutex and SkMutex are the same thing, and we
 // can't easily get rid of static initializers.
 //
-class SkMutex : SkNoncopyable {
+class SK_API SkMutex : SkNoncopyable {
 public:
     SkMutex();
     ~SkMutex();
diff --git a/include/core/SkTileGridPicture.h b/include/core/SkTileGridPicture.h
index b35c2a3..6263ecb 100644
--- a/include/core/SkTileGridPicture.h
+++ b/include/core/SkTileGridPicture.h
@@ -18,7 +18,7 @@
  * primitives for arbitrary query rectangles. It is most effective for
  * tiled playback when the tile structure is known at record time.
  */
-class SkTileGridPicture : public SkPicture {
+class SK_API SkTileGridPicture : public SkPicture {
 public:
     SkTileGridPicture(int tileWidth, int tileHeight, int width, int height);
     virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE;
diff --git a/include/core/SkTime.h b/include/core/SkTime.h
index ede1fd9..7f3c270 100644
--- a/include/core/SkTime.h
+++ b/include/core/SkTime.h
@@ -62,4 +62,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTrace.h b/include/core/SkTrace.h
index 2d48799..e6e7e2c 100644
--- a/include/core/SkTrace.h
+++ b/include/core/SkTrace.h
@@ -43,5 +43,3 @@
 #endif
 
 #endif
-
-
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
index 1c80f0e..cbba50c 100644
--- a/include/core/SkTypes.h
+++ b/include/core/SkTypes.h
@@ -112,6 +112,16 @@
     #define SkAssertResult(cond)        cond
 #endif
 
+#ifdef SK_DEVELOPER
+    #define SkDEVCODE(code)             code
+    // the 'toString' helper functions convert Sk* objects to human-readable
+    // form in developer mode
+    #define SK_DEVELOPER_TO_STRING()    virtual void toString(SkString* str) const SK_OVERRIDE;
+#else
+    #define SkDEVCODE(code)
+    #define SK_DEVELOPER_TO_STRING()
+#endif
+
 template <bool>
 struct SkCompileAssert {
 };
@@ -201,7 +211,7 @@
 #define SK_MinS32   -SK_MaxS32
 #define SK_MaxU32   0xFFFFFFFF
 #define SK_MinU32   0
-#define SK_NaN32    0x80000000
+#define SK_NaN32    (1 << 31)
 
 /** Returns true if the value can be represented with signed 16bits
  */
@@ -283,6 +293,13 @@
 #endif
 }
 
+template <typename T> inline T SkTAbs(T value) {
+    if (value < 0) {
+        value = -value;
+    }
+    return value;
+}
+
 static inline int32_t SkMax32(int32_t a, int32_t b) {
     if (a < b)
         a = b;
diff --git a/include/core/SkUnitMapper.h b/include/core/SkUnitMapper.h
index 45bb987..754be26 100644
--- a/include/core/SkUnitMapper.h
+++ b/include/core/SkUnitMapper.h
@@ -33,4 +33,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkUtils.h b/include/core/SkUtils.h
index b700b96..c0ce51f 100644
--- a/include/core/SkUtils.h
+++ b/include/core/SkUtils.h
@@ -133,4 +133,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index 3f5a243..51044ab 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -24,6 +24,7 @@
 class SkWStream;
 
 class SkWriter32 : SkNoncopyable {
+    struct BlockHeader;
 public:
     /**
      *  The caller can specify an initial block of storage, which the caller manages.
@@ -34,37 +35,35 @@
     SkWriter32(size_t minSize, void* initialStorage, size_t storageSize);
 
     SkWriter32(size_t minSize)
-        : fMinSize(minSize),
-          fSize(0),
-          fSingleBlock(NULL),
-          fSingleBlockSize(0),
-          fWrittenBeforeLastBlock(0),
-          fHead(NULL),
-          fTail(NULL),
-          fHeadIsExternalStorage(false) {}
+        : fHead(NULL)
+        , fTail(NULL)
+        , fMinSize(minSize)
+        , fSize(0)
+        , fWrittenBeforeLastBlock(0)
+        {}
 
     ~SkWriter32();
 
-    /**
-     *  Returns the single block backing the writer, or NULL if the memory is
-     *  to be dynamically allocated.
-     */
-    void* getSingleBlock() const { return fSingleBlock; }
-
     // return the current offset (will always be a multiple of 4)
     uint32_t bytesWritten() const { return fSize; }
     // DEPRECATED: use byetsWritten instead
     uint32_t  size() const { return this->bytesWritten(); }
 
     void      reset();
-    uint32_t* reserve(size_t size); // size MUST be multiple of 4
 
-    /**
-     *  Specify the single block to back the writer, rathern than dynamically
-     *  allocating the memory. If block == NULL, then the writer reverts to
-     *  dynamic allocation (and resets).
-     */
-    void reset(void* block, size_t size);
+    // size MUST be multiple of 4
+    uint32_t* reserve(size_t size) {
+        SkASSERT(SkAlign4(size) == size);
+
+        Block* block = fTail;
+        if (NULL == block || block->available() < size) {
+            block = this->doReserve(size);
+        }
+        fSize += size;
+        return block->alloc(size);
+    }
+
+    void reset(void* storage, size_t size);
 
     bool writeBool(bool value) {
         this->writeInt(value);
@@ -197,23 +196,80 @@
     bool writeToStream(SkWStream*);
 
 private:
+    struct Block {
+        Block*  fNext;
+        char*   fBasePtr;
+        size_t  fSizeOfBlock;      // total space allocated (after this)
+        size_t  fAllocatedSoFar;    // space used so far
+
+        size_t  available() const { return fSizeOfBlock - fAllocatedSoFar; }
+        char*   base() { return fBasePtr; }
+        const char* base() const { return fBasePtr; }
+
+        uint32_t* alloc(size_t size) {
+            SkASSERT(SkAlign4(size) == size);
+            SkASSERT(this->available() >= size);
+            void* ptr = this->base() + fAllocatedSoFar;
+            fAllocatedSoFar += size;
+            SkASSERT(fAllocatedSoFar <= fSizeOfBlock);
+            return (uint32_t*)ptr;
+        }
+
+        uint32_t* peek32(size_t offset) {
+            SkASSERT(offset <= fAllocatedSoFar + 4);
+            void* ptr = this->base() + offset;
+            return (uint32_t*)ptr;
+        }
+
+        void rewind() {
+            fNext = NULL;
+            fAllocatedSoFar = 0;
+            // keep fSizeOfBlock as is
+        }
+
+        static Block* Create(size_t size) {
+            SkASSERT(SkIsAlign4(size));
+            Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
+            block->fNext = NULL;
+            block->fBasePtr = (char*)(block + 1);
+            block->fSizeOfBlock = size;
+            block->fAllocatedSoFar = 0;
+            return block;
+        }
+
+        Block* initFromStorage(void* storage, size_t size) {
+            SkASSERT(SkIsAlign4((intptr_t)storage));
+            SkASSERT(SkIsAlign4(size));
+            Block* block = this;
+            block->fNext = NULL;
+            block->fBasePtr = (char*)storage;
+            block->fSizeOfBlock = size;
+            block->fAllocatedSoFar = 0;
+            return block;
+        }
+    };
+
+    enum {
+        MIN_BLOCKSIZE = sizeof(SkWriter32::Block) + sizeof(intptr_t)
+    };
+
+    Block       fExternalBlock;
+    Block*      fHead;
+    Block*      fTail;
     size_t      fMinSize;
     uint32_t    fSize;
-
-    char*       fSingleBlock;
-    uint32_t    fSingleBlockSize;
-
     // sum of bytes written in all blocks *before* fTail
     uint32_t    fWrittenBeforeLastBlock;
 
-    struct Block;
-    Block*  fHead;
-    Block*  fTail;
-
-    bool fHeadIsExternalStorage;
+    bool isHeadExternallyAllocated() const {
+        return fHead == &fExternalBlock;
+    }
 
     Block* newBlock(size_t bytes);
 
+    // only call from reserve()
+    Block* doReserve(size_t bytes);
+
     SkDEBUGCODE(void validate() const;)
 };
 
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h
index 7fc1d50..d396529 100644
--- a/include/core/SkXfermode.h
+++ b/include/core/SkXfermode.h
@@ -13,6 +13,8 @@
 #include "SkFlattenable.h"
 #include "SkColor.h"
 
+class SkString;
+
 /** \class SkXfermode
 
     SkXfermode is the base class for objects that are called to implement custom
@@ -101,7 +103,7 @@
         // all remaining modes are defined in the SVG Compositing standard
         // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
         kPlus_Mode,
-        kMultiply_Mode,
+        kModulate_Mode, // multiplies all components (= alpha and color)
 
         // all above modes can be expressed as pair of src/dst Coeffs
         kCoeffModesCnt,
@@ -174,6 +176,7 @@
         return AsMode(xfer, mode);
     }
 
+    SkDEVCODE(virtual void toString(SkString* str) const = 0;)
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 protected:
     SkXfermode(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {}
@@ -216,6 +219,7 @@
     virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
                         const SkAlpha aa[]) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcXfermode)
 
 protected:
diff --git a/include/effects/Sk1DPathEffect.h b/include/effects/Sk1DPathEffect.h
index 0ceadc1..4599276 100644
--- a/include/effects/Sk1DPathEffect.h
+++ b/include/effects/Sk1DPathEffect.h
@@ -17,7 +17,7 @@
 class SK_API Sk1DPathEffect : public SkPathEffect {
 public:
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
 protected:
     /** Called at the start of each contour, returns the initial offset
@@ -55,7 +55,7 @@
     SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style);
 
     virtual bool filterPath(SkPath*, const SkPath&,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPath1DPathEffect)
 
diff --git a/include/effects/Sk2DPathEffect.h b/include/effects/Sk2DPathEffect.h
index 3f774ae..ed7f674 100644
--- a/include/effects/Sk2DPathEffect.h
+++ b/include/effects/Sk2DPathEffect.h
@@ -17,7 +17,7 @@
     Sk2DPathEffect(const SkMatrix& mat);
 
     virtual bool filterPath(SkPath*, const SkPath&,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sk2DPathEffect)
 
@@ -62,7 +62,7 @@
     : Sk2DPathEffect(matrix), fWidth(width) {}
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLine2DPathEffect)
 
diff --git a/include/effects/SkArithmeticMode.h b/include/effects/SkArithmeticMode.h
index 87da333..dc5493f 100644
--- a/include/effects/SkArithmeticMode.h
+++ b/include/effects/SkArithmeticMode.h
@@ -24,6 +24,9 @@
      */
     static SkXfermode* Create(SkScalar k1, SkScalar k2,
                               SkScalar k3, SkScalar k4);
+
+private:
+    typedef SkXfermode INHERITED;
 };
 
 #endif
diff --git a/include/effects/SkAvoidXfermode.h b/include/effects/SkAvoidXfermode.h
index d5b33e3..e354391 100644
--- a/include/effects/SkAvoidXfermode.h
+++ b/include/effects/SkAvoidXfermode.h
@@ -49,6 +49,7 @@
     virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
                         const SkAlpha aa[]) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAvoidXfermode)
 
 protected:
diff --git a/include/effects/SkBicubicImageFilter.h b/include/effects/SkBicubicImageFilter.h
new file mode 100644
index 0000000..cb856fb
--- /dev/null
+++ b/include/effects/SkBicubicImageFilter.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SkBicubicImageFilter_DEFINED
+#define SkBicubicImageFilter_DEFINED
+
+#include "SkSingleInputImageFilter.h"
+#include "SkScalar.h"
+#include "SkSize.h"
+#include "SkPoint.h"
+
+/*! \class SkBicubicImageFilter
+    Bicubic resampling image filter.  This filter does a 16-tap bicubic
+    filter using the given matrix.
+ */
+
+class SK_API SkBicubicImageFilter : public SkSingleInputImageFilter {
+public:
+    /** Construct a (scaling-only) bicubic resampling image filter.
+        @param scale        How much to scale the image.
+        @param coefficients The 16 coefficients of the bicubic matrix.
+        @param input        The input image filter.  If NULL, the src bitmap
+                            passed to filterImage() is used instead.
+    */
+
+    SkBicubicImageFilter(const SkSize& scale, const SkScalar coefficients[16], SkImageFilter* input = NULL);
+    static SkBicubicImageFilter* CreateMitchell(const SkSize& scale, SkImageFilter* input = NULL);
+    virtual ~SkBicubicImageFilter();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBicubicImageFilter)
+
+protected:
+    SkBicubicImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
+
+private:
+    SkSize    fScale;
+    SkScalar  fCoefficients[16];
+    typedef SkSingleInputImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkBlendImageFilter.h b/include/effects/SkBlendImageFilter.h
index c16186f..a2dc847 100644
--- a/include/effects/SkBlendImageFilter.h
+++ b/include/effects/SkBlendImageFilter.h
@@ -33,7 +33,7 @@
                                SkIPoint* offset) SK_OVERRIDE;
 #if SK_SUPPORT_GPU
     virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
-    virtual GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) SK_OVERRIDE;
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
 #endif
 
 protected:
diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h
index c410761..e968857 100644
--- a/include/effects/SkBlurDrawLooper.h
+++ b/include/effects/SkBlurDrawLooper.h
@@ -43,6 +43,7 @@
     virtual void init(SkCanvas*);
     virtual bool next(SkCanvas*, SkPaint* paint);
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurDrawLooper)
 
 protected:
diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h
index 757fee5..018718f 100644
--- a/include/effects/SkBlurImageFilter.h
+++ b/include/effects/SkBlurImageFilter.h
@@ -25,7 +25,7 @@
                                SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
 
     bool canFilterImageGPU() const SK_OVERRIDE { return true; }
-    virtual GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) SK_OVERRIDE;
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
 
 private:
     SkSize   fSigma;
diff --git a/include/effects/SkColorMatrixFilter.h b/include/effects/SkColorMatrixFilter.h
index 222646e..e44a7cd 100644
--- a/include/effects/SkColorMatrixFilter.h
+++ b/include/effects/SkColorMatrixFilter.h
@@ -22,7 +22,7 @@
     virtual uint32_t getFlags() const SK_OVERRIDE;
     virtual bool asColorMatrix(SkScalar matrix[20]) const SK_OVERRIDE;
 #if SK_SUPPORT_GPU
-    virtual GrEffect* asNewEffect(GrContext*) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext*) const SK_OVERRIDE;
 #endif
 
     struct State {
diff --git a/include/effects/SkCornerPathEffect.h b/include/effects/SkCornerPathEffect.h
index ee89219..704b7fb 100644
--- a/include/effects/SkCornerPathEffect.h
+++ b/include/effects/SkCornerPathEffect.h
@@ -24,7 +24,7 @@
     virtual ~SkCornerPathEffect();
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkCornerPathEffect)
 
diff --git a/include/effects/SkDashPathEffect.h b/include/effects/SkDashPathEffect.h
index cc9ad75..9c0775d 100644
--- a/include/effects/SkDashPathEffect.h
+++ b/include/effects/SkDashPathEffect.h
@@ -41,10 +41,11 @@
     virtual ~SkDashPathEffect();
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     virtual bool asPoints(PointData* results, const SkPath& src,
-                          const SkStrokeRec&, const SkMatrix&) const SK_OVERRIDE;
+                          const SkStrokeRec&, const SkMatrix&,
+                          const SkRect*) const SK_OVERRIDE;
 
     virtual Factory getFactory() SK_OVERRIDE;
 
diff --git a/include/effects/SkDiscretePathEffect.h b/include/effects/SkDiscretePathEffect.h
index 029ebfd..999ea04 100644
--- a/include/effects/SkDiscretePathEffect.h
+++ b/include/effects/SkDiscretePathEffect.h
@@ -23,7 +23,7 @@
     SkDiscretePathEffect(SkScalar segLength, SkScalar deviation);
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiscretePathEffect)
 
diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h
new file mode 100644
index 0000000..18f9df8
--- /dev/null
+++ b/include/effects/SkDisplacementMapEffect.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDisplacementMapEffect_DEFINED
+#define SkDisplacementMapEffect_DEFINED
+
+#include "SkImageFilter.h"
+#include "SkBitmap.h"
+
+class SK_API SkDisplacementMapEffect : public SkImageFilter {
+public:
+    enum ChannelSelectorType {
+        kUnknown_ChannelSelectorType,
+        kR_ChannelSelectorType,
+        kG_ChannelSelectorType,
+        kB_ChannelSelectorType,
+        kA_ChannelSelectorType,
+        kKeyBits = 3 // Max value is 4, so 3 bits are required at most
+    };
+
+    SkDisplacementMapEffect(ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, SkScalar scale, SkImageFilter* displacement, SkImageFilter* color = NULL);
+
+    ~SkDisplacementMapEffect();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDisplacementMapEffect)
+
+    virtual bool onFilterImage(Proxy* proxy,
+                               const SkBitmap& src,
+                               const SkMatrix& ctm,
+                               SkBitmap* dst,
+                               SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
+
+protected:
+    explicit SkDisplacementMapEffect(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    ChannelSelectorType fXChannelSelector;
+    ChannelSelectorType fYChannelSelector;
+    SkScalar fScale;
+    typedef SkImageFilter INHERITED;
+    SkImageFilter* getDisplacementInput() { return getInput(0); }
+    SkImageFilter* getColorInput() { return getInput(1); }
+};
+
+#endif
diff --git a/include/effects/SkGradientShader.h b/include/effects/SkGradientShader.h
index 443be93..dbeb2fb 100644
--- a/include/effects/SkGradientShader.h
+++ b/include/effects/SkGradientShader.h
@@ -133,4 +133,3 @@
 };
 
 #endif
-
diff --git a/include/effects/SkImageFilterUtils.h b/include/effects/SkImageFilterUtils.h
new file mode 100644
index 0000000..72a25fe
--- /dev/null
+++ b/include/effects/SkImageFilterUtils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SkImageFilterUtils_DEFINED
+#define SkImageFilterUtils_DEFINED
+
+#if SK_SUPPORT_GPU
+
+#include "SkImageFilter.h"
+
+class SkBitmap;
+class GrTexture;
+class SkImageFilter;
+
+class SK_API SkImageFilterUtils {
+public:
+    /**
+     * Wrap the given texture in a texture-backed SkBitmap.
+     */
+    static bool WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result);
+
+    /**
+     * Recursively evaluate the given filter on the GPU.  If filter is NULL,
+     * this function returns src.  If the filter has no GPU implementation, it
+     * will be processed in software and uploaded to the GPU.
+     */
+    static bool GetInputResultGPU(SkImageFilter* filter, SkImageFilter::Proxy* proxy, const SkBitmap& src, SkBitmap* result);
+};
+
+#endif
+
+#endif
diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h
index 5196af0..cec78b7 100644
--- a/include/effects/SkLayerDrawLooper.h
+++ b/include/effects/SkLayerDrawLooper.h
@@ -90,7 +90,7 @@
     SkPaint* addLayer(const LayerInfo&);
 
     /**
-     *  This layer will draw with the original paint, ad the specified offset
+     *  This layer will draw with the original paint, at the specified offset
      */
     void addLayer(SkScalar dx, SkScalar dy);
 
@@ -103,6 +103,7 @@
     virtual void init(SkCanvas*);
     virtual bool next(SkCanvas*, SkPaint* paint);
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLayerDrawLooper)
 
 protected:
diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h
index 95498bc..da95b7b 100644
--- a/include/effects/SkLightingImageFilter.h
+++ b/include/effects/SkLightingImageFilter.h
@@ -87,4 +87,3 @@
 };
 
 #endif
-
diff --git a/include/effects/SkMagnifierImageFilter.h b/include/effects/SkMagnifierImageFilter.h
index 400a010..e013426 100644
--- a/include/effects/SkMagnifierImageFilter.h
+++ b/include/effects/SkMagnifierImageFilter.h
@@ -16,8 +16,7 @@
 public:
     SkMagnifierImageFilter(SkRect srcRect, SkScalar inset);
 
-    virtual bool asNewEffect(GrEffect** effect,
-                             GrTexture* texture) const SK_OVERRIDE;
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture* texture) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMagnifierImageFilter)
 
diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h
index f4d2c8e..09cb5ef 100644
--- a/include/effects/SkMatrixConvolutionImageFilter.h
+++ b/include/effects/SkMatrixConvolutionImageFilter.h
@@ -62,7 +62,7 @@
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
 
 #if SK_SUPPORT_GPU
-    virtual bool asNewEffect(GrEffect**, GrTexture*) const SK_OVERRIDE;
+    virtual bool asNewEffect(GrEffectRef**, GrTexture*) const SK_OVERRIDE;
 #endif
 
 private:
diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
index edee221..b4bc676 100644
--- a/include/effects/SkMorphologyImageFilter.h
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -38,8 +38,7 @@
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
 #if SK_SUPPORT_GPU
-    virtual GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src,
-                                        const SkRect& rect) SK_OVERRIDE;
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
 #endif
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDilateImageFilter)
@@ -59,8 +58,7 @@
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
 #if SK_SUPPORT_GPU
-    virtual GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src,
-                                        const SkRect& rect) SK_OVERRIDE;
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
 #endif
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkErodeImageFilter)
@@ -73,4 +71,3 @@
 };
 
 #endif
-
diff --git a/include/effects/SkPixelXorXfermode.h b/include/effects/SkPixelXorXfermode.h
index 53f1210..5411b12 100644
--- a/include/effects/SkPixelXorXfermode.h
+++ b/include/effects/SkPixelXorXfermode.h
@@ -19,6 +19,7 @@
 public:
     SkPixelXorXfermode(SkColor opColor) : fOpColor(opColor) {}
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPixelXorXfermode)
 
 protected:
@@ -26,7 +27,7 @@
     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     // override from SkXfermode
-    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst);
+    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const;
 
 private:
     SkColor fOpColor;
diff --git a/include/effects/SkPorterDuff.h b/include/effects/SkPorterDuff.h
index 1bba171..c5f5492 100644
--- a/include/effects/SkPorterDuff.h
+++ b/include/effects/SkPorterDuff.h
@@ -40,7 +40,7 @@
         kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
         kDarken_Mode,   //!< [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]
         kLighten_Mode,  //!< [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]
-        kMultiply_Mode, //!< [Sa * Da, Sc * Dc]
+        kModulate_Mode, //!< [Sa * Da, Sc * Dc] multiplies all components
         kScreen_Mode,   //!< [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
         kAdd_Mode,      //!< Saturate(S + D)
 #ifdef SK_BUILD_FOR_ANDROID
diff --git a/include/effects/SkSingleInputImageFilter.h b/include/effects/SkSingleInputImageFilter.h
index e7819c4..3df718f 100644
--- a/include/effects/SkSingleInputImageFilter.h
+++ b/include/effects/SkSingleInputImageFilter.h
@@ -29,12 +29,6 @@
     SkBitmap getInputResult(Proxy*, const SkBitmap& src, const SkMatrix&,
                             SkIPoint* offset);
 
-#if SK_SUPPORT_GPU
-    // Recurses on input (if non-NULL), and returns the processed result as
-    // a texture, otherwise returns src.
-    GrTexture* getInputResultAsTexture(Proxy* proxy, GrTexture* src, const SkRect& rect);
-#endif
-
     SkImageFilter* input() const { return getInput(0); }
 private:
     typedef SkImageFilter INHERITED;
diff --git a/include/effects/SkTransparentShader.h b/include/effects/SkTransparentShader.h
index 74187f7..bee9a02 100644
--- a/include/effects/SkTransparentShader.h
+++ b/include/effects/SkTransparentShader.h
@@ -21,6 +21,7 @@
     virtual void    shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
     virtual void    shadeSpan16(int x, int y, uint16_t span[], int count) SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTransparentShader)
 
 private:
diff --git a/include/gpu/GrBackendEffectFactory.h b/include/gpu/GrBackendEffectFactory.h
index 2bfefb9..a291387 100644
--- a/include/gpu/GrBackendEffectFactory.h
+++ b/include/gpu/GrBackendEffectFactory.h
@@ -23,7 +23,7 @@
     of GrGLEffect.
  */
 
-class GrEffect;
+class GrEffectRef;
 class GrEffectStage;
 class GrGLEffect;
 class GrGLCaps;
@@ -44,7 +44,7 @@
     };
 
     virtual EffectKey glEffectKey(const GrEffectStage&, const GrGLCaps&) const = 0;
-    virtual GrGLEffect* createGLInstance(const GrEffect&) const = 0;
+    virtual GrGLEffect* createGLInstance(const GrEffectRef&) const = 0;
 
     bool operator ==(const GrBackendEffectFactory& b) const {
         return fEffectClassID == b.fEffectClassID;
@@ -63,6 +63,7 @@
     GrBackendEffectFactory() {
         fEffectClassID = kIllegalEffectClassID;
     }
+    virtual ~GrBackendEffectFactory() {}
 
     static EffectKey GenID() {
         GR_DEBUGCODE(static const int32_t kClassIDBits = 8 * sizeof(EffectKey) -
diff --git a/include/gpu/GrCacheID.h b/include/gpu/GrCacheID.h
deleted file mode 100644
index e593d7e..0000000
--- a/include/gpu/GrCacheID.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCacheID_DEFINED
-#define GrCacheID_DEFINED
-
-#include "GrTypes.h"
-
-///////////////////////////////////////////////////////////////////////////////
-#define GR_DECLARE_RESOURCE_CACHE_TYPE()                         \
-    static int8_t GetResourceType();
-
-#define GR_DEFINE_RESOURCE_CACHE_TYPE(ClassName)                 \
-    int8_t ClassName::GetResourceType() {                        \
-        static int8_t kResourceTypeID = 0;                       \
-        if (0 == kResourceTypeID) {                              \
-            kResourceTypeID = GrCacheID::GetNextResourceType();  \
-        }                                                        \
-        return kResourceTypeID;                                  \
-    }
-
-
-///////////////////////////////////////////////////////////////////////////////
-#define GR_DECLARE_RESOURCE_CACHE_DOMAIN(AccessorName)           \
-    static int8_t AccessorName();
-
-#define GR_DEFINE_RESOURCE_CACHE_DOMAIN(ClassName, AccessorName) \
-    int8_t ClassName::AccessorName() {                           \
-        static int8_t kDomainID = 0;                             \
-        if (0 == kDomainID) {                                    \
-            kDomainID = GrCacheID::GetNextDomain();              \
-        }                                                        \
-        return kDomainID;                                        \
-    }
-
-/**
- * The cache ID adds structure to the IDs used for caching GPU resources. It
- * is broken into three portions:
- *      the public portion - which is filled in by Skia clients
- *      the private portion - which is used by the cache (domain & type)
- *      the resource-specific portion - which is filled in by each GrResource-
- *              derived class.
- *
- * For the public portion each client of the cache makes up its own
- * unique-per-resource identifier (e.g., bitmap genID). A public ID of
- * 'kScratch_CacheID' indicates that the resource is a "scratch" resource.
- * When used to acquire a resource it indicates the cache user is
- * looking for a resource that matches a resource-subclass-specific set of
- * “dimensions” such as width, height, buffer size, or pixel config, but not
- * for particular resource contents (e.g., texel or vertex values). The public
- * IDs are unique within a private ID value but not necessarily across
- * private IDs.
- *
- * The domain portion identifies the cache client while the type field
- * indicates the resource type. When the public portion indicates that the
- * resource is a scratch resource, the domain field should be kUnrestricted
- * so that scratch resources can be recycled across domains.
- */
-class GrCacheID {
-public:
-    uint64_t     fPublicID;
-
-    uint32_t     fResourceSpecific32;
-
-    uint8_t      fDomain;
-private:
-    uint8_t      fResourceType;
-
-public:
-    uint16_t     fResourceSpecific16;
-
-    GrCacheID(uint8_t resourceType)
-        : fPublicID(kDefaultPublicCacheID)
-        , fDomain(GrCacheData::kScratch_ResourceDomain)
-        , fResourceType(resourceType) {
-    }
-
-    void toRaw(uint32_t v[4]);
-
-    uint8_t getResourceType() const { return fResourceType; }
-
-    /*
-     * Default value for public portion of GrCacheID
-     */
-    static const uint64_t kDefaultPublicCacheID = 0;
-
-    static const uint8_t kInvalid_ResourceType = 0;
-
-    static uint8_t    GetNextDomain();
-    static uint8_t    GetNextResourceType();
-
-
-};
-
-#endif // GrCacheID_DEFINED
diff --git a/include/gpu/GrClipData.h b/include/gpu/GrClipData.h
index 0cf7022..078b61e 100644
--- a/include/gpu/GrClipData.h
+++ b/include/gpu/GrClipData.h
@@ -55,4 +55,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrColor.h b/include/gpu/GrColor.h
index 4318f14..af12ac6 100644
--- a/include/gpu/GrColor.h
+++ b/include/gpu/GrColor.h
@@ -68,4 +68,3 @@
 }
 
 #endif
-
diff --git a/include/gpu/GrConfig.h b/include/gpu/GrConfig.h
index 766212d..d423a2a 100644
--- a/include/gpu/GrConfig.h
+++ b/include/gpu/GrConfig.h
@@ -384,6 +384,14 @@
     #define GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE 0
 #endif
 
+/**
+ * GR_STROKE_PATH_RENDERING controls whether or not the GrStrokePathRenderer can be selected
+ * as a path renderer. GrStrokePathRenderer is currently an experimental path renderer.
+ */
+#if !defined(GR_STROKE_PATH_RENDERING)
+    #define GR_STROKE_PATH_RENDERING                 0
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // tail section:
 //
@@ -433,4 +441,3 @@
 #endif
 
 #endif
-
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 06d2682..dc69766 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -118,45 +118,45 @@
     // Textures
 
     /**
-     *  Create a new entry, based on the specified key and texture, and return
-     *  a "locked" texture. Must call be balanced with an unlockTexture() call.
+     * Creates a new entry, based on the specified key and texture and returns it. The caller owns a
+     * ref on the returned texture which must be balanced by a call to unref.
      *
      * @param params    The texture params used to draw a texture may help determine
      *                  the cache entry used. (e.g. different versions may exist
      *                  for different wrap modes on GPUs with limited NPOT
      *                  texture support). NULL implies clamp wrap modes.
      * @param desc      Description of the texture properties.
-     * @param cacheData Cache-specific properties (e.g., texture gen ID)
+     * @param cacheID Cache-specific properties (e.g., texture gen ID)
      * @param srcData   Pointer to the pixel values.
      * @param rowBytes  The number of bytes between rows of the texture. Zero
      *                  implies tightly packed rows.
      */
     GrTexture* createTexture(const GrTextureParams* params,
                              const GrTextureDesc& desc,
-                             const GrCacheData& cacheData,
+                             const GrCacheID& cacheID,
                              void* srcData, size_t rowBytes);
 
     /**
-     *  Search for an entry based on key and dimensions. If found,
-     *  return it. The return value will be NULL if not found.
+     * Search for an entry based on key and dimensions. If found, ref it and return it. The return
+     * value will be NULL if not found. The caller must balance with a call to unref.
      *
      *  @param desc     Description of the texture properties.
-     *  @param cacheData Cache-specific properties (e.g., texture gen ID)
+     *  @param cacheID Cache-specific properties (e.g., texture gen ID)
      *  @param params   The texture params used to draw a texture may help determine
      *                  the cache entry used. (e.g. different versions may exist
      *                  for different wrap modes on GPUs with limited NPOT
      *                  texture support). NULL implies clamp wrap modes.
      */
-    GrTexture* findTexture(const GrTextureDesc& desc,
-                           const GrCacheData& cacheData,
-                           const GrTextureParams* params);
+    GrTexture* findAndRefTexture(const GrTextureDesc& desc,
+                                 const GrCacheID& cacheID,
+                                 const GrTextureParams* params);
     /**
      * Determines whether a texture is in the cache. If the texture is found it
      * will not be locked or returned. This call does not affect the priority of
      * the texture for deletion.
      */
     bool isTextureInCache(const GrTextureDesc& desc,
-                          const GrCacheData& cacheData,
+                          const GrCacheID& cacheID,
                           const GrTextureParams* params) const;
 
     /**
@@ -183,7 +183,8 @@
      * Returns a texture matching the desc. It's contents are unknown. Subsequent
      * requests with the same descriptor are not guaranteed to return the same
      * texture. The same texture is guaranteed not be returned again until it is
-     * unlocked. Call must be balanced with an unlockTexture() call.
+     * unlocked. Call must be balanced with an unlockTexture() call. The caller
+     * owns a ref on the returned texture and must balance with a call to unref.
      *
      * Textures created by createAndLockTexture() hide the complications of
      * tiling non-power-of-two textures on APIs that don't support this (e.g.
@@ -191,12 +192,11 @@
      * such an API will create gaps in the tiling pattern. This includes clamp
      * mode. (This may be addressed in a future update.)
      */
-    GrTexture* lockScratchTexture(const GrTextureDesc& desc,
-                                  ScratchTexMatch match);
+    GrTexture* lockAndRefScratchTexture(const GrTextureDesc&, ScratchTexMatch match);
 
     /**
-     *  When done with an entry, call unlockTexture(entry) on it, which returns
-     *  it to the cache, where it may be purged.
+     *  When done with an entry, call unlockScratchTexture(entry) on it, which returns
+     *  it to the cache, where it may be purged. This does not unref the texture.
      */
     void unlockScratchTexture(GrTexture* texture);
 
@@ -444,15 +444,12 @@
      * Draws an oval.
      *
      * @param paint         describes how to color pixels.
-     * @param rect          the bounding rect of the oval.
-     * @param strokeWidth   if strokeWidth < 0, then the oval is filled, else
-     *                      the rect is stroked based on strokeWidth. If
-     *                      strokeWidth == 0, then the stroke is always a single
-     *                      pixel thick.
+     * @param oval          the bounding rect of the oval.
+     * @param stroke        the stroke information (width, style)
      */
     void drawOval(const GrPaint& paint,
-                  const GrRect& rect,
-                  SkScalar strokeWidth);
+                  const GrRect& oval,
+                  const SkStrokeRec& stroke);
 
     ///////////////////////////////////////////////////////////////////////////
     // Misc.
@@ -623,22 +620,6 @@
                              const SkRect& rect,
                              float sigmaX, float sigmaY);
 
-    /**
-     * Zooms a subset of the texture to a larger size with a nice edge.
-     * The inner rectangle is a simple scaling of the texture by a factor of
-     * |zoom|.  The outer |inset| pixels transition from the background texture
-     * to the zoomed coordinate system at a rate of
-     * (distance_to_edge / inset) ^2, producing a rounded lens effect.
-     * @param srcTexture      The source texture to be zoomed.
-     * @param dstRect         The destination rectangle.
-     * @param srcRect         The source rectangle.  Must be smaller than
-     *                        dstRect
-     * @param inset           Number of pixels to blend along the edges.
-     * @return the zoomed texture, which is dstTexture.
-     */
-     GrTexture* zoom(GrTexture* srcTexture,
-                     const SkRect& dstRect, const SkRect& srcRect, float inset);
-
     ///////////////////////////////////////////////////////////////////////////
     // Helpers
 
@@ -912,8 +893,11 @@
 
     void internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke);
 
+    void internalDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke);
+    bool canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const;
+
     GrTexture* createResizedTexture(const GrTextureDesc& desc,
-                                    const GrCacheData& cacheData,
+                                    const GrCacheID& cacheID,
                                     void* srcData,
                                     size_t rowBytes,
                                     bool needsFiltering);
@@ -926,14 +910,17 @@
     // for use with textures released from an GrAutoScratchTexture.
     void addExistingTextureToCache(GrTexture* texture);
 
-    bool installPMToUPMEffect(GrTexture* texture,
-                              bool swapRAndB,
-                              const SkMatrix& matrix,
-                              GrEffectStage* stage);
-    bool installUPMToPMEffect(GrTexture* texture,
-                              bool swapRAndB,
-                              const SkMatrix& matrix,
-                              GrEffectStage* stage);
+    /**
+     * These functions create premul <-> unpremul effects if it is possible to generate a pair
+     * of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they
+     * return NULL.
+     */
+    const GrEffectRef* createPMToUPMEffect(GrTexture* texture,
+                                           bool swapRAndB,
+                                           const SkMatrix& matrix);
+    const GrEffectRef* createUPMToPMEffect(GrTexture* texture,
+                                           bool swapRAndB,
+                                           const SkMatrix& matrix);
 
     typedef GrRefCnt INHERITED;
 };
@@ -951,8 +938,7 @@
 
     GrAutoScratchTexture(GrContext* context,
                          const GrTextureDesc& desc,
-                         GrContext::ScratchTexMatch match =
-                            GrContext::kApprox_ScratchTexMatch)
+                         GrContext::ScratchTexMatch match = GrContext::kApprox_ScratchTexMatch)
       : fContext(NULL)
       , fTexture(NULL) {
       this->set(context, desc, match);
@@ -965,6 +951,7 @@
     void reset() {
         if (NULL != fContext && NULL != fTexture) {
             fContext->unlockScratchTexture(fTexture);
+            fTexture->unref();
             fTexture = NULL;
         }
     }
@@ -975,34 +962,36 @@
      * "locked" in the texture cache until it is freed and recycled in
      * GrTexture::internal_dispose. In reality, the texture has been removed
      * from the cache (because this is in AutoScratchTexture) and by not
-     * calling unlockTexture we simply don't re-add it. It will be reattached
-     * in GrTexture::internal_dispose.
+     * calling unlockScratchTexture we simply don't re-add it. It will be
+     * reattached in GrTexture::internal_dispose.
      *
      * Note that the caller is assumed to accept and manage the ref to the
      * returned texture.
      */
     GrTexture* detach() {
-        GrTexture* temp = fTexture;
-
-        // Conceptually the texture's cache entry loses its ref to the
-        // texture while the caller of this method gets a ref.
-        GrAssert(NULL != temp->getCacheEntry());
-
+        GrTexture* texture = fTexture;
         fTexture = NULL;
 
-        temp->setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
-        return temp;
+        // This GrAutoScratchTexture has a ref from lockAndRefScratchTexture, which we give up now.
+        // The cache also has a ref which we are lending to the caller of detach(). When the caller
+        // lets go of the ref and the ref count goes to 0 internal_dispose will see this flag is
+        // set and re-ref the texture, thereby restoring the cache's ref.
+        GrAssert(texture->getRefCnt() > 1);
+        texture->setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
+        texture->unref();
+        GrAssert(NULL != texture->getCacheEntry());
+
+        return texture;
     }
 
     GrTexture* set(GrContext* context,
                    const GrTextureDesc& desc,
-                   GrContext::ScratchTexMatch match =
-                        GrContext::kApprox_ScratchTexMatch) {
+                   GrContext::ScratchTexMatch match = GrContext::kApprox_ScratchTexMatch) {
         this->reset();
 
         fContext = context;
         if (NULL != fContext) {
-            fTexture = fContext->lockScratchTexture(desc, match);
+            fTexture = fContext->lockAndRefScratchTexture(desc, match);
             if (NULL == fTexture) {
                 fContext = NULL;
             }
diff --git a/include/gpu/GrEffect.h b/include/gpu/GrEffect.h
index 0adc00b..7b7cd33 100644
--- a/include/gpu/GrEffect.h
+++ b/include/gpu/GrEffect.h
@@ -8,37 +8,90 @@
 #ifndef GrEffect_DEFINED
 #define GrEffect_DEFINED
 
-#include "GrRefCnt.h"
-#include "GrNoncopyable.h"
+#include "GrColor.h"
 #include "GrEffectUnitTest.h"
+#include "GrNoncopyable.h"
+#include "GrRefCnt.h"
+#include "GrTexture.h"
 #include "GrTextureAccess.h"
 
 class GrBackendEffectFactory;
 class GrContext;
-class GrTexture;
+class GrEffect;
 class SkString;
 
-/** Provides custom vertex shader, fragment shader, uniform data for a
-    particular stage of the Ganesh shading pipeline.
+/**
+ * A Wrapper class for GrEffect. Its ref-count will track owners that may use effects to enqueue
+ * new draw operations separately from ownership within a deferred drawing queue. When the
+ * GrEffectRef ref count reaches zero the scratch GrResources owned by the effect can be recycled
+ * in service of later draws. However, the deferred draw queue may still own direct references to
+ * the underlying GrEffect.
+ */
+class GrEffectRef : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrEffectRef);
+
+    GrEffect* get() { return fEffect; }
+    const GrEffect* get() const { return fEffect; }
+
+    const GrEffect* operator-> () { return fEffect; }
+    const GrEffect* operator-> () const { return fEffect; }
+
+    void* operator new(size_t size);
+    void operator delete(void* target);
+
+private:
+    friend class GrEffect; // to construct these
+
+    explicit GrEffectRef(GrEffect* effect);
+
+    virtual ~GrEffectRef();
+
+    GrEffect* fEffect;
+
+    typedef SkRefCnt INHERITED;
+};
+
+/** Provides custom vertex shader, fragment shader, uniform data for a particular stage of the
+    Ganesh shading pipeline.
     Subclasses must have a function that produces a human-readable name:
         static const char* Name();
-    GrEffect objects *must* be immutable: after being constructed,
-    their fields may not change.  (Immutability isn't actually required
-    until they've been used in a draw call, but supporting that would require
-    setters and getters that could fail, copy-on-write, or deep copying of these
-    objects when they're stored by a GrGLEffect.)
-  */
-class GrEffect : public GrRefCnt {
+    GrEffect objects *must* be immutable: after being constructed, their fields may not change.
 
+    GrEffect subclass objects should be created by factory functions that return GrEffectRef.
+    There is no public way to wrap a GrEffect in a GrEffectRef. Thus, a factory should be a static
+    member function of a GrEffect subclass.
+
+    Because almost no code should ever handle a GrEffect outside of a GrEffectRef, we privately
+    inherit from GrRefCnt to help prevent accidental direct ref'ing/unref'ing of effects.
+  */
+class GrEffect : private GrRefCnt {
 public:
     SK_DECLARE_INST_COUNT(GrEffect)
 
-    explicit GrEffect(int numTextures);
     virtual ~GrEffect();
 
-    /** If given an input texture that is/is not opaque, is this
-        effect guaranteed to produce an opaque output? */
-    virtual bool isOpaque(bool inputTextureIsOpaque) const;
+    /**
+     * Flags for getConstantColorComponents. They are defined so that the bit order reflects the
+     * GrColor shift order.
+     */
+    enum ValidComponentFlags {
+        kR_ValidComponentFlag = 1 << (GrColor_SHIFT_R / 8),
+        kG_ValidComponentFlag = 1 << (GrColor_SHIFT_G / 8),
+        kB_ValidComponentFlag = 1 << (GrColor_SHIFT_B / 8),
+        kA_ValidComponentFlag = 1 << (GrColor_SHIFT_A / 8),
+
+        kAll_ValidComponentFlags = (kR_ValidComponentFlag | kG_ValidComponentFlag |
+                                    kB_ValidComponentFlag | kA_ValidComponentFlag)
+    };
+
+    /**
+     * This function is used to perform optimizations. When called the color and validFlags params
+     * indicate whether the input components to this effect in the FS will have known values. The
+     * function updates both params to indicate known values of its output. A component of the color
+     * param only has meaning if the corresponding bit in validFlags is set.
+     */
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const = 0;
 
     /** This object, besides creating back-end-specific helper objects, is used for run-time-type-
         identification. The factory should be an instance of templated class,
@@ -57,41 +110,152 @@
      */
     virtual const GrBackendEffectFactory& getFactory() const = 0;
 
-    /** Returns true if the other effect will generate identical output.
-        Must only be called if the two are already known to be of the
-        same type (i.e.  they return the same value from getFactory()).
+    /** Returns true if this and other effect conservatively draw identically. It can only return
+        true when the two effects are of the same subclass (i.e. they return the same object from
+        from getFactory()).
 
-        Equality is not the same thing as equivalence.
-        To test for equivalence (that they will generate the same
-        shader code, but may have different uniforms), check equality
-        of the EffectKey produced by the GrBackendEffectFactory:
-        a.getFactory().glEffectKey(a) == b.getFactory().glEffectKey(b).
-
-        The default implementation of this function returns true iff
-        the two stages have the same return value for numTextures() and
-        for texture() over all valid indices.
+        A return value of true from isEqual() should not be used to test whether the effects would
+        generate the same shader code. To test for identical code generation use the EffectKey
+        computed by the GrBackendEffectFactory:
+            effectA.getFactory().glEffectKey(effectA) == effectB.getFactory().glEffectKey(effectB).
      */
-    virtual bool isEqual(const GrEffect&) const;
+    bool isEqual(const GrEffectRef& other) const {
+        return this->isEqual(*other.get());
+    }
 
     /** Human-meaningful string to identify this effect; may be embedded
         in generated shader code. */
     const char* name() const;
 
-    int numTextures() const { return fNumTextures; }
+    int numTextures() const { return fTextureAccesses.count(); }
 
     /** Returns the access pattern for the texture at index. index must be valid according to
         numTextures(). */
-    virtual const GrTextureAccess& textureAccess(int index) const;
+    const GrTextureAccess& textureAccess(int index) const { return *fTextureAccesses[index]; }
 
     /** Shortcut for textureAccess(index).texture(); */
     GrTexture* texture(int index) const { return this->textureAccess(index).getTexture(); }
 
+    /** Useful for effects that want to insert a texture matrix that is implied by the texture
+        dimensions */
+    static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) {
+        GrAssert(NULL != texture);
+        SkMatrix mat;
+        mat.setIDiv(texture->width(), texture->height());
+        return mat;
+    }
+
     void* operator new(size_t size);
     void operator delete(void* target);
 
+    /** These functions are used when recording effects into a deferred drawing queue. The inc call
+        keeps the effect alive outside of GrEffectRef while allowing any resources owned by the
+        effect to be returned to the cache for reuse. The dec call must balance the inc call. */
+    void incDeferredRefCounts() const {
+        this->ref();
+        int count = fTextureAccesses.count();
+        for (int t = 0; t < count; ++t) {
+            fTextureAccesses[t]->getTexture()->incDeferredRefCount();
+        }
+    }
+    void decDeferredRefCounts() const {
+        int count = fTextureAccesses.count();
+        for (int t = 0; t < count; ++t) {
+            fTextureAccesses[t]->getTexture()->decDeferredRefCount();
+        }
+        this->unref();
+    }
+
+protected:
+    /**
+     * Subclasses call this from their constructor to register GrTextureAcceses. The effect subclass
+     * manages the lifetime of the accesses (this function only stores a pointer). This must only be
+     * called from the constructor because GrEffects are supposed to be immutable.
+     */
+    void addTextureAccess(const GrTextureAccess* textureAccess);
+
+    GrEffect() : fEffectRef(NULL) {};
+
+    /** This should be called by GrEffect subclass factories. See the comment on AutoEffectUnref for
+        an example factory function. */
+    static GrEffectRef* CreateEffectRef(GrEffect* effect) {
+        if (NULL == effect->fEffectRef) {
+            effect->fEffectRef = SkNEW_ARGS(GrEffectRef, (effect));
+        } else {
+            effect->fEffectRef->ref();
+        }
+        return effect->fEffectRef;
+    }
+
+    static const GrEffectRef* CreateEffectRef(const GrEffect* effect) {
+        return CreateEffectRef(const_cast<GrEffect*>(effect));
+    }
+
+    /** Helper used in subclass factory functions to unref the effect after it has been wrapped in a
+        GrEffectRef. E.g.:
+
+        class EffectSubclass : public GrEffect {
+        public:
+            GrEffectRef* Create(ParamType1 param1, ParamType2 param2, ...) {
+                AutoEffectUnref effect(SkNEW_ARGS(EffectSubclass, (param1, param2, ...)));
+                return CreateEffectRef(effect);
+            }
+     */
+    class AutoEffectUnref {
+    public:
+        AutoEffectUnref(GrEffect* effect) : fEffect(effect) { }
+        ~AutoEffectUnref() { fEffect->unref(); }
+        operator GrEffect*() { return fEffect; }
+    private:
+        GrEffect* fEffect;
+    };
+
+    /** Helper for getting the GrEffect out of a GrEffectRef and down-casting to a GrEffect subclass
+      */
+    template <typename T>
+    static const T& CastEffect(const GrEffect& effectRef) {
+        return *static_cast<const T*>(&effectRef);
+    }
+
 private:
-    int fNumTextures;
+    bool isEqual(const GrEffect& other) const {
+        if (&this->getFactory() != &other.getFactory()) {
+            return false;
+        }
+        bool result = this->onIsEqual(other);
+#if GR_DEBUG
+        if (result) {
+            GrAssert(this->numTextures() == other.numTextures());
+            for (int i = 0; i < this->numTextures(); ++i) {
+                GrAssert(*fTextureAccesses[i] == *other.fTextureAccesses[i]);
+            }
+        }
+#endif
+        return result;
+    }
+
+    /** Subclass implements this to support isEqual(). It will only be called if it is known that
+        the two effects are of the same subclass (i.e. they return the same object from
+        getFactory()).*/
+    virtual bool onIsEqual(const GrEffect& other) const = 0;
+
+    void EffectRefDestroyed() { fEffectRef = NULL; }
+
+    friend class GrEffectRef;   // to call EffectRefDestroyed()
+    friend class GrEffectStage; // to rewrap GrEffect in GrEffectRef when restoring an effect-stage
+                                // from deferred state, to call isEqual on naked GrEffects, and
+                                // to inc/dec deferred ref counts.
+
+    SkSTArray<4, const GrTextureAccess*, true>  fTextureAccesses;
+    GrEffectRef*                                fEffectRef;
+
     typedef GrRefCnt INHERITED;
 };
 
+inline GrEffectRef::GrEffectRef(GrEffect* effect) {
+    GrAssert(NULL != effect);
+    effect->ref();
+    fEffect = effect;
+}
+
 #endif
diff --git a/include/gpu/GrEffectStage.h b/include/gpu/GrEffectStage.h
index e42f198..05bc313 100644
--- a/include/gpu/GrEffectStage.h
+++ b/include/gpu/GrEffectStage.h
@@ -20,30 +20,25 @@
 
 class GrEffectStage {
 public:
-
     GrEffectStage()
-    : fEffect (NULL) {
+    : fEffectRef (NULL) {
         GR_DEBUGCODE(fSavedCoordChangeCnt = 0;)
     }
 
     ~GrEffectStage() {
-        GrSafeUnref(fEffect);
+        GrSafeUnref(fEffectRef);
         GrAssert(0 == fSavedCoordChangeCnt);
     }
 
     bool operator ==(const GrEffectStage& other) const {
         // first handle cases where one or the other has no effect
-        if (NULL == fEffect) {
-            return NULL == other.fEffect;
-        } else if (NULL == other.fEffect) {
+        if (NULL == fEffectRef) {
+            return NULL == other.fEffectRef;
+        } else if (NULL == other.fEffectRef) {
             return false;
         }
 
-        if (fEffect->getFactory() != other.fEffect->getFactory()) {
-            return false;
-        }
-
-        if (!fEffect->isEqual(*other.fEffect)) {
+        if (!(*this->getEffect())->isEqual(*other.getEffect())) {
             return false;
         }
 
@@ -53,8 +48,8 @@
     bool operator !=(const GrEffectStage& s) const { return !(*this == s); }
 
     GrEffectStage& operator =(const GrEffectStage& other) {
-        GrSafeAssign(fEffect, other.fEffect);
-        if (NULL != fEffect) {
+        GrSafeAssign(fEffectRef, other.fEffectRef);
+        if (NULL != fEffectRef) {
             fCoordChangeMatrix = other.fCoordChangeMatrix;
         }
         return *this;
@@ -70,7 +65,7 @@
     class SavedCoordChange {
     private:
         SkMatrix fCoordChangeMatrix;
-        GR_DEBUGCODE(mutable SkAutoTUnref<const GrEffect> fEffect;)
+        GR_DEBUGCODE(mutable SkAutoTUnref<const GrEffectRef> fEffectRef;)
 
         friend class GrEffectStage;
     };
@@ -83,9 +78,9 @@
      */
     void saveCoordChange(SavedCoordChange* savedCoordChange) const {
         savedCoordChange->fCoordChangeMatrix = fCoordChangeMatrix;
-        GrAssert(NULL == savedCoordChange->fEffect.get());
-        GR_DEBUGCODE(GrSafeRef(fEffect);)
-        GR_DEBUGCODE(savedCoordChange->fEffect.reset(fEffect);)
+        GrAssert(NULL == savedCoordChange->fEffectRef.get());
+        GR_DEBUGCODE(GrSafeRef(fEffectRef);)
+        GR_DEBUGCODE(savedCoordChange->fEffectRef.reset(fEffectRef);)
         GR_DEBUGCODE(++fSavedCoordChangeCnt);
     }
 
@@ -94,36 +89,93 @@
      */
     void restoreCoordChange(const SavedCoordChange& savedCoordChange) {
         fCoordChangeMatrix = savedCoordChange.fCoordChangeMatrix;
-        GrAssert(savedCoordChange.fEffect.get() == fEffect);
+        GrAssert(savedCoordChange.fEffectRef.get() == fEffectRef);
         GR_DEBUGCODE(--fSavedCoordChangeCnt);
-        GR_DEBUGCODE(savedCoordChange.fEffect.reset(NULL);)
+        GR_DEBUGCODE(savedCoordChange.fEffectRef.reset(NULL);)
     }
 
     /**
+     * Used when storing a deferred GrDrawState. The DeferredStage allows resources owned by its
+     * GrEffect to be recycled through the cache.
+     */
+    class DeferredStage {
+    public:
+        DeferredStage() : fEffect(NULL) {
+            SkDEBUGCODE(fInitialized = false;)
+        }
+
+        ~DeferredStage() {
+            if (NULL != fEffect) {
+                fEffect->decDeferredRefCounts();
+            }
+        }
+
+        void saveFrom(const GrEffectStage& stage) {
+            GrAssert(!fInitialized);
+            if (NULL != stage.fEffectRef) {
+                stage.fEffectRef->get()->incDeferredRefCounts();
+                fEffect = stage.fEffectRef->get();
+                fCoordChangeMatrix = stage.fCoordChangeMatrix;
+            }
+            SkDEBUGCODE(fInitialized = true;)
+        }
+
+        void restoreTo(GrEffectStage* stage) {
+            GrAssert(fInitialized);
+            const GrEffectRef* oldEffectRef = stage->fEffectRef;
+            if (NULL != fEffect) {
+                stage->fEffectRef = GrEffect::CreateEffectRef(fEffect);
+                stage->fCoordChangeMatrix = fCoordChangeMatrix;
+            } else {
+                stage->fEffectRef = NULL;
+            }
+            SkSafeUnref(oldEffectRef);
+        }
+
+        bool isEqual(const GrEffectStage& stage) const {
+            if (NULL == stage.fEffectRef) {
+                return NULL == fEffect;
+            } else if (NULL == fEffect) {
+                return false;
+            }
+
+            if (!(*stage.getEffect())->isEqual(*fEffect)) {
+                return false;
+            }
+
+            return fCoordChangeMatrix == stage.fCoordChangeMatrix;
+        }
+
+    private:
+        const GrEffect*               fEffect;
+        SkMatrix                      fCoordChangeMatrix;
+        SkDEBUGCODE(bool fInitialized;)
+    };
+
+    /**
      * Gets the matrix representing all changes of coordinate system since the GrEffect was
      * installed in the stage.
      */
     const SkMatrix& getCoordChangeMatrix() const { return fCoordChangeMatrix; }
 
     void reset() {
-        GrSafeSetNull(fEffect);
+        GrSafeSetNull(fEffectRef);
     }
 
-    const GrEffect* setEffect(const GrEffect* effect) {
+    const GrEffectRef* setEffect(const GrEffectRef* EffectRef) {
         GrAssert(0 == fSavedCoordChangeCnt);
-        GrSafeAssign(fEffect, effect);
+        GrSafeAssign(fEffectRef, EffectRef);
         fCoordChangeMatrix.reset();
-        return effect;
+        return EffectRef;
     }
 
-    const GrEffect* getEffect() const { return fEffect; }
+    const GrEffectRef* getEffect() const { return fEffectRef; }
 
 private:
     SkMatrix            fCoordChangeMatrix;
-    const GrEffect*     fEffect;
+    const GrEffectRef*  fEffectRef;
 
     GR_DEBUGCODE(mutable int fSavedCoordChangeCnt;)
 };
 
 #endif
-
diff --git a/include/gpu/GrEffectUnitTest.h b/include/gpu/GrEffectUnitTest.h
index 8cc2689..8b65e97 100644
--- a/include/gpu/GrEffectUnitTest.h
+++ b/include/gpu/GrEffectUnitTest.h
@@ -31,22 +31,22 @@
 #if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
 
 class GrContext;
-class GrEffect;
+class GrEffectRef;
 class GrTexture;
 
 class GrEffectTestFactory : GrNoncopyable {
 public:
 
-    typedef GrEffect* (*CreateProc)(SkRandom*, GrContext*, GrTexture* dummyTextures[]);
+    typedef GrEffectRef* (*CreateProc)(SkRandom*, GrContext*, GrTexture* dummyTextures[]);
 
     GrEffectTestFactory(CreateProc createProc) {
         fCreateProc = createProc;
         GetFactories()->push_back(this);
     }
 
-    static GrEffect* CreateStage(SkRandom* random,
-                                      GrContext* context,
-                                      GrTexture* dummyTextures[]) {
+    static GrEffectRef* CreateStage(SkRandom* random,
+                                    GrContext* context,
+                                    GrTexture* dummyTextures[]) {
         uint32_t idx = random->nextRangeU(0, GetFactories()->count() - 1);
         GrEffectTestFactory* factory = (*GetFactories())[idx];
         return factory->fCreateProc(random, context, dummyTextures);
@@ -62,7 +62,7 @@
  */
 #define GR_DECLARE_EFFECT_TEST                                                      \
     static GrEffectTestFactory gTestFactory;                                        \
-    static GrEffect* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
+    static GrEffectRef* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
 
 /** GrEffect subclasses should insert this macro in their implementation file. They must then
  *  also implement this static function:
@@ -80,7 +80,7 @@
 // The unit test relies on static initializers. Just declare the TestCreate function so that
 // its definitions will compile.
 #define GR_DECLARE_EFFECT_TEST \
-    static GrEffect* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
+    static GrEffectRef* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
 #define GR_DEFINE_EFFECT_TEST(X)
 
 #endif // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
diff --git a/include/gpu/GrFontScaler.h b/include/gpu/GrFontScaler.h
index a69e912..e3a7a2a 100644
--- a/include/gpu/GrFontScaler.h
+++ b/include/gpu/GrFontScaler.h
@@ -39,4 +39,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrGlyph.h b/include/gpu/GrGlyph.h
index 956404e..4945f2a 100644
--- a/include/gpu/GrGlyph.h
+++ b/include/gpu/GrGlyph.h
@@ -79,4 +79,3 @@
 
 
 #endif
-
diff --git a/include/gpu/GrInstanceCounter.h b/include/gpu/GrInstanceCounter.h
deleted file mode 100644
index b3e21d2..0000000
--- a/include/gpu/GrInstanceCounter.h
+++ /dev/null
@@ -1,40 +0,0 @@
-
-/*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-
-#ifndef GrInstanceCounter_DEFINED
-#define GrInstanceCounter_DEFINED
-
-#include "GrTypes.h"
-
-template <typename T> class GrInstanceCounter {
-public:
-    GrInstanceCounter() {
-        ++gCounter;
-        GrPrintf("+ %s %d\n", T::InstanceCounterClassName(), gCounter);
-    }
-
-    ~GrInstanceCounter() {
-        --gCounter;
-        GrPrintf("- %s %d\n", T::InstanceCounterClassName(), gCounter);
-    }
-
-private:
-    static int gCounter;
-};
-
-template <typename T> int GrInstanceCounter<T>::gCounter;
-
-#define DECLARE_INSTANCE_COUNTER(T)                                 \
-    static const char* InstanceCounterClassName() { return #T; }    \
-    friend class GrInstanceCounter<T>;                              \
-    GrInstanceCounter<T> fInstanceCounter
-
-#endif
-
diff --git a/include/gpu/GrKey.h b/include/gpu/GrKey.h
index 17d00de..80eb537 100644
--- a/include/gpu/GrKey.h
+++ b/include/gpu/GrKey.h
@@ -41,4 +41,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrNoncopyable.h b/include/gpu/GrNoncopyable.h
index cad722f..3d47170 100644
--- a/include/gpu/GrNoncopyable.h
+++ b/include/gpu/GrNoncopyable.h
@@ -28,4 +28,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrPoint.h b/include/gpu/GrPoint.h
index 302c4e1..c987b01 100644
--- a/include/gpu/GrPoint.h
+++ b/include/gpu/GrPoint.h
@@ -28,4 +28,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrRect.h b/include/gpu/GrRect.h
index 42ddd8e..821e137 100644
--- a/include/gpu/GrRect.h
+++ b/include/gpu/GrRect.h
@@ -34,4 +34,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrRefCnt.h b/include/gpu/GrRefCnt.h
index 50c32c6..31aa80d 100644
--- a/include/gpu/GrRefCnt.h
+++ b/include/gpu/GrRefCnt.h
@@ -31,4 +31,3 @@
 }
 
 #endif
-
diff --git a/include/gpu/GrRenderTarget.h b/include/gpu/GrRenderTarget.h
index 9964c9b..19a37a5 100644
--- a/include/gpu/GrRenderTarget.h
+++ b/include/gpu/GrRenderTarget.h
@@ -139,10 +139,11 @@
 
 protected:
     GrRenderTarget(GrGpu* gpu,
+                   bool isWrapped,
                    GrTexture* texture,
                    const GrTextureDesc& desc,
-                   Origin origin)
-        : INHERITED(gpu, desc, origin)
+                   GrSurfaceOrigin origin)
+        : INHERITED(gpu, isWrapped, desc, origin)
         , fStencilBuffer(NULL)
         , fTexture(texture) {
         fResolveRect.setLargestInverted();
diff --git a/include/gpu/GrResource.h b/include/gpu/GrResource.h
index 3c306f8..72e6928 100644
--- a/include/gpu/GrResource.h
+++ b/include/gpu/GrResource.h
@@ -57,20 +57,28 @@
      */
     virtual size_t sizeInBytes() const = 0;
 
-     /**
-      * Retrieves the context that owns the resource. Note that it is possible
-      * for this to return NULL. When resources have been release()ed or
-      * abandon()ed they no longer have an owning context. Destroying a
-      * GrContext automatically releases all its resources.
-      */
+    /**
+     * Retrieves the context that owns the resource. Note that it is possible
+     * for this to return NULL. When resources have been release()ed or
+     * abandon()ed they no longer have an owning context. Destroying a
+     * GrContext automatically releases all its resources.
+     */
     const GrContext* getContext() const;
     GrContext* getContext();
 
     void setCacheEntry(GrResourceEntry* cacheEntry) { fCacheEntry = cacheEntry; }
     GrResourceEntry* getCacheEntry() { return fCacheEntry; }
 
+    void incDeferredRefCount() const { GrAssert(fDeferredRefCount >= 0); ++fDeferredRefCount; }
+    void decDeferredRefCount() const { GrAssert(fDeferredRefCount > 0); --fDeferredRefCount; }
+
 protected:
-    explicit GrResource(GrGpu* gpu);
+    /**
+     * isWrapped indicates we have wrapped a client-created backend resource in a GrResource. If it
+     * is true then the client is responsible for the lifetime of the underlying backend resource.
+     * Otherwise, our onRelease() should free the resource.
+     */
+    GrResource(GrGpu* gpu, bool isWrapped);
     virtual ~GrResource();
 
     GrGpu* getGpu() const { return fGpu; }
@@ -81,22 +89,27 @@
     virtual void onAbandon() {};
 
     bool isInCache() const { return NULL != fCacheEntry; }
+    bool isWrapped() const { return kWrapped_Flag & fFlags; }
 
 private:
-
 #if GR_DEBUG
     friend class GrGpu; // for assert in GrGpu to access getGpu
 #endif
 
-    GrGpu*      fGpu;       // not reffed. The GrGpu can be deleted while there
-                            // are still live GrResources. It will call
-                            // release() on all such resources in its
-                            // destructor.
-
     // We're in an internal doubly linked list
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResource);
 
-    GrResourceEntry* fCacheEntry;  // NULL if not in cache
+    GrGpu*              fGpu;               // not reffed. The GrGpu can be deleted while there
+                                            // are still live GrResources. It will call
+                                            // release() on all such resources in its
+                                            // destructor.
+    GrResourceEntry*    fCacheEntry;        // NULL if not in cache
+    mutable int         fDeferredRefCount;  // How many references in deferred drawing buffers.
+
+    enum Flags {
+        kWrapped_Flag = 0x1,
+    };
+    uint32_t         fFlags;
 
     typedef GrRefCnt INHERITED;
 };
diff --git a/include/gpu/GrSurface.h b/include/gpu/GrSurface.h
index d7aa267..3429cc6 100644
--- a/include/gpu/GrSurface.h
+++ b/include/gpu/GrSurface.h
@@ -33,19 +33,8 @@
      */
     int height() const { return fDesc.fHeight; }
 
-    /**
-     * Some surfaces will be stored such that the upper and left edges of the content meet at the
-     * the origin (in texture coord space) and for other surfaces the lower and left edges meet at
-     * the origin. Render-targets are always consistent with the convention of the underlying
-     * backend API to make it easier to mix native backend rendering with Skia rendering. Wrapped
-     * backend surfaces always use the backend's convention as well.
-     */
-    enum Origin {
-        kTopLeft_Origin,
-        kBottomLeft_Origin,
-    };
-    Origin origin() const {
-        GrAssert(kTopLeft_Origin == fOrigin || kBottomLeft_Origin == fOrigin);
+    GrSurfaceOrigin origin() const {
+        GrAssert(kTopLeft_GrSurfaceOrigin == fOrigin || kBottomLeft_GrSurfaceOrigin == fOrigin);
         return fOrigin;
     }
 
@@ -115,8 +104,8 @@
                              uint32_t pixelOpsFlags = 0) = 0;
 
 protected:
-    GrSurface(GrGpu* gpu, const GrTextureDesc& desc, Origin origin)
-    : INHERITED(gpu)
+    GrSurface(GrGpu* gpu, bool isWrapped, const GrTextureDesc& desc, GrSurfaceOrigin origin)
+    : INHERITED(gpu, isWrapped)
     , fDesc(desc)
     , fOrigin(origin) {
     }
@@ -124,7 +113,7 @@
     GrTextureDesc fDesc;
 
 private:
-    Origin fOrigin;
+    GrSurfaceOrigin fOrigin;
 
     typedef GrResource INHERITED;
 };
diff --git a/include/gpu/GrTBackendEffectFactory.h b/include/gpu/GrTBackendEffectFactory.h
index 52dbc64..7ea7e39 100644
--- a/include/gpu/GrTBackendEffectFactory.h
+++ b/include/gpu/GrTBackendEffectFactory.h
@@ -34,7 +34,7 @@
                                   const GrGLCaps& caps) const SK_OVERRIDE {
         GrAssert(kIllegalEffectClassID != fEffectClassID);
         EffectKey effectKey = GLEffect::GenKey(stage, caps);
-        EffectKey textureKey = GLEffect::GenTextureKey(*stage.getEffect(), caps);
+        EffectKey textureKey = GLEffect::GenTextureKey(stage.getEffect(), caps);
 #if GR_DEBUG
         static const EffectKey kIllegalIDMask = (uint16_t) (~((1U << kEffectKeyBits) - 1));
         GrAssert(!(kIllegalIDMask & effectKey));
@@ -48,7 +48,7 @@
     /** Returns a new instance of the appropriate *GL* implementation class
         for the given GrEffect; caller is responsible for deleting
         the object. */
-    virtual GLEffect* createGLInstance(const GrEffect& effect) const SK_OVERRIDE {
+    virtual GLEffect* createGLInstance(const GrEffectRef& effect) const SK_OVERRIDE {
         return SkNEW_ARGS(GLEffect, (*this, effect));
     }
 
diff --git a/include/gpu/GrTextContext.h b/include/gpu/GrTextContext.h
index 6b28b79..fc7735d 100644
--- a/include/gpu/GrTextContext.h
+++ b/include/gpu/GrTextContext.h
@@ -61,4 +61,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index d41be11..94d5788 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -10,7 +10,6 @@
 #define GrTexture_DEFINED
 
 #include "GrSurface.h"
-#include "GrCacheID.h"
 
 class GrRenderTarget;
 class GrResourceKey;
@@ -20,8 +19,6 @@
 
 public:
     SK_DECLARE_INST_COUNT(GrTexture)
-    GR_DECLARE_RESOURCE_CACHE_TYPE()
-
     // from GrResource
     /**
      * Informational texture flags
@@ -130,15 +127,12 @@
 #else
     void validate() const {}
 #endif
-
     static GrResourceKey ComputeKey(const GrGpu* gpu,
-                                    const GrTextureParams* sampler,
+                                    const GrTextureParams* params,
                                     const GrTextureDesc& desc,
-                                    const GrCacheData& cacheData,
-                                    bool scratch);
-
+                                    const GrCacheID& cacheID);
+    static GrResourceKey ComputeScratchKey(const GrTextureDesc& desc);
     static bool NeedsResizing(const GrResourceKey& key);
-    static bool IsScratchTexture(const GrResourceKey& key);
     static bool NeedsFiltering(const GrResourceKey& key);
 
 protected:
@@ -146,8 +140,8 @@
                                    // base class cons sets to NULL
                                    // subclass cons can create and set
 
-    GrTexture(GrGpu* gpu, const GrTextureDesc& desc, Origin origin)
-    : INHERITED(gpu, desc, origin)
+    GrTexture(GrGpu* gpu, bool isWrapped, const GrTextureDesc& desc, GrSurfaceOrigin origin)
+    : INHERITED(gpu, isWrapped, desc, origin)
     , fRenderTarget(NULL) {
 
         // only make sense if alloc size is pow2
@@ -173,4 +167,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index 21ae6de..055750d 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -425,6 +425,17 @@
     kGrColorTableSize = 256 * 4 //sizeof(GrColor)
 };
 
+/**
+ * Some textures will be stored such that the upper and left edges of the content meet at the
+ * the origin (in texture coord space) and for other textures the lower and left edges meet at
+ * the origin. Render-targets are always consistent with the convention of the underlying
+ * backend API to make it easier to mix native backend rendering with Skia rendering.
+ */
+
+enum GrSurfaceOrigin {
+    kBottomLeft_GrSurfaceOrigin,
+    kTopLeft_GrSurfaceOrigin,
+};
 
 /**
  * Describes a texture to be created.
@@ -459,44 +470,55 @@
 };
 
 /**
- * GrCacheData holds user-provided cache-specific data. It is used in
- * combination with the GrTextureDesc to construct a cache key for texture
- * resources.
+ * GrCacheID is used create and find cached GrResources (e.g. GrTextures). The ID has two parts:
+ * the domain and the key. Domains simply allow multiple clients to use 0-based indices as their
+ * cache key without colliding. The key uniquely identifies a GrResource within the domain.
+ * Users of the cache must obtain a domain via GenerateDomain().
  */
-struct GrCacheData {
-    /*
-     * Scratch textures should all have this value as their fClientCacheID
-     */
-    static const uint64_t kScratch_CacheID = 0xBBBBBBBB;
+struct GrCacheID {
+public:
+    typedef uint8_t  Domain;
+
+    struct Key {
+        union {
+            uint8_t  fData8[16];
+            uint32_t fData32[4];
+            uint64_t fData64[2];
+        };
+    };
 
     /**
-      * Resources in the "scratch" domain can be used by any domain. All
-      * scratch textures will have this as their domain.
-      */
-    static const uint8_t kScratch_ResourceDomain = 0;
+     * A default cache ID is invalid; a set method must be called before the object is used.
+     */
+    GrCacheID() { fDomain = kInvalid_Domain; }
 
-
-    // No default constructor is provided since, if you are creating one
-    // of these, you should definitely have a key (or be using the scratch
-    // key).
-    GrCacheData(uint64_t key)
-    : fClientCacheID(key)
-    , fResourceDomain(kScratch_ResourceDomain) {
+    /**
+     * Initialize the cache ID to a domain and key.
+     */
+    GrCacheID(Domain domain, const Key& key) {
+        GrAssert(kInvalid_Domain != domain);
+        this->reset(domain, key);
     }
 
-    /**
-     * A user-provided texture ID. It should be unique to the texture data and
-     * does not need to take into account the width or height. Two textures
-     * with the same ID but different dimensions will not collide. This field
-     * is only relevant for textures that will be cached.
-     */
-    uint64_t               fClientCacheID;
+    void reset(Domain domain, const Key& key) {
+        fDomain = domain;
+        memcpy(&fKey, &key, sizeof(Key));
+    }
 
-    /**
-     * Allows cache clients to cluster their textures inside domains (e.g.,
-     * alpha clip masks). Only relevant for cached textures.
-     */
-    uint8_t                fResourceDomain;
+    /** Has this been initialized to a valid domain */
+    bool isValid() const { return kInvalid_Domain != fDomain; }
+
+    const Key& getKey() const { GrAssert(this->isValid()); return fKey; }
+    Domain getDomain() const { GrAssert(this->isValid()); return fDomain; }
+
+    /** Creates a new unique ID domain. */
+    static Domain GenerateDomain();
+
+private:
+    Key             fKey;
+    Domain          fDomain;
+
+    static const Domain kInvalid_Domain = 0;
 };
 
 /**
@@ -585,6 +607,7 @@
 struct GrBackendTextureDesc {
     GrBackendTextureDesc() { memset(this, 0, sizeof(*this)); }
     GrBackendTextureFlags           fFlags;
+    GrSurfaceOrigin                 fOrigin;
     int                             fWidth;         //<! width in pixels
     int                             fHeight;        //<! height in pixels
     GrPixelConfig                   fConfig;        //<! color format
@@ -653,7 +676,4 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// this is included only to make it easy to use this debugging facility
-#include "GrInstanceCounter.h"
-
 #endif
diff --git a/include/gpu/GrUserConfig.h b/include/gpu/GrUserConfig.h
index 3eb77a6..ff10044 100644
--- a/include/gpu/GrUserConfig.h
+++ b/include/gpu/GrUserConfig.h
@@ -68,5 +68,3 @@
 #define GR_TEXT_SCALAR_IS_FLOAT     1
 
 #endif
-
-
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 1e78b4a..a6087c8 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -64,6 +64,8 @@
                             const SkPoint[], const SkPaint& paint) SK_OVERRIDE;
     virtual void drawRect(const SkDraw&, const SkRect& r,
                           const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawOval(const SkDraw&, const SkRect& oval,
+                          const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPath(const SkDraw&, const SkPath& path,
                           const SkPaint& paint, const SkMatrix* prePathMatrix,
                           bool pathIsMutable) SK_OVERRIDE;
@@ -110,9 +112,6 @@
     class SkAutoCachedTexture; // used internally
 
 protected:
-    bool isBitmapInTextureCache(const SkBitmap& bitmap,
-                                const GrTextureParams& params) const;
-
     // overrides from SkDevice
     virtual bool onReadPixels(const SkBitmap& bitmap,
                               int x, int y,
@@ -184,4 +183,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 88ca08a..cf37f47 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -75,11 +75,11 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrTexture* GrLockCachedBitmapTexture(GrContext*,
-                                     const SkBitmap&,
-                                     const GrTextureParams*);
+bool GrIsBitmapInCache(const GrContext*, const SkBitmap&, const GrTextureParams*);
 
-void GrUnlockCachedBitmapTexture(GrTexture*);
+GrTexture* GrLockAndRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams*);
+
+void GrUnlockAndUnrefCachedBitmapTexture(GrTexture*);
 
 ////////////////////////////////////////////////////////////////////////////////
 // Classes
diff --git a/include/gpu/SkGrPixelRef.h b/include/gpu/SkGrPixelRef.h
index 0a32f21..ec1e22e 100644
--- a/include/gpu/SkGrPixelRef.h
+++ b/include/gpu/SkGrPixelRef.h
@@ -68,4 +68,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/SkGrTexturePixelRef.h b/include/gpu/SkGrTexturePixelRef.h
index ffa4bef..7129512 100644
--- a/include/gpu/SkGrTexturePixelRef.h
+++ b/include/gpu/SkGrTexturePixelRef.h
@@ -17,4 +17,3 @@
 typedef SkGrPixelRef SkGrRenderTargetPixelRef;
 
 #endif
-
diff --git a/include/gpu/gl/SkDebugGLContext.h b/include/gpu/gl/SkDebugGLContext.h
index c51f54c..6e29b67 100644
--- a/include/gpu/gl/SkDebugGLContext.h
+++ b/include/gpu/gl/SkDebugGLContext.h
@@ -24,4 +24,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/gl/SkNullGLContext.h b/include/gpu/gl/SkNullGLContext.h
index 9e16cee..83213a3 100644
--- a/include/gpu/gl/SkNullGLContext.h
+++ b/include/gpu/gl/SkNullGLContext.h
@@ -24,4 +24,3 @@
 };
 
 #endif
-
diff --git a/include/images/SkImageRef.h b/include/images/SkImageRef.h
index dcc4c0e..bca4305 100644
--- a/include/images/SkImageRef.h
+++ b/include/images/SkImageRef.h
@@ -34,7 +34,7 @@
         @param config The preferred config of the decoded bitmap.
         @param sampleSize Requested sampleSize for decoding. Defaults to 1.
     */
-    SkImageRef(SkStream*, SkBitmap::Config config, int sampleSize = 1);
+    SkImageRef(SkStream*, SkBitmap::Config config, int sampleSize = 1, SkBaseMutex* mutex = NULL);
     virtual ~SkImageRef();
 
     /** this value is passed onto the decoder. Default is true
@@ -73,9 +73,9 @@
 
     virtual void* onLockPixels(SkColorTable**);
     // override this in your subclass to clean up when we're unlocking pixels
-    virtual void onUnlockPixels();
+    virtual void onUnlockPixels() {}
 
-    SkImageRef(SkFlattenableReadBuffer&);
+    SkImageRef(SkFlattenableReadBuffer&, SkBaseMutex* mutex = NULL);
     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     SkBitmap fBitmap;
diff --git a/include/images/SkPageFlipper.h b/include/images/SkPageFlipper.h
index 91a1e9d..7779c30 100644
--- a/include/images/SkPageFlipper.h
+++ b/include/images/SkPageFlipper.h
@@ -60,4 +60,3 @@
 };
 
 #endif
-
diff --git a/include/ports/SkTypeface_mac.h b/include/ports/SkTypeface_mac.h
index eb9d25f..0166ee5 100644
--- a/include/ports/SkTypeface_mac.h
+++ b/include/ports/SkTypeface_mac.h
@@ -27,4 +27,3 @@
 SK_API extern SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef);
 
 #endif
-
diff --git a/include/ports/SkTypeface_win.h b/include/ports/SkTypeface_win.h
index 6da843f..fdf38e2 100644
--- a/include/ports/SkTypeface_win.h
+++ b/include/ports/SkTypeface_win.h
@@ -29,4 +29,3 @@
 SK_API void SkLOGFONTFromTypeface(const SkTypeface* typeface, LOGFONT* lf);
 
 #endif
-
diff --git a/include/svg/SkSVGBase.h b/include/svg/SkSVGBase.h
index a512eab..6bfc39d 100644
--- a/include/svg/SkSVGBase.h
+++ b/include/svg/SkSVGBase.h
@@ -23,4 +23,3 @@
 };
 
 #endif // SkSVGBase_DEFINEDes(const SkSVGAttribute** attrPtr) = 0;
-
diff --git a/include/text/SkTextLayout.h b/include/text/SkTextLayout.h
index 7d0ffe7..718acdb 100644
--- a/include/text/SkTextLayout.h
+++ b/include/text/SkTextLayout.h
@@ -58,4 +58,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkBoundaryPatch.h b/include/utils/SkBoundaryPatch.h
index 89060a6..cf001d5 100644
--- a/include/utils/SkBoundaryPatch.h
+++ b/include/utils/SkBoundaryPatch.h
@@ -64,4 +64,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkCamera.h b/include/utils/SkCamera.h
index 6527c32..eafacbc 100644
--- a/include/utils/SkCamera.h
+++ b/include/utils/SkCamera.h
@@ -173,4 +173,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkCubicInterval.h b/include/utils/SkCubicInterval.h
index bd1f9b9..64d63cf 100644
--- a/include/utils/SkCubicInterval.h
+++ b/include/utils/SkCubicInterval.h
@@ -20,4 +20,3 @@
 }
 
 #endif
-
diff --git a/include/utils/SkDumpCanvas.h b/include/utils/SkDumpCanvas.h
index 608ab01..8d9c67d 100644
--- a/include/utils/SkDumpCanvas.h
+++ b/include/utils/SkDumpCanvas.h
@@ -10,6 +10,8 @@
 
 #include "SkCanvas.h"
 
+#ifdef SK_DEVELOPER
+
 /** This class overrides all the draw methods on SkCanvas, and formats them
     as text, and then sends that to a Dumper helper object.
 
@@ -155,3 +157,5 @@
 };
 
 #endif
+
+#endif
diff --git a/include/utils/SkInterpolator.h b/include/utils/SkInterpolator.h
index 289e886..74789c8 100644
--- a/include/utils/SkInterpolator.h
+++ b/include/utils/SkInterpolator.h
@@ -129,4 +129,3 @@
                            SkScalar cx, SkScalar cy);
 
 #endif
-
diff --git a/include/utils/SkMatrix44.h b/include/utils/SkMatrix44.h
index 4789e7e..41f1a30 100644
--- a/include/utils/SkMatrix44.h
+++ b/include/utils/SkMatrix44.h
@@ -16,7 +16,6 @@
     #error "can't define MSCALAR both as DOUBLE and FLOAT"
 #endif
     typedef double SkMScalar;
-    typedef int64_t SkMIntScalar;
 
     static inline double SkFloatToMScalar(float x) {
         return static_cast<double>(x);
@@ -36,7 +35,6 @@
     #error "can't define MSCALAR both as DOUBLE and FLOAT"
 #endif
     typedef float SkMScalar;
-    typedef int32_t SkMIntScalar;
 
     static inline float SkFloatToMScalar(float x) {
         return x;
@@ -114,13 +112,21 @@
     SkMatrix44(Uninitialized_Constructor) { }
     SkMatrix44(Identity_Constructor) { this->setIdentity(); }
 
+    // DEPRECATED: use the constructors that take an enum
     SkMatrix44() { this->setIdentity(); }
-    SkMatrix44(const SkMatrix44&);
-    SkMatrix44(const SkMatrix44& a, const SkMatrix44& b);
+
+    SkMatrix44(const SkMatrix44& src) {
+        memcpy(fMat, src.fMat, sizeof(fMat));
+        fTypeMask = src.fTypeMask;
+    }
+
+    SkMatrix44(const SkMatrix44& a, const SkMatrix44& b) {
+        this->setConcat(a, b);
+    }
 
     SkMatrix44& operator=(const SkMatrix44& src) {
-        SkASSERT(sizeof(src) == sizeof(fMat) + sizeof(SkMIntScalar));
-        memcpy(this, &src, sizeof(*this));
+        memcpy(fMat, src.fMat, sizeof(fMat));
+        fTypeMask = src.fTypeMask;
         return *this;
     }
 
@@ -353,11 +359,8 @@
     double determinant() const;
 
 private:
-    SkMScalar               fMat[4][4];
-    // we use SkMIntScalar instead of just int, as we want to ensure that
-    // we are always packed with no extra bits, allowing us to call memcpy
-    // without fear of copying uninitialized bits.
-    mutable SkMIntScalar    fTypeMask;
+    SkMScalar           fMat[4][4];
+    mutable unsigned    fTypeMask;
 
     enum {
         kUnknown_Mask = 0x80,
diff --git a/include/utils/SkNWayCanvas.h b/include/utils/SkNWayCanvas.h
index fcd0ccf..d833455 100644
--- a/include/utils/SkNWayCanvas.h
+++ b/include/utils/SkNWayCanvas.h
@@ -84,4 +84,3 @@
 
 
 #endif
-
diff --git a/include/utils/SkParse.h b/include/utils/SkParse.h
index 7491cd6..c5aac7d 100644
--- a/include/utils/SkParse.h
+++ b/include/utils/SkParse.h
@@ -34,4 +34,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkParsePaint.h b/include/utils/SkParsePaint.h
index b35fcf1..066292f 100644
--- a/include/utils/SkParsePaint.h
+++ b/include/utils/SkParsePaint.h
@@ -24,4 +24,3 @@
 void SkPaint_Inflate(SkPaint*, const SkDOM&, const SkDOM::Node*);
 
 #endif
-
diff --git a/include/utils/SkParsePath.h b/include/utils/SkParsePath.h
index b48c2c5..c52b3c0 100644
--- a/include/utils/SkParsePath.h
+++ b/include/utils/SkParsePath.h
@@ -21,4 +21,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkRTConf.h b/include/utils/SkRTConf.h
new file mode 100644
index 0000000..c34dafb
--- /dev/null
+++ b/include/utils/SkRTConf.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2013 Google, Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkRTConf_DEFINED
+#define SkRTConf_DEFINED
+
+#include "SkString.h"
+#include "SkStream.h"
+
+#include "SkTDict.h"
+#include "SkTArray.h"
+
+/** \class SkRTConfBase
+    Non-templated base class for the runtime configs
+*/
+
+class SkRTConfBase {
+public:
+    SkRTConfBase(const char *name) : fName(name) {}
+    virtual ~SkRTConfBase() {}
+    virtual const char *getName() const { return fName.c_str(); }
+    virtual bool isDefault() const = 0;
+    virtual void print(SkWStream *o) const = 0;
+    virtual bool equals(const SkRTConfBase *conf) const = 0;
+protected:
+    SkString fName;
+};
+
+/** \class SkRTConf
+    A class to provide runtime configurability.
+*/
+template<typename T> class SkRTConf: public SkRTConfBase {
+public:
+    SkRTConf(const char *name, const T &defaultValue, const char *description);
+    operator const T&() const { return fValue; }
+    void print(SkWStream *o) const;
+    bool equals(const SkRTConfBase *conf) const;
+    bool isDefault() const { return fDefault == fValue; }
+    void set(const T& value) { fValue = value; }
+protected:
+    void doPrint(char *s) const;
+
+    T        fValue;
+    T        fDefault;
+    SkString fDescription;
+};
+
+#ifdef SK_DEVELOPER
+#define SK_CONF_DECLARE(confType, varName, confName, defaultValue, description) static SkRTConf<confType> varName(confName, defaultValue, description)
+#define SK_CONF_SET(confname, value) skRTConfRegistry().set(confname, value)
+#else
+#define SK_CONF_DECLARE(confType, varName, confName, defaultValue, description) static const confType varName = defaultValue
+#define SK_CONF_SET(confname, value) (void) confname, (void) value
+#endif
+
+/** \class SkRTConfRegistry
+    A class that maintains a systemwide registry of all runtime configuration
+    parameters.  Mainly used for printing them out and handling multiply-defined
+    knobs.
+*/
+
+class SkRTConfRegistry {
+public:
+    SkRTConfRegistry();
+    void printAll(const char *fname = NULL) const;
+    void printNonDefault(const char *fname = NULL) const;
+    const char *configFileLocation() const;
+    void possiblyDumpFile() const;
+    void validate() const;
+    template <typename T> void set(const char *confname, T value);
+private:
+    template<typename T> friend class SkRTConf;
+
+    void registerConf(SkRTConfBase *conf);
+    template <typename T> bool parse(const char *name, T* value);
+
+    SkTDArray<SkString *> fConfigFileKeys, fConfigFileValues;
+    typedef SkTDict< SkTDArray<SkRTConfBase *> * > ConfMap;
+    ConfMap fConfs;
+};
+
+// our singleton registry
+
+SkRTConfRegistry &skRTConfRegistry();
+
+template<typename T>
+SkRTConf<T>::SkRTConf(const char *name, const T &defaultValue, const char *description)
+    : SkRTConfBase(name)
+    , fValue(defaultValue)
+    , fDefault(defaultValue)
+    , fDescription(description) {
+
+    T value;
+    if (skRTConfRegistry().parse(fName.c_str(), &value)) {
+        fValue = value;
+    }
+    skRTConfRegistry().registerConf(this);
+}
+
+template<typename T>
+void SkRTConf<T>::print(SkWStream *o) const {
+    char outline[200]; // should be ok because we specify a max. width for everything here.
+
+    sprintf(outline, "%-30.30s", getName());
+    doPrint(&(outline[30]));
+    sprintf(&(outline[60]), " %.128s", fDescription.c_str());
+    if (' ' == outline[strlen(outline)-1]) {
+        for (int i = strlen(outline)-1 ; ' ' == outline[i] ; i--) {
+            outline[i] = '\0';
+        }
+    }
+    o->writeText(outline);
+}
+
+template<typename T>
+void SkRTConf<T>::doPrint(char *s) const {
+    sprintf(s, "%-30.30s", "How do I print myself??");
+}
+
+template<> inline void SkRTConf<bool>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%s # [%s]", fValue ? "true" : "false", fDefault ? "true" : "false");
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<int>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%d # [%d]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<unsigned int>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%u # [%u]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<float>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%6.6f # [%6.6f]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<double>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%6.6f # [%6.6f]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<const char *>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%s # [%s]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<typename T>
+bool SkRTConf<T>::equals(const SkRTConfBase *conf) const {
+    // static_cast here is okay because there's only one kind of child class.
+    const SkRTConf<T> *child_pointer = static_cast<const SkRTConf<T> *>(conf);
+    return child_pointer &&
+           fName == child_pointer->fName &&
+           fDescription == child_pointer->fDescription &&
+           fValue == child_pointer->fValue &&
+           fDefault == child_pointer->fDefault;
+}
+
+#endif
diff --git a/include/utils/SkRandom.h b/include/utils/SkRandom.h
new file mode 100644
index 0000000..150384d
--- /dev/null
+++ b/include/utils/SkRandom.h
@@ -0,0 +1,323 @@
+
+/*
+ * Copyright 2006 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.
+ */
+
+
+#ifndef SkRandom_DEFINED
+#define SkRandom_DEFINED
+
+#include "Sk64.h"
+#include "SkScalar.h"
+
+/** \class SkRandom
+
+    Utility class that implements pseudo random 32bit numbers using a fast
+    linear equation. Unlike rand(), this class holds its own seed (initially
+    set to 0), so that multiple instances can be used with no side-effects.
+*/
+class SkRandom {
+public:
+    SkRandom() : fSeed(0) {}
+    SkRandom(uint32_t seed) : fSeed(seed) {}
+
+    /** Return the next pseudo random number as an unsigned 32bit value.
+    */
+    uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
+
+    /** Return the next pseudo random number as a signed 32bit value.
+    */
+    int32_t nextS() { return (int32_t)this->nextU(); }
+
+    /** Return the next pseudo random number as an unsigned 16bit value.
+    */
+    U16CPU nextU16() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number as a signed 16bit value.
+    */
+    S16CPU nextS16() { return this->nextS() >> 16; }
+
+    /**
+     *  Returns value [0...1) as a float
+     */
+    float nextF() {
+        // const is 1 / (2^32 - 1)
+        return (float)(this->nextU() * 2.32830644e-10);
+    }
+
+    /**
+     *  Returns value [min...max) as a float
+     */
+    float nextRangeF(float min, float max) {
+        return min + this->nextF() * (max - min);
+    }
+
+    /** Return the next pseudo random number, as an unsigned value of
+        at most bitCount bits.
+        @param bitCount The maximum number of bits to be returned
+    */
+    uint32_t nextBits(unsigned bitCount) {
+        SkASSERT(bitCount > 0 && bitCount <= 32);
+        return this->nextU() >> (32 - bitCount);
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+        [min, max] inclusive.
+    */
+    uint32_t nextRangeU(uint32_t min, uint32_t max) {
+        SkASSERT(min <= max);
+        uint32_t range = max - min + 1;
+        if (0 == range) {
+            return this->nextU();
+        } else {
+            return min + this->nextU() % range;
+        }
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+        [0, count).
+     */
+    uint32_t nextULessThan(uint32_t count) {
+        SkASSERT(count > 0);
+        return this->nextRangeU(0, count - 1);
+    }
+
+    /** Return the next pseudo random number expressed as an unsigned SkFixed
+        in the range [0..SK_Fixed1).
+    */
+    SkFixed nextUFixed1() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number expressed as a signed SkFixed
+        in the range (-SK_Fixed1..SK_Fixed1).
+    */
+    SkFixed nextSFixed1() { return this->nextS() >> 15; }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range [0..SK_Scalar1).
+    */
+    SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range [min..max).
+    */
+    SkScalar nextRangeScalar(SkScalar min, SkScalar max) {
+        return SkScalarMul(this->nextUScalar1(), (max - min)) + min;
+    }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range (-SK_Scalar1..SK_Scalar1).
+    */
+    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
+
+    /** Return the next pseudo random number as a bool.
+    */
+    bool nextBool() { return this->nextU() >= 0x80000000; }
+
+    /** A biased version of nextBool().
+     */
+    bool nextBiasedBool(SkScalar fractionTrue) {
+        SkASSERT(fractionTrue >= 0 && fractionTrue <= SK_Scalar1);
+        return this->nextUScalar1() <= fractionTrue;
+    }
+
+    /** Return the next pseudo random number as a signed 64bit value.
+    */
+    void next64(Sk64* a) {
+        SkASSERT(a);
+        a->set(this->nextS(), this->nextU());
+    }
+
+    /**
+     *  Return the current seed. This allows the caller to later reset to the
+     *  same seed (using setSeed) so it can generate the same sequence.
+     */
+    int32_t getSeed() const { return fSeed; }
+
+    /** Set the seed of the random object. The seed is initialized to 0 when the
+        object is first created, and is updated each time the next pseudo random
+        number is requested.
+    */
+    void setSeed(int32_t seed) { fSeed = (uint32_t)seed; }
+
+private:
+    //  See "Numerical Recipes in C", 1992 page 284 for these constants
+    enum {
+        kMul = 1664525,
+        kAdd = 1013904223
+    };
+    uint32_t fSeed;
+};
+
+/** \class SkMWCRandom
+
+ Utility class that implements pseudo random 32bit numbers using Marsaglia's
+ multiply-with-carry "mother of all" algorithm. Unlike rand(), this class holds
+ its own state, so that multiple instances can be used with no side-effects.
+
+ Has a large period and all bits are well-randomized.
+ */
+class SkMWCRandom {
+public:
+    SkMWCRandom() { init(0); }
+    SkMWCRandom(uint32_t seed) { init(seed); }
+    SkMWCRandom(const SkMWCRandom& rand) : fK(rand.fK), fJ(rand.fJ) {}
+
+    SkMWCRandom& operator=(const SkMWCRandom& rand) {
+        fK = rand.fK;
+        fJ = rand.fJ;
+
+        return *this;
+    }
+
+    /** Return the next pseudo random number as an unsigned 32bit value.
+     */
+    uint32_t nextU() {
+        fK = kKMul*(fK & 0xffff) + (fK >> 16);
+        fJ = kJMul*(fJ & 0xffff) + (fJ >> 16);
+        return (((fK << 16) | (fK >> 16)) + fJ);
+    }
+
+    /** Return the next pseudo random number as a signed 32bit value.
+     */
+    int32_t nextS() { return (int32_t)this->nextU(); }
+
+    /** Return the next pseudo random number as an unsigned 16bit value.
+     */
+    U16CPU nextU16() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number as a signed 16bit value.
+     */
+    S16CPU nextS16() { return this->nextS() >> 16; }
+
+    /**
+     *  Returns value [0...1) as an IEEE float
+     */
+    float nextF() {
+        unsigned int floatint = 0x3f800000 | (this->nextU() >> 9);
+        float f = *(float*)(&floatint) - 1.0f;
+        return f;
+    }
+
+    /**
+     *  Returns value [min...max) as a float
+     */
+    float nextRangeF(float min, float max) {
+        return min + this->nextF() * (max - min);
+    }
+
+    /** Return the next pseudo random number, as an unsigned value of
+     at most bitCount bits.
+     @param bitCount The maximum number of bits to be returned
+     */
+    uint32_t nextBits(unsigned bitCount) {
+        SkASSERT(bitCount > 0 && bitCount <= 32);
+        return this->nextU() >> (32 - bitCount);
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+     [min, max] inclusive.
+     */
+    uint32_t nextRangeU(uint32_t min, uint32_t max) {
+        SkASSERT(min <= max);
+        uint32_t range = max - min + 1;
+        if (0 == range) {
+            return this->nextU();
+        } else {
+            return min + this->nextU() % range;
+        }
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+     [0, count).
+     */
+    uint32_t nextULessThan(uint32_t count) {
+        SkASSERT(count > 0);
+        return this->nextRangeU(0, count - 1);
+    }
+
+    /** Return the next pseudo random number expressed as an unsigned SkFixed
+     in the range [0..SK_Fixed1).
+     */
+    SkFixed nextUFixed1() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number expressed as a signed SkFixed
+     in the range (-SK_Fixed1..SK_Fixed1).
+     */
+    SkFixed nextSFixed1() { return this->nextS() >> 15; }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+     in the range [0..SK_Scalar1).
+     */
+    SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+     in the range [min..max).
+     */
+    SkScalar nextRangeScalar(SkScalar min, SkScalar max) {
+        return SkScalarMul(this->nextUScalar1(), (max - min)) + min;
+    }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+     in the range (-SK_Scalar1..SK_Scalar1).
+     */
+    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
+
+    /** Return the next pseudo random number as a bool.
+     */
+    bool nextBool() { return this->nextU() >= 0x80000000; }
+
+    /** A biased version of nextBool().
+     */
+    bool nextBiasedBool(SkScalar fractionTrue) {
+        SkASSERT(fractionTrue >= 0 && fractionTrue <= SK_Scalar1);
+        return this->nextUScalar1() <= fractionTrue;
+    }
+
+    /** Return the next pseudo random number as a signed 64bit value.
+     */
+    void next64(Sk64* a) {
+        SkASSERT(a);
+        a->set(this->nextS(), this->nextU());
+    }
+
+    /** Reset the random object.
+     */
+    void setSeed(uint32_t seed) { init(seed); }
+
+private:
+    // Initialize state variables with LCG.
+    // We must ensure that both J and K are non-zero, otherwise the
+    // multiply-with-carry step will forevermore return zero.
+    void init(uint32_t seed) {
+        fK = NextLCG(seed);
+        if (0 == fK) {
+            fK = NextLCG(fK);
+        }
+        fJ = NextLCG(fK);
+        if (0 == fJ) {
+            fJ = NextLCG(fJ);
+        }
+        SkASSERT(0 != fK && 0 != fJ);
+    }
+    static uint32_t NextLCG(uint32_t seed) { return kMul*seed + kAdd; }
+
+    //  See "Numerical Recipes in C", 1992 page 284 for these constants
+    //  For the LCG that sets the initial state from a seed
+    enum {
+        kMul = 1664525,
+        kAdd = 1013904223
+    };
+    // Constants for the multiply-with-carry steps
+    enum {
+        kKMul = 30345,
+        kJMul = 18000,
+    };
+
+    uint32_t fK;
+    uint32_t fJ;
+};
+
+#endif
diff --git a/include/utils/SkUnitMappers.h b/include/utils/SkUnitMappers.h
index 901650d..64aab5d 100644
--- a/include/utils/SkUnitMappers.h
+++ b/include/utils/SkUnitMappers.h
@@ -53,4 +53,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkBGViewArtist.h b/include/views/SkBGViewArtist.h
index 869beab..33054c8 100644
--- a/include/views/SkBGViewArtist.h
+++ b/include/views/SkBGViewArtist.h
@@ -31,4 +31,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkEventSink.h b/include/views/SkEventSink.h
index 8ef92b4..01d4f7a 100644
--- a/include/views/SkEventSink.h
+++ b/include/views/SkEventSink.h
@@ -110,4 +110,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkKey.h b/include/views/SkKey.h
index a019c28..036e2c3 100644
--- a/include/views/SkKey.h
+++ b/include/views/SkKey.h
@@ -52,5 +52,11 @@
     kSkKeyCount
 };
 
-#endif
+enum SkModifierKeys {
+    kShift_SkModifierKey    = 1 << 0,
+    kControl_SkModifierKey  = 1 << 1,
+    kOption_SkModifierKey   = 1 << 2,   // same as ALT
+    kCommand_SkModifierKey  = 1 << 3,
+};
 
+#endif
diff --git a/include/views/SkOSMenu.h b/include/views/SkOSMenu.h
index 3e5ee43..8801a52 100644
--- a/include/views/SkOSMenu.h
+++ b/include/views/SkOSMenu.h
@@ -180,4 +180,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_Android.h b/include/views/SkOSWindow_Android.h
index 9b72b48..ca9b770 100644
--- a/include/views/SkOSWindow_Android.h
+++ b/include/views/SkOSWindow_Android.h
@@ -43,4 +43,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_Mac.h b/include/views/SkOSWindow_Mac.h
index d195cf1..aa52021 100644
--- a/include/views/SkOSWindow_Mac.h
+++ b/include/views/SkOSWindow_Mac.h
@@ -18,7 +18,7 @@
     void*   getHWND() const { return fHWND; }
 
     virtual bool onDispatchClick(int x, int y, Click::State state,
-                                 void* owner);
+                                 void* owner, unsigned modi);
     enum SkBackEndTypes {
         kNone_BackEndType,
 #if SK_SUPPORT_GPU
diff --git a/include/views/SkOSWindow_NaCl.h b/include/views/SkOSWindow_NaCl.h
index 8c4ddc6..52514ca 100644
--- a/include/views/SkOSWindow_NaCl.h
+++ b/include/views/SkOSWindow_NaCl.h
@@ -43,4 +43,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_SDL.h b/include/views/SkOSWindow_SDL.h
index 037de3b..e6b59e6 100644
--- a/include/views/SkOSWindow_SDL.h
+++ b/include/views/SkOSWindow_SDL.h
@@ -42,4 +42,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_iOS.h b/include/views/SkOSWindow_iOS.h
index eda7c5f..1dd3997 100755
--- a/include/views/SkOSWindow_iOS.h
+++ b/include/views/SkOSWindow_iOS.h
@@ -16,9 +16,6 @@
     ~SkOSWindow();
     void*   getHWND() const { return fHWND; }
 
-    virtual bool onDispatchClick(int x, int y, Click::State state,
-                                 void* owner);
-
     enum SkBackEndTypes {
         kNone_BackEndType,
         kNativeGL_BackEndType,
@@ -46,4 +43,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkStackViewLayout.h b/include/views/SkStackViewLayout.h
index f8d23de..f2a7cf0 100644
--- a/include/views/SkStackViewLayout.h
+++ b/include/views/SkStackViewLayout.h
@@ -86,4 +86,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkTextBox.h b/include/views/SkTextBox.h
index e3b365d..e217076 100644
--- a/include/views/SkTextBox.h
+++ b/include/views/SkTextBox.h
@@ -75,4 +75,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkTouchGesture.h b/include/views/SkTouchGesture.h
index 1ba3865..3a0cfce 100644
--- a/include/views/SkTouchGesture.h
+++ b/include/views/SkTouchGesture.h
@@ -75,5 +75,3 @@
 };
 
 #endif
-
-
diff --git a/include/views/SkView.h b/include/views/SkView.h
index eb0621a..786d363 100644
--- a/include/views/SkView.h
+++ b/include/views/SkView.h
@@ -15,6 +15,7 @@
 #include "SkDOM.h"
 #include "SkTDict.h"
 #include "SkMatrix.h"
+#include "SkMetaData.h"
 
 class SkCanvas;
 class SkLayerView;
@@ -155,6 +156,9 @@
         SkIPoint    fIOrig, fIPrev, fICurr;
         State       fState;
         void*       fOwner;
+        unsigned    fModifierKeys;
+
+        SkMetaData  fMeta;
     private:
         SkEventSinkID   fTargetID;
         char*           fType;
@@ -164,11 +168,11 @@
 
         friend class SkView;
     };
-    Click*  findClickHandler(SkScalar x, SkScalar y);
+    Click*  findClickHandler(SkScalar x, SkScalar y, unsigned modifierKeys);
 
-    static void DoClickDown(Click*, int x, int y);
-    static void DoClickMoved(Click*, int x, int y);
-    static void DoClickUp(Click*, int x, int y);
+    static void DoClickDown(Click*, int x, int y, unsigned modi);
+    static void DoClickMoved(Click*, int x, int y, unsigned modi);
+    static void DoClickUp(Click*, int x, int y, unsigned modi);
 
     /** Send the event to the view's parent, and its parent etc. until one of them
         returns true from its onEvent call. This view is returned. If no parent handles
@@ -342,13 +346,13 @@
 
     /** Override this if you might handle the click
     */
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi);
     /** Override this to decide if your children are targets for a click.
         The default returns true, in which case your children views will be
         candidates for onFindClickHandler. Returning false wil skip the children
         and just call your onFindClickHandler.
      */
-    virtual bool onSendClickToChildren(SkScalar x, SkScalar y);
+    virtual bool onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi);
     /** Override this to track clicks, returning true as long as you want to track
         the pen/mouse.
     */
@@ -393,4 +397,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkViewInflate.h b/include/views/SkViewInflate.h
index b2cd1e6..db3689a 100644
--- a/include/views/SkViewInflate.h
+++ b/include/views/SkViewInflate.h
@@ -69,4 +69,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h
index ca48e27..ca68e75 100644
--- a/include/views/SkWindow.h
+++ b/include/views/SkWindow.h
@@ -45,7 +45,7 @@
     // return the bounds of the dirty/inval rgn, or [0,0,0,0] if none
     const SkIRect& getDirtyBounds() const { return fDirtyRgn.getBounds(); }
 
-    bool    handleClick(int x, int y, Click::State, void* owner = NULL);
+    bool    handleClick(int x, int y, Click::State, void* owner, unsigned modi = 0);
     bool    handleChar(SkUnichar);
     bool    handleKey(SkKey);
     bool    handleKeyUp(SkKey);
@@ -67,7 +67,7 @@
         const char path[]) {}
 protected:
     virtual bool onEvent(const SkEvent&);
-    virtual bool onDispatchClick(int x, int y, Click::State, void* owner);
+    virtual bool onDispatchClick(int x, int y, Click::State, void* owner, unsigned modi);
     // called if part of our bitmap is invalidated
     virtual void onHandleInval(const SkIRect&);
     virtual bool onHandleChar(SkUnichar);
diff --git a/include/views/animated/SkBorderView.h b/include/views/animated/SkBorderView.h
index 02f8725..8b1e537 100644
--- a/include/views/animated/SkBorderView.h
+++ b/include/views/animated/SkBorderView.h
@@ -38,4 +38,3 @@
 };
 
 #endif
-
diff --git a/include/views/animated/SkScrollBarView.h b/include/views/animated/SkScrollBarView.h
index 1270e14..05042f0 100644
--- a/include/views/animated/SkScrollBarView.h
+++ b/include/views/animated/SkScrollBarView.h
@@ -42,4 +42,3 @@
     typedef SkWidgetView INHERITED;
 };
 #endif
-
diff --git a/include/xml/SkBML_WXMLParser.h b/include/xml/SkBML_WXMLParser.h
index 5d220cf..74f164c 100644
--- a/include/xml/SkBML_WXMLParser.h
+++ b/include/xml/SkBML_WXMLParser.h
@@ -44,4 +44,3 @@
 };
 
 #endif // SkBML_WXMLParser_DEFINED
-
diff --git a/include/xml/SkBML_XMLParser.h b/include/xml/SkBML_XMLParser.h
index 7a8d6a1..9bdbf51 100644
--- a/include/xml/SkBML_XMLParser.h
+++ b/include/xml/SkBML_XMLParser.h
@@ -29,4 +29,3 @@
 };
 
 #endif // SkBML_XMLParser_DEFINED
-
diff --git a/include/xml/SkDOM.h b/include/xml/SkDOM.h
index fd175f0..e0bb744 100644
--- a/include/xml/SkDOM.h
+++ b/include/xml/SkDOM.h
@@ -89,4 +89,3 @@
 };
 
 #endif
-
diff --git a/include/xml/SkJS.h b/include/xml/SkJS.h
index 645e03b..8a11097 100644
--- a/include/xml/SkJS.h
+++ b/include/xml/SkJS.h
@@ -37,4 +37,3 @@
     JSContext *fContext;
     JSObject *fGlobal;
 };
-
diff --git a/include/xml/SkXMLWriter.h b/include/xml/SkXMLWriter.h
index 4e0eda1..214fefe 100644
--- a/include/xml/SkXMLWriter.h
+++ b/include/xml/SkXMLWriter.h
@@ -83,4 +83,3 @@
 
 
 #endif
-
diff --git a/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp
index f99a6a1..1e9b89e 100644
--- a/samplecode/ClockFaceView.cpp
+++ b/samplecode/ClockFaceView.cpp
@@ -19,8 +19,7 @@
 #include "SkTypeface.h"
 #include "SkAvoidXfermode.h"
 
-static inline SkPMColor rgb2gray(SkPMColor c)
-{
+static inline SkPMColor rgb2gray(SkPMColor c) {
     unsigned r = SkGetPackedR32(c);
     unsigned g = SkGetPackedG32(c);
     unsigned b = SkGetPackedB32(c);
@@ -32,32 +31,33 @@
 
 class SkGrayScaleColorFilter : public SkColorFilter {
 public:
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
-    {
-        for (int i = 0; i < count; i++)
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) const SK_OVERRIDE {
+        for (int i = 0; i < count; i++) {
             result[i] = rgb2gray(src[i]);
+        }
     }
 };
 
 class SkChannelMaskColorFilter : public SkColorFilter {
 public:
-    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask)
-    {
+    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
         fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
     }
 
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
-    {
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) const SK_OVERRIDE {
         SkPMColor mask = fMask;
-        for (int i = 0; i < count; i++)
+        for (int i = 0; i < count; i++) {
             result[i] = src[i] & mask;
+        }
     }
 
 private:
     SkPMColor   fMask;
 };
 
-///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 
 #include "SkGradientShader.h"
 #include "SkLayerRasterizer.h"
@@ -110,7 +110,7 @@
 public:
     InverseFillPE() {}
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*) const SK_OVERRIDE {
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE {
         *dst = src;
         dst->setFillType(SkPath::kInverseWinding_FillType);
         return true;
@@ -164,33 +164,29 @@
     SkTypeface* fFace;
     SkScalar fInterp;
     SkScalar fDx;
+
 public:
-    ClockFaceView()
-    {
+    ClockFaceView() {
         fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
         fInterp = 0;
         fDx = SK_Scalar1/64;
     }
 
-    virtual ~ClockFaceView()
-    {
+    virtual ~ClockFaceView() {
         SkSafeUnref(fFace);
     }
 
 protected:
     // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)
-    {
-        if (SampleCode::TitleQ(*evt))
-        {
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Text Effects");
             return true;
         }
         return this->INHERITED::onQuery(evt);
     }
 
-    void drawBG(SkCanvas* canvas)
-    {
+    void drawBG(SkCanvas* canvas) {
 //        canvas->drawColor(0xFFDDDDDD);
         canvas->drawColor(SK_ColorWHITE);
     }
@@ -202,7 +198,7 @@
         SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
         SkPath path, dstPath;
         orig.getTextPath("9", 1, 0, 0, &path);
-        pe->filterPath(&dstPath, path, &rec);
+        pe->filterPath(&dstPath, path, &rec, NULL);
 
         SkPaint p;
         p.setAntiAlias(true);
@@ -254,4 +250,3 @@
 
 static SkView* MyFactory() { return new ClockFaceView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/OverView.cpp b/samplecode/OverView.cpp
index f170bdb..3b7183e 100644
--- a/samplecode/OverView.cpp
+++ b/samplecode/OverView.cpp
@@ -49,11 +49,11 @@
 
     virtual SkCanvas* beforeChildren(SkCanvas*) SK_OVERRIDE;
 
-    virtual bool onSendClickToChildren(SkScalar x, SkScalar y) SK_OVERRIDE {
+    virtual bool onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
         return false;
     }
 
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y) SK_OVERRIDE {
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
         int ix = (int)(SkScalarDiv(x * N, kWidth));
         int iy = (int)(SkScalarDiv(y * N, kHeight));
         if (ix >= 0 && iy >= 0) {
diff --git a/samplecode/SampleAAClip.cpp b/samplecode/SampleAAClip.cpp
index 196641e..d4e632d 100644
--- a/samplecode/SampleAAClip.cpp
+++ b/samplecode/SampleAAClip.cpp
@@ -18,7 +18,7 @@
     c1.setRect(r1);
     c2.op(c0, c1, op);
 
-    SkIRect r2 = c2.getBounds();
+    SkDEBUGCODE(SkIRect r2 = c2.getBounds());
     SkASSERT(r2 == expectedR);
 }
 
@@ -125,4 +125,3 @@
 
 static SkView* MyFactory() { return new AAClipView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAARectModes.cpp b/samplecode/SampleAARectModes.cpp
index 00edfba..3a8eb13 100644
--- a/samplecode/SampleAARectModes.cpp
+++ b/samplecode/SampleAARectModes.cpp
@@ -137,4 +137,3 @@
 
 static SkView* MyFactory() { return new AARectsModesView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAARects.cpp b/samplecode/SampleAARects.cpp
index e66f8df..0f84900 100644
--- a/samplecode/SampleAARects.cpp
+++ b/samplecode/SampleAARects.cpp
@@ -195,4 +195,3 @@
 
 static SkView* MyFactory() { return new AARectView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAll.cpp b/samplecode/SampleAll.cpp
index acdb148..ff8249f 100644
--- a/samplecode/SampleAll.cpp
+++ b/samplecode/SampleAll.cpp
@@ -51,7 +51,8 @@
 
 class SkGrayScaleColorFilter : public SkColorFilter {
 public:
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) const SK_OVERRIDE {
         for (int i = 0; i < count; i++)
             result[i] = rgb2gray(src[i]);
     }
@@ -63,7 +64,8 @@
         fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
     }
 
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor result[]) const SK_OVERRIDE {
         SkPMColor mask = fMask;
         for (int i = 0; i < count; i++) {
             result[i] = src[i] & mask;
@@ -163,7 +165,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Dot2DPathEffect)
 
 protected:
-    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) const SK_OVERRIDE {
         dst->addCircle(loc.fX, loc.fY, fRadius);
     }
 
@@ -528,10 +530,10 @@
 SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect {
 */
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) {
         fClickPt.set(x, y);
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     SkPathEffect* pathEffectTest() {
@@ -642,4 +644,3 @@
 
 static SkView* MyFactory() { return new DemoView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAnimBlur.cpp b/samplecode/SampleAnimBlur.cpp
index cada0cd..98c9814 100644
--- a/samplecode/SampleAnimBlur.cpp
+++ b/samplecode/SampleAnimBlur.cpp
@@ -67,4 +67,3 @@
 
 static SkView* MyFactory() { return new AnimBlurView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAnimatedGradient.cpp b/samplecode/SampleAnimatedGradient.cpp
index c4a4ec5..79ee499 100644
--- a/samplecode/SampleAnimatedGradient.cpp
+++ b/samplecode/SampleAnimatedGradient.cpp
@@ -95,4 +95,3 @@
 
 static SkView* MyFactory() { return new GradientView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAnimator.cpp b/samplecode/SampleAnimator.cpp
index 7b46d47..50b61ba 100644
--- a/samplecode/SampleAnimator.cpp
+++ b/samplecode/SampleAnimator.cpp
@@ -172,4 +172,3 @@
 }
 
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index 716998f..dbc779f 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -64,14 +64,6 @@
 SkTCPServer gServer;
 #endif
 
-#define DEBUGGERx
-#ifdef  DEBUGGER
-extern SkView* create_debugger(const char* data, size_t size);
-extern bool is_debugger(SkView* view);
-SkTDArray<char> gTempDataStore;
-#endif
-
-
 #define USE_ARROWS_FOR_ZOOM true
 
 #if SK_ANGLE
@@ -260,32 +252,15 @@
 
     virtual SkCanvas* createCanvas(SampleWindow::DeviceType dType,
                                    SampleWindow* win) {
-        switch (dType) {
-            case kRaster_DeviceType:
-                // fallthrough
-            case kPicture_DeviceType:
-                // fallthrough
-#if SK_ANGLE
-            case kANGLE_DeviceType:
-#endif
-                break;
 #if SK_SUPPORT_GPU
-            case kGPU_DeviceType:
-            case kNullGPU_DeviceType:
-                if (fCurContext) {
-                    SkAutoTUnref<SkDevice> device(new SkGpuDevice(fCurContext,
-                                                                  fCurRenderTarget));
-                    return new SkCanvas(device);
-                } else {
-                    return NULL;
-                }
-                break;
+        if (IsGpuDeviceType(dType) && NULL != fCurContext) {
+            SkAutoTUnref<SkDevice> device(new SkGpuDevice(fCurContext, fCurRenderTarget));
+            return new SkCanvas(device);
+        } else
 #endif
-            default:
-                SkASSERT(false);
-                return NULL;
+        {
+            return NULL;
         }
-        return NULL;
     }
 
     virtual void publishCanvas(SampleWindow::DeviceType dType,
@@ -296,7 +271,7 @@
             // in case we have queued drawing calls
             fCurContext->flush();
 
-            if (kGPU_DeviceType != dType && kNullGPU_DeviceType != dType) {
+            if (!IsGpuDeviceType(dType)) {
                 // need to send the raster bits to the (gpu) window
                 fCurContext->setRenderTarget(fCurRenderTarget);
                 const SkBitmap& bm = win->getBitmap();
@@ -811,7 +786,6 @@
     fZoomScale = SK_Scalar1;
 
     fMagnify = false;
-    fDebugger = false;
 
     fSaveToPdf = false;
     fPdfCanvas = NULL;
@@ -847,10 +821,6 @@
     itemID = fAppMenu->appendTriState("Tiling", "Tiling", sinkID, fTilingState);
     fAppMenu->assignKeyEquivalentToItem(itemID, 't');
 
-#ifdef DEBUGGER
-    itemID = fAppMenu->appendSwitch("Debugger", "Debugger", sinkID, fDebugger);
-    fAppMenu->assignKeyEquivalentToItem(itemID, 'q');
-#endif
     itemID = fAppMenu->appendSwitch("Slide Show", "Slide Show" , sinkID, false);
     fAppMenu->assignKeyEquivalentToItem(itemID, 'a');
     itemID = fAppMenu->appendSwitch("Clip", "Clip" , sinkID, fUseClip);
@@ -1286,30 +1256,15 @@
         fPdfCanvas = new SkCanvas(pdfDevice);
         pdfDevice->unref();
         canvas = fPdfCanvas;
+    } else if (kPicture_DeviceType == fDeviceType) {
+        fPicture = new SkPicture;
+        canvas = fPicture->beginRecording(9999, 9999);
     } else {
-        switch (fDeviceType) {
-            case kRaster_DeviceType:
-                // fallthrough
 #if SK_SUPPORT_GPU
-            case kGPU_DeviceType:
-                // fallthrough
-#if SK_ANGLE
-            case kANGLE_DeviceType:
-#endif // SK_ANGLE
-#endif // SK_SUPPORT_GPU
-                canvas = this->INHERITED::beforeChildren(canvas);
-                break;
-            case kPicture_DeviceType:
-                fPicture = new SkPicture;
-                canvas = fPicture->beginRecording(9999, 9999);
-                break;
-#if SK_SUPPORT_GPU
-            case kNullGPU_DeviceType:
-                break;
+        if (kNullGPU_DeviceType != fDeviceType)
 #endif
-            default:
-                SkASSERT(false);
-                break;
+        {
+            canvas = this->INHERITED::beforeChildren(canvas);
         }
     }
 
@@ -1421,25 +1376,6 @@
         bm.scrollRect(&r, dx, dy, &inval);
         paint_rgn(bm, r, inval);
     }
-#ifdef DEBUGGER
-    SkView* curr = curr_view(this);
-    if (fDebugger && !is_debugger(curr) && !is_transition(curr) && !is_overview(curr)) {
-        //Stop Pipe when fDebugger is active
-        if (fPipeState != SkOSMenu::kOffState) {
-            fPipeState = SkOSMenu::kOffState;
-            (void)SampleView::SetUsePipe(curr, fPipeState);
-            fAppMenu->getItemByID(fUsePipeMenuItemID)->setTriState(fPipeState);
-            this->onUpdateMenu(fAppMenu);
-        }
-
-        //Reset any transformations
-        fGesture.stop();
-        fGesture.reset();
-
-        this->loadView(create_debugger(gTempDataStore.begin(),
-                                       gTempDataStore.count()));
-    }
-#endif
 }
 
 void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
@@ -1686,18 +1622,6 @@
         this->saveToPdf();
         return true;
     }
-#ifdef DEBUGGER
-    if (SkOSMenu::FindSwitchState(evt, "Debugger", &fDebugger)) {
-        if (fDebugger) {
-            fPipeState = SkOSMenu::kOnState;
-            (void)SampleView::SetUsePipe(curr_view(this), fPipeState);
-        } else {
-            this->loadView((*fSamples[fCurrIndex])());
-        }
-        this->inval(NULL);
-        return true;
-    }
-#endif
     return this->INHERITED::onEvent(evt);
 }
 
@@ -1955,7 +1879,7 @@
 static const char gGestureClickType[] = "GestureClickType";
 
 bool SampleWindow::onDispatchClick(int x, int y, Click::State state,
-        void* owner) {
+        void* owner, unsigned modi) {
     if (Click::kMoved_State == state) {
         updatePointer(x, y);
     }
@@ -1970,9 +1894,19 @@
         //it's only necessary to update the drawing if there's a click
         this->inval(NULL);
         return false; //prevent dragging while magnify is enabled
-    }
-    else {
-        return this->INHERITED::onDispatchClick(x, y, state, owner);
+    } else {
+        // capture control+option, and trigger debugger
+        if ((modi & kControl_SkModifierKey) && (modi & kOption_SkModifierKey)) {
+            if (Click::kDown_State == state) {
+                SkEvent evt("debug-hit-test");
+                evt.setS32("debug-hit-test-x", x);
+                evt.setS32("debug-hit-test-y", y);
+                curr_view(this)->doEvent(evt);
+            }
+            return true;
+        } else {
+            return this->INHERITED::onDispatchClick(x, y, state, owner, modi);
+        }
     }
 }
 
@@ -1987,7 +1921,8 @@
     }
 };
 
-SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
+SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y,
+                                                unsigned modi) {
     return new GestureClick(this);
 }
 
@@ -2030,12 +1965,7 @@
 
     //repopulate the slide menu when a view is loaded
     fSlideMenu->reset();
-#ifdef DEBUGGER
-    if (!is_debugger(view) && !is_overview(view) && !is_transition(view) && fDebugger) {
-        //Force Pipe to be on if using debugger
-        fPipeState = SkOSMenu::kOnState;
-    }
-#endif
+
     (void)SampleView::SetUsePipe(view, fPipeState);
     if (SampleView::IsSampleView(view))
         ((SampleView*)view)->requestMenu(fSlideMenu);
@@ -2071,21 +2001,6 @@
 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypePrefix) == SampleWindow::kDeviceTypeCnt,
                   array_size_mismatch);
 
-static const bool gDeviceTypeIsGPU[] = {
-    false,
-    false,
-#if SK_SUPPORT_GPU
-    true,
-#if SK_ANGLE
-    true,
-#endif // SK_ANGLE
-    true
-#endif // SK_SUPPORT_GPU
-};
-SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypeIsGPU) == SampleWindow::kDeviceTypeCnt,
-                  array_size_mismatch);
-
-
 static const char* trystate_str(SkOSMenu::TriState state,
                                 const char trueStr[], const char falseStr[]) {
     if (SkOSMenu::kOnState == state) {
@@ -2154,8 +2069,9 @@
     }
 
 #if SK_SUPPORT_GPU
-    if (gDeviceTypeIsGPU[fDeviceType] &&
+    if (IsGpuDeviceType(fDeviceType) &&
         NULL != fDevManager &&
+        fDevManager->getGrRenderTarget() &&
         fDevManager->getGrRenderTarget()->numSamples() > 0) {
         title.appendf(" [MSAA: %d]",
                        fDevManager->getGrRenderTarget()->numSamples());
@@ -2234,11 +2150,21 @@
         fRepeatCount = evt.getFast32();
         return true;
     }
+
     int32_t pipeHolder;
     if (evt.findS32(set_use_pipe_tag, &pipeHolder)) {
         fPipeState = static_cast<SkOSMenu::TriState>(pipeHolder);
         return true;
     }
+
+    if (evt.isType("debug-hit-test")) {
+        fDebugHitTest = true;
+        evt.findS32("debug-hit-test-x", &fDebugHitTestLoc.fX);
+        evt.findS32("debug-hit-test-y", &fDebugHitTestLoc.fY);
+        this->inval(NULL);
+        return true;
+    }
+
     return this->INHERITED::onEvent(evt);
 }
 
@@ -2295,10 +2221,6 @@
             gServer.writePacket(fBlock, fTotalWritten);
         }
 #endif
-#ifdef  DEBUGGER
-        gTempDataStore.reset();
-        gTempDataStore.append(fTotalWritten, (const char*)fBlock);
-#endif
     }
     sk_free(fBlock);
 }
@@ -2347,13 +2269,46 @@
         writer.endRecording();
     }
 }
+
+#include "SkBounder.h"
+
+class DebugHitTestBounder : public SkBounder {
+public:
+    DebugHitTestBounder(int x, int y) {
+        fLoc.set(x, y);
+    }
+
+    virtual bool onIRect(const SkIRect& bounds) SK_OVERRIDE {
+        if (bounds.contains(fLoc.x(), fLoc.y())) {
+            //
+            // Set a break-point here to see what was being drawn under
+            // the click point (just needed a line of code to stop the debugger)
+            //
+            bounds.centerX();
+        }
+        return true;
+    }
+
+private:
+    SkIPoint fLoc;
+    typedef SkBounder INHERITED;
+};
+
 void SampleView::onDraw(SkCanvas* canvas) {
     this->onDrawBackground(canvas);
 
+    DebugHitTestBounder bounder(fDebugHitTestLoc.x(), fDebugHitTestLoc.y());
+    if (fDebugHitTest) {
+        canvas->setBounder(&bounder);
+    }
+
     for (int i = 0; i < fRepeatCount; i++) {
         SkAutoCanvasRestore acr(canvas, true);
         this->onDrawContent(canvas);
     }
+
+    fDebugHitTest = false;
+    canvas->setBounder(NULL);
 }
 
 void SampleView::onDrawBackground(SkCanvas* canvas) {
diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h
index 2308a12..e2bdc5f 100644
--- a/samplecode/SampleApp.h
+++ b/samplecode/SampleApp.h
@@ -41,6 +41,23 @@
 
         kDeviceTypeCnt
     };
+
+    static bool IsGpuDeviceType(DeviceType devType) {
+    #if SK_SUPPORT_GPU
+        switch (devType) {
+            case kGPU_DeviceType:
+    #if SK_ANGLE
+            case kANGLE_DeviceType:
+    #endif // SK_ANGLE
+            case kNullGPU_DeviceType:
+                return true;
+            default:
+                return false;
+        }
+    #endif // SK_SUPPORT_GPU
+        return false;
+    }
+
     /**
      * SampleApp ports can subclass this manager class if they want to:
      *      * filter the types of devices supported
@@ -119,22 +136,24 @@
     DeviceType getDeviceType() const { return fDeviceType; }
 
 protected:
-    virtual void onDraw(SkCanvas* canvas);
-    virtual bool onHandleKey(SkKey key);
-    virtual bool onHandleChar(SkUnichar);
-    virtual void onSizeChange();
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE;
+    virtual bool onHandleKey(SkKey key) SK_OVERRIDE;
+    virtual bool onHandleChar(SkUnichar) SK_OVERRIDE;
+    virtual void onSizeChange() SK_OVERRIDE;
 
-    virtual SkCanvas* beforeChildren(SkCanvas*);
-    virtual void afterChildren(SkCanvas*);
-    virtual void beforeChild(SkView* child, SkCanvas* canvas);
-    virtual void afterChild(SkView* child, SkCanvas* canvas);
+    virtual SkCanvas* beforeChildren(SkCanvas*) SK_OVERRIDE;
+    virtual void afterChildren(SkCanvas*) SK_OVERRIDE;
+    virtual void beforeChild(SkView* child, SkCanvas* canvas) SK_OVERRIDE;
+    virtual void afterChild(SkView* child, SkCanvas* canvas) SK_OVERRIDE;
 
-    virtual bool onEvent(const SkEvent& evt);
-    virtual bool onQuery(SkEvent* evt);
+    virtual bool onEvent(const SkEvent& evt) SK_OVERRIDE;
+    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE;
 
-    virtual bool onDispatchClick(int x, int y, Click::State, void* owner);
-    virtual bool onClick(Click* click);
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    virtual bool onDispatchClick(int x, int y, Click::State, void* owner,
+                                 unsigned modi) SK_OVERRIDE;
+    virtual bool onClick(Click* click) SK_OVERRIDE;
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                      unsigned modi) SK_OVERRIDE;
 
     void registerPictFileSamples(char** argv, int argc);
     void registerPictFileSample(char** argv, int argc);
@@ -177,7 +196,6 @@
                                     // On uses a normal pipe
                                     // Off uses no pipe
     int  fUsePipeMenuItemID;
-    bool fDebugger;
 
     // The following are for the 'fatbits' drawing
     // Latest position of the mouse.
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
index a87d280..6af37b6 100644
--- a/samplecode/SampleArc.cpp
+++ b/samplecode/SampleArc.cpp
@@ -168,10 +168,11 @@
         this->inval(NULL);
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) {
      //   fSweep += SK_Scalar1;
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     virtual bool onClick(Click* click) {
@@ -188,4 +189,3 @@
 
 static SkView* MyFactory() { return new ArcsView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAvoid.cpp b/samplecode/SampleAvoid.cpp
index b8bac0d..879481b 100644
--- a/samplecode/SampleAvoid.cpp
+++ b/samplecode/SampleAvoid.cpp
@@ -103,4 +103,3 @@
 }
 
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBigBlur.cpp b/samplecode/SampleBigBlur.cpp
index f5e632c..de49c4e 100644
--- a/samplecode/SampleBigBlur.cpp
+++ b/samplecode/SampleBigBlur.cpp
@@ -47,4 +47,3 @@
 
 static SkView* MyFactory() { return new BigBlurView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBigGradient.cpp b/samplecode/SampleBigGradient.cpp
index 6a68fa0..c2e6225 100644
--- a/samplecode/SampleBigGradient.cpp
+++ b/samplecode/SampleBigGradient.cpp
@@ -47,4 +47,3 @@
 
 static SkView* MyFactory() { return new BigGradientView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp
index 3f895fc..04297e0 100644
--- a/samplecode/SampleBitmapRect.cpp
+++ b/samplecode/SampleBitmapRect.cpp
@@ -245,4 +245,3 @@
 static SkView* F1() { return new BitmapRectView2; }
 static SkViewRegister gR0(F0);
 static SkViewRegister gR1(F1);
-
diff --git a/samplecode/SampleBlur.cpp b/samplecode/SampleBlur.cpp
index 1444949..4a743cc 100644
--- a/samplecode/SampleBlur.cpp
+++ b/samplecode/SampleBlur.cpp
@@ -138,4 +138,3 @@
 
 static SkView* MyFactory() { return new BlurView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBox.cpp b/samplecode/SampleBox.cpp
index 19ff6ca..8aec992 100644
--- a/samplecode/SampleBox.cpp
+++ b/samplecode/SampleBox.cpp
@@ -53,4 +53,3 @@
 
 static SkView* MyFactory() { return new SimpleView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp
index 12e7e04..e864528 100644
--- a/samplecode/SampleCamera.cpp
+++ b/samplecode/SampleCamera.cpp
@@ -103,4 +103,3 @@
 
 static SkView* MyFactory() { return new CameraView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleCircle.cpp b/samplecode/SampleCircle.cpp
index 1444e1b..aca7460 100644
--- a/samplecode/SampleCircle.cpp
+++ b/samplecode/SampleCircle.cpp
@@ -133,4 +133,3 @@
 
 static SkView* MyFactory() { return new CircleView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleClamp.cpp b/samplecode/SampleClamp.cpp
index c521f81..2368ab2 100644
--- a/samplecode/SampleClamp.cpp
+++ b/samplecode/SampleClamp.cpp
@@ -65,4 +65,3 @@
 
 static SkView* MyFactory() { return new ClampView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleClip.cpp b/samplecode/SampleClip.cpp
index ce3736e..3a38724 100644
--- a/samplecode/SampleClip.cpp
+++ b/samplecode/SampleClip.cpp
@@ -162,4 +162,3 @@
 
 static SkView* MyFactory() { return new ClipView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleCode.h b/samplecode/SampleCode.h
index 8bd5fbd..2dacc1f 100644
--- a/samplecode/SampleCode.h
+++ b/samplecode/SampleCode.h
@@ -107,8 +107,11 @@
 
 class SampleView : public SkView {
 public:
-    SampleView() : fPipeState(SkOSMenu::kOffState),
-            fBGColor(SK_ColorWHITE), fRepeatCount(1) {
+    SampleView()
+        : fPipeState(SkOSMenu::kOffState)
+        , fBGColor(SK_ColorWHITE)
+        , fRepeatCount(1)
+        , fDebugHitTest(false) {
     }
 
     void setBGColor(SkColor color) { fBGColor = color; }
@@ -142,8 +145,10 @@
 private:
     int fRepeatCount;
 
+    bool fDebugHitTest;
+    SkIPoint fDebugHitTestLoc;
+
     typedef SkView INHERITED;
 };
 
 #endif
-
diff --git a/samplecode/SampleColorFilter.cpp b/samplecode/SampleColorFilter.cpp
index e57ccb0..41392ac 100644
--- a/samplecode/SampleColorFilter.cpp
+++ b/samplecode/SampleColorFilter.cpp
@@ -23,6 +23,7 @@
 
 #define SK_R16_BITS 5
 
+#ifdef SK_DEBUG
 static int round5_slow(int x) {
     int orig = x & 7;
     int fake = x >> 5;
@@ -38,15 +39,17 @@
     }
     return trunc + bias;
 }
+#endif
 
 static int round5_fast(int x) {
     int result = x + 3 - (x >> 5) + (x >> 7);
     result >>= 3;
-
+#ifdef SK_DEBUG
     {
         int r2 = round5_slow(x);
         SkASSERT(r2 == result);
     }
+#endif
     return result;
 }
 
@@ -183,7 +186,7 @@
             SkXfermode::kDstATop_Mode,
             SkXfermode::kXor_Mode,
             SkXfermode::kPlus_Mode,
-            SkXfermode::kMultiply_Mode,
+            SkXfermode::kModulate_Mode,
         };
 
         static const SkColor gColors[] = {
@@ -216,4 +219,3 @@
 
 static SkView* MyFactory() { return new ColorFilterView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleComplexClip.cpp b/samplecode/SampleComplexClip.cpp
index ff343dd..30f94f7 100644
--- a/samplecode/SampleComplexClip.cpp
+++ b/samplecode/SampleComplexClip.cpp
@@ -151,4 +151,3 @@
 
 static SkView* MyFactory() { return new ComplexClipView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleConcavePaths.cpp b/samplecode/SampleConcavePaths.cpp
index 549e9e2..2fc0107 100644
--- a/samplecode/SampleConcavePaths.cpp
+++ b/samplecode/SampleConcavePaths.cpp
@@ -137,9 +137,10 @@
         }
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) {
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
 private:
@@ -150,4 +151,3 @@
 
 static SkView* MyFactory() { return new ConcavePathView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleCull.cpp b/samplecode/SampleCull.cpp
index 18d9bdc..18f2d0c 100644
--- a/samplecode/SampleCull.cpp
+++ b/samplecode/SampleCull.cpp
@@ -193,4 +193,3 @@
 
 static SkView* MyFactory() { return new CullView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDash.cpp b/samplecode/SampleDash.cpp
index 2c16e54..20e2896 100644
--- a/samplecode/SampleDash.cpp
+++ b/samplecode/SampleDash.cpp
@@ -92,4 +92,3 @@
 
 static SkView* MyFactory() { return new DashView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDecode.cpp b/samplecode/SampleDecode.cpp
index 48759c0..e96be6a 100644
--- a/samplecode/SampleDecode.cpp
+++ b/samplecode/SampleDecode.cpp
@@ -74,4 +74,3 @@
 
 static SkView* MyFactory() { return new DecodeView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp
index 259bc94..2468df7 100644
--- a/samplecode/SampleDither.cpp
+++ b/samplecode/SampleDither.cpp
@@ -182,4 +182,3 @@
 
 static SkView* MyFactory() { return new DitherView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDitherBitmap.cpp b/samplecode/SampleDitherBitmap.cpp
index b84d0dd..df727c4 100644
--- a/samplecode/SampleDitherBitmap.cpp
+++ b/samplecode/SampleDitherBitmap.cpp
@@ -145,4 +145,3 @@
 
 static SkView* MyFactory() { return new DitherBitmapView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDraw.cpp b/samplecode/SampleDraw.cpp
index e1f62fe..616650f 100644
--- a/samplecode/SampleDraw.cpp
+++ b/samplecode/SampleDraw.cpp
@@ -377,4 +377,3 @@
 
 static SkView* MyFactory() { return new DrawView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleEmboss.cpp b/samplecode/SampleEmboss.cpp
index 41c7455..faf7027 100644
--- a/samplecode/SampleEmboss.cpp
+++ b/samplecode/SampleEmboss.cpp
@@ -70,4 +70,3 @@
 
 static SkView* MyFactory() { return new EmbossView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleEmptyPath.cpp b/samplecode/SampleEmptyPath.cpp
index 49ad7c5..51c570e 100644
--- a/samplecode/SampleEmptyPath.cpp
+++ b/samplecode/SampleEmptyPath.cpp
@@ -127,4 +127,3 @@
 
 static SkView* MyFactory() { return new EmptyPathView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleEncode.cpp b/samplecode/SampleEncode.cpp
index afcde03..594c2a1 100644
--- a/samplecode/SampleEncode.cpp
+++ b/samplecode/SampleEncode.cpp
@@ -215,9 +215,10 @@
         }
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) {
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
 private:
@@ -228,4 +229,3 @@
 
 static SkView* MyFactory() { return new EncodeView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFatBits.cpp b/samplecode/SampleFatBits.cpp
index 29fd865..7ac87a7 100644
--- a/samplecode/SampleFatBits.cpp
+++ b/samplecode/SampleFatBits.cpp
@@ -120,7 +120,6 @@
                 break;
             case kStroke_Style:
                 paint->setStrokeWidth(SK_Scalar1);
-//                paint->setStrokeWidth(SK_Scalar1 + SK_Scalar1/500);
                 break;
         }
         paint->setAntiAlias(aa);
@@ -421,7 +420,8 @@
         }
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
         SkPoint pt = { x, y };
         int index = -1;
         SkScalar tol = 12;
@@ -433,7 +433,7 @@
         return new IndexClick(this, index);
     }
 
-    virtual bool onClick(Click* click) {
+    virtual bool onClick(Click* click) SK_OVERRIDE {
         int index = IndexClick::GetIndex(click);
         if (index >= 0 && index <= 1) {
             fPts[index] = click->fCurr;
@@ -456,4 +456,3 @@
 
 static SkView* MyFactory() { return new DrawLineView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFillType.cpp b/samplecode/SampleFillType.cpp
index b291834..aac6aed 100644
--- a/samplecode/SampleFillType.cpp
+++ b/samplecode/SampleFillType.cpp
@@ -94,4 +94,3 @@
 
 static SkView* MyFactory() { return new FillTypeView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFilter.cpp b/samplecode/SampleFilter.cpp
index 75eb7d7..3eb9cdc 100644
--- a/samplecode/SampleFilter.cpp
+++ b/samplecode/SampleFilter.cpp
@@ -142,4 +142,3 @@
 
 static SkView* MyFactory() { return new FilterView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFilter2.cpp b/samplecode/SampleFilter2.cpp
index 8582447..f3a8b46 100644
--- a/samplecode/SampleFilter2.cpp
+++ b/samplecode/SampleFilter2.cpp
@@ -120,4 +120,3 @@
 
 static SkView* MyFactory() { return new Filter2View; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFontCache.cpp b/samplecode/SampleFontCache.cpp
index d5c2add..c5f4457 100644
--- a/samplecode/SampleFontCache.cpp
+++ b/samplecode/SampleFontCache.cpp
@@ -139,4 +139,3 @@
 
 static SkView* MyFactory() { return new FontCacheView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFontScalerTest.cpp b/samplecode/SampleFontScalerTest.cpp
index 8494e75..a01de03 100644
--- a/samplecode/SampleFontScalerTest.cpp
+++ b/samplecode/SampleFontScalerTest.cpp
@@ -138,4 +138,3 @@
 
 static SkView* MyFactory() { return new FontScalerTestView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFuzz.cpp b/samplecode/SampleFuzz.cpp
index c082c4d..a5d8917 100644
--- a/samplecode/SampleFuzz.cpp
+++ b/samplecode/SampleFuzz.cpp
@@ -379,4 +379,3 @@
 
 static SkView* MyFactory() { return new FuzzView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleGradients.cpp b/samplecode/SampleGradients.cpp
index f1a2dd9..fead714 100644
--- a/samplecode/SampleGradients.cpp
+++ b/samplecode/SampleGradients.cpp
@@ -182,4 +182,3 @@
 
 static SkView* MyFactory() { return new GradientsView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleHairCurves.cpp b/samplecode/SampleHairCurves.cpp
index 2bb2fa2..d121cf0 100644
--- a/samplecode/SampleHairCurves.cpp
+++ b/samplecode/SampleHairCurves.cpp
@@ -109,4 +109,3 @@
 
 static SkView* MyFactory() { return new HairCurvesView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleHairModes.cpp b/samplecode/SampleHairModes.cpp
index 59cb25c..4168ffd 100644
--- a/samplecode/SampleHairModes.cpp
+++ b/samplecode/SampleHairModes.cpp
@@ -135,4 +135,3 @@
 
 static SkView* MyFactory() { return new HairModesView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp
index 28ed68f..fb42e2c 100644
--- a/samplecode/SampleHairline.cpp
+++ b/samplecode/SampleHairline.cpp
@@ -261,10 +261,11 @@
         }
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) {
         fDoAA = !fDoAA;
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
 
@@ -276,4 +277,3 @@
 
 static SkView* MyFactory() { return new HairlineView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLCD.cpp b/samplecode/SampleLCD.cpp
index 7136448..130a28c 100644
--- a/samplecode/SampleLCD.cpp
+++ b/samplecode/SampleLCD.cpp
@@ -65,4 +65,3 @@
 
 static SkView* MyFactory() { return new LCDView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLayerMask.cpp b/samplecode/SampleLayerMask.cpp
index d65c131..2e70833 100644
--- a/samplecode/SampleLayerMask.cpp
+++ b/samplecode/SampleLayerMask.cpp
@@ -72,4 +72,3 @@
 
 static SkView* MyFactory() { return new LayerMaskView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp
index cf6b009..b1311ee 100644
--- a/samplecode/SampleLayers.cpp
+++ b/samplecode/SampleLayers.cpp
@@ -249,10 +249,11 @@
         canvas->restore();
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
         this->inval(NULL);
 
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     virtual bool onClick(Click* click) {
@@ -272,4 +273,3 @@
 
 static SkView* MyFactory() { return new LayersView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLines.cpp b/samplecode/SampleLines.cpp
index 57d7050..a9d070f 100644
--- a/samplecode/SampleLines.cpp
+++ b/samplecode/SampleLines.cpp
@@ -113,4 +113,3 @@
 
 static SkView* MyFactory() { return new LinesView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleManyRects.cpp b/samplecode/SampleManyRects.cpp
new file mode 100644
index 0000000..d5d71f4
--- /dev/null
+++ b/samplecode/SampleManyRects.cpp
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkShader.h"
+#include "SkView.h"
+
+/**
+ * Animated sample used to develop batched rect implementation in GrInOrderDrawBuffer.
+ */
+class ManyRectsView : public SampleView {
+private:
+    enum {
+        N = 1000,
+    };
+
+public:
+    ManyRectsView() {}
+
+protected:
+    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "ManyRects");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkISize dsize = canvas->getDeviceSize();
+        canvas->clear(0xFFF0E0F0);
+
+        for (int i = 0; i < N; ++i) {
+            SkRect rect = SkRect::MakeWH(SkIntToScalar(fRandom.nextRangeU(10, 100)),
+                                         SkIntToScalar(fRandom.nextRangeU(10, 100)));
+            int x = fRandom.nextRangeU(0, dsize.fWidth);
+            int y = fRandom.nextRangeU(0, dsize.fHeight);
+            canvas->save();
+
+            canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+            // Rotation messes up the GPU batching because of the clip below. We don't notice
+            // that the rect is inside the clip so the clip changes interrupt batching.
+            if (false) {
+                SkMatrix rotate;
+                rotate.setRotate(fRandom.nextUScalar1() * 360,
+                                 SkIntToScalar(x) + SkScalarHalf(rect.fRight),
+                                 SkIntToScalar(y) + SkScalarHalf(rect.fBottom));
+                canvas->concat(rotate);
+            }
+            SkRect clipRect = rect;
+            // This clip will always contain the entire rect. It's here to give the GPU batching
+            // code a little more challenge.
+            clipRect.outset(10, 10);
+            canvas->clipRect(clipRect);
+            SkPaint paint;
+            paint.setColor(fRandom.nextU());
+            canvas->drawRect(rect, paint);
+            canvas->restore();
+        }
+        this->inval(NULL);
+    }
+
+private:
+    SkMWCRandom fRandom;
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ManyRectsView; }
+static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleMeasure.cpp b/samplecode/SampleMeasure.cpp
index 18b3720..257f7d9 100644
--- a/samplecode/SampleMeasure.cpp
+++ b/samplecode/SampleMeasure.cpp
@@ -119,4 +119,3 @@
 
 static SkView* MyFactory() { return new MeasureView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleMipMap.cpp b/samplecode/SampleMipMap.cpp
index f3a12cd..e93636d 100644
--- a/samplecode/SampleMipMap.cpp
+++ b/samplecode/SampleMipMap.cpp
@@ -158,4 +158,3 @@
 
 static SkView* MyFactory() { return new MipMapView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleMovie.cpp b/samplecode/SampleMovie.cpp
index c85976b..560151c 100644
--- a/samplecode/SampleMovie.cpp
+++ b/samplecode/SampleMovie.cpp
@@ -65,4 +65,3 @@
 
 static SkView* MyFactory() { return new AnimGifView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleOvalTest.cpp b/samplecode/SampleOvalTest.cpp
index 88b7d5f..5acf568 100644
--- a/samplecode/SampleOvalTest.cpp
+++ b/samplecode/SampleOvalTest.cpp
@@ -114,4 +114,3 @@
 
 static SkView* MyFactory() { return new OvalTestView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleOverflow.cpp b/samplecode/SampleOverflow.cpp
index 3cf30ad..35adbf3 100644
--- a/samplecode/SampleOverflow.cpp
+++ b/samplecode/SampleOverflow.cpp
@@ -106,4 +106,3 @@
 
 static SkView* MyFactory() { return new OverflowView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePatch.cpp b/samplecode/SamplePatch.cpp
index 90aaf4c..3da4b0c 100644
--- a/samplecode/SamplePatch.cpp
+++ b/samplecode/SamplePatch.cpp
@@ -310,13 +310,14 @@
         return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
         for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
             if (hittest(fPts[i], x, y)) {
                 return new PtClick(this, i);
             }
         }
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     virtual bool onClick(Click* click) {
@@ -333,4 +334,3 @@
 
 static SkView* MyFactory() { return new PatchView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePath.cpp b/samplecode/SamplePath.cpp
index b07307a..fa6ad43 100644
--- a/samplecode/SamplePath.cpp
+++ b/samplecode/SamplePath.cpp
@@ -202,10 +202,10 @@
         this->inval(NULL);
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
         fShowHairline = !fShowHairline;
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
 private:
@@ -216,4 +216,3 @@
 
 static SkView* MyFactory() { return new PathView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp
index b999c39..b7583ab 100644
--- a/samplecode/SamplePathClip.cpp
+++ b/samplecode/SamplePathClip.cpp
@@ -88,4 +88,3 @@
 
 static SkView* MyFactory() { return new PathClipView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp
index 35d3260..ac77f21 100644
--- a/samplecode/SamplePathEffects.cpp
+++ b/samplecode/SamplePathEffects.cpp
@@ -188,4 +188,3 @@
 
 static SkView* MyFactory() { return new PathEffectView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePathFill.cpp b/samplecode/SamplePathFill.cpp
index 9c7cdc6..1a79d7f 100644
--- a/samplecode/SamplePathFill.cpp
+++ b/samplecode/SamplePathFill.cpp
@@ -144,4 +144,3 @@
 
 static SkView* MyFactory() { return new PathFillView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePictFile.cpp b/samplecode/SamplePictFile.cpp
index b53ba83..65279cb 100644
--- a/samplecode/SamplePictFile.cpp
+++ b/samplecode/SamplePictFile.cpp
@@ -139,4 +139,3 @@
 static SkView* MyFactory() { return new PictFileView; }
 static SkViewRegister reg(MyFactory);
 #endif
-
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
index 17a0d75..eaec829 100644
--- a/samplecode/SamplePicture.cpp
+++ b/samplecode/SamplePicture.cpp
@@ -153,11 +153,13 @@
         canvas->drawPicture(*pict);
         canvas->restore();
 
+#ifdef SK_DEVELOPER
         if (false) {
             SkDebugfDumper dumper;
             SkDumpCanvas dumpCanvas(&dumper);
             dumpCanvas.drawPicture(*pict);
         }
+#endif
 
         // test that we can re-record a subpicture, and see the results
 
@@ -194,4 +196,3 @@
 
 static SkView* MyFactory() { return new PictureView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePoints.cpp b/samplecode/SamplePoints.cpp
index 705a7b1..ac2f625 100644
--- a/samplecode/SamplePoints.cpp
+++ b/samplecode/SamplePoints.cpp
@@ -82,4 +82,3 @@
 
 static SkView* MyFactory() { return new PointsView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePolyToPoly.cpp b/samplecode/SamplePolyToPoly.cpp
index 88adae9..93a5351 100644
--- a/samplecode/SamplePolyToPoly.cpp
+++ b/samplecode/SamplePolyToPoly.cpp
@@ -167,4 +167,3 @@
 
 static SkView* MyFactory() { return new PolyToPolyView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
index 8b3a03c..cb860fc 100644
--- a/samplecode/SampleRegion.cpp
+++ b/samplecode/SampleRegion.cpp
@@ -274,13 +274,13 @@
 
             {
                 char    buffer[1000];
-                size_t  size = tmp.writeToMemory(NULL);
+                SkDEBUGCODE(size_t  size = ) tmp.writeToMemory(NULL);
                 SkASSERT(size <= sizeof(buffer));
-                size_t  size2 = tmp.writeToMemory(buffer);
+                SkDEBUGCODE(size_t  size2 = ) tmp.writeToMemory(buffer);
                 SkASSERT(size == size2);
 
                 SkRegion    tmp3;
-                size2 = tmp3.readFromMemory(buffer);
+                SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer);
                 SkASSERT(size == size2);
 
                 SkASSERT(tmp3 == tmp);
@@ -415,4 +415,3 @@
 
 static SkView* MyFactory() { return new RegionView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleRepeatTile.cpp b/samplecode/SampleRepeatTile.cpp
index 54c79ef..c97c228 100644
--- a/samplecode/SampleRepeatTile.cpp
+++ b/samplecode/SampleRepeatTile.cpp
@@ -67,10 +67,10 @@
         canvas->drawPaint(paint);
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
         this->inval(NULL);
 
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     virtual bool onClick(Click* click) {
@@ -90,4 +90,3 @@
 
 static SkView* MyFactory() { return new RepeatTileView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleRotateCircles.cpp b/samplecode/SampleRotateCircles.cpp
new file mode 100644
index 0000000..1f586be
--- /dev/null
+++ b/samplecode/SampleRotateCircles.cpp
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkRandom.h"
+#include "SkRRect.h"
+#include "SkColorPriv.h"
+
+static void rotateAbout(SkCanvas* canvas, SkScalar degrees,
+                        SkScalar cx, SkScalar cy) {
+    canvas->translate(cx, cy);
+    canvas->rotate(degrees);
+    canvas->translate(-cx, -cy);
+}
+
+class RotateCirclesView : public SampleView {
+public:
+    RotateCirclesView() {
+        this->setBGColor(SK_ColorLTGRAY);
+
+        fAngle = 0;
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "RotateCircles");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkRandom rand;
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStrokeWidth(20);
+
+        SkScalar cx = 240;
+        SkScalar cy = 240;
+        SkScalar DX = 240 * 2;
+        SkColor color = 0;
+
+        float scale = 1;
+        float sign = 0.3f;
+        for (SkScalar rad = 200; rad >= 20; rad -= 15) {
+            sign = -sign;
+            scale += 0.2f;
+
+            paint.setColor(rand.nextU());
+            paint.setAlpha(0xFF);
+            color = ~color;
+
+            paint.setStyle(SkPaint::kFill_Style);
+
+            canvas->save();
+            rotateAbout(canvas, fAngle * scale * sign, cx, cy);
+            canvas->drawCircle(cx, cy, rad, paint);
+            canvas->restore();
+
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(rad*2);
+
+            canvas->save();
+            rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy);
+            canvas->drawCircle(cx + DX, cy, 10, paint);
+            canvas->restore();
+
+            canvas->save();
+            rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy + DX);
+            canvas->drawCircle(cx + DX, cy + DX, 10, paint);
+            canvas->restore();
+
+        }
+
+        fAngle = (fAngle + 1) % 360;
+        this->inval(NULL);
+    }
+
+private:
+    int fAngle;
+    typedef SkView INHERITED;
+};
+
+class TestCirclesView : public SampleView {
+public:
+    TestCirclesView() {
+    }
+
+protected:
+    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "RotateCircles2");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void draw_real_circle(SkCanvas* canvas, SkScalar radius) {
+        int w = SkScalarCeilToInt(radius * 2);
+        int h = w;
+
+        SkBitmap bm;
+        bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+        bm.allocPixels();
+        bm.eraseColor(0);
+
+        SkAutoLockPixels alp(bm);
+
+        SkScalar cx = radius;
+        SkScalar cy = radius;
+        for (int y = 0; y < h; y += 1) {
+            for (int x = 0; x < w; x += 1) {
+                float d = sqrtf((x - cx)*(x - cx) + (y - cy)*(y - cy));
+                if (d <= radius) {
+                    *bm.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0);
+                }
+            }
+        }
+
+        canvas->drawBitmap(bm, 0, 0, NULL);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkScalar radius = 256;
+        canvas->translate(10, 10);
+
+        draw_real_circle(canvas, radius);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        paint.setColor(0x80FF0000);
+        canvas->drawCircle(radius, radius, radius, paint);
+
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(radius);
+        paint.setColor(0x8000FF00);
+        canvas->drawCircle(radius, radius, radius/2, paint);
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) {
+    const SkScalar TOL = 7;
+    return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL;
+}
+
+static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
+    SkPath::RawIter iter(path);
+    SkPoint pts[4];
+    SkPath::Verb verb;
+
+    int count = 0;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+            case SkPath::kLine_Verb:
+            case SkPath::kQuad_Verb:
+            case SkPath::kCubic_Verb:
+                storage[count++] = pts[0];
+                break;
+            default:
+                break;
+        }
+    }
+    return count;
+}
+
+#include "SkPathMeasure.h"
+
+class TestStrokeView : public SampleView {
+    enum {
+        SKELETON_COLOR = 0xFF0000FF,
+        WIREFRAME_COLOR = 0x80FF0000
+    };
+
+    enum {
+        kCount = 9
+    };
+    SkPoint fPts[kCount];
+    SkScalar fWidth, fDWidth;
+public:
+    TestStrokeView() {
+        this->setBGColor(SK_ColorLTGRAY);
+
+        fPts[0].set(50, 200);
+        fPts[1].set(50, 100);
+        fPts[2].set(150, 50);
+        fPts[3].set(300, 50);
+
+        fPts[4].set(350, 200);
+        fPts[5].set(350, 100);
+        fPts[6].set(450, 50);
+
+        fPts[7].set(200, 200);
+        fPts[8].set(400, 400);
+
+        fWidth = 50;
+        fDWidth = 0.25f;
+    }
+
+protected:
+    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "RotateCircles3");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
+                     bool show_lines) {
+        SkPaint paint;
+        paint.setColor(color);
+        paint.setAlpha(0x80);
+
+        int n = path.countPoints();
+        SkAutoSTArray<32, SkPoint> pts(n);
+        if (show_lines) {
+            path.getPoints(pts.get(), n);
+            canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts.get(), paint);
+        } else {
+            n = getOnCurvePoints(path, pts.get());
+        }
+        paint.setStrokeWidth(5);
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
+    }
+
+    void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
+                   SkColor color) {
+        const SkScalar radius = width / 2;
+
+        SkPathMeasure meas(path, false);
+        SkScalar total = meas.getLength();
+
+        SkScalar delta = 8;
+        SkPaint paint;
+        paint.setColor(color);
+
+        SkPoint pos, tan;
+        for (SkScalar dist = 0; dist <= total; dist += delta) {
+            if (meas.getPosTan(dist, &pos, &tan)) {
+                tan.scale(radius);
+                tan.rotateCCW();
+                canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
+                                 pos.x() - tan.x(), pos.y() - tan.y(), paint);
+            }
+        }
+    }
+
+    void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+
+        paint.setColor(SKELETON_COLOR);
+        canvas->drawPath(path, paint);
+        draw_points(canvas, path, SKELETON_COLOR, true);
+
+        draw_ribs(canvas, path, width, 0xFF00FF00);
+
+        SkPath fill;
+
+        SkPaint p;
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(width);
+        p.getFillPath(path, &fill);
+
+        paint.setColor(WIREFRAME_COLOR);
+        canvas->drawPath(fill, paint);
+        draw_points(canvas, fill, WIREFRAME_COLOR, false);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPath path;
+        SkScalar width = fWidth;
+
+        path.moveTo(fPts[0]);
+        path.cubicTo(fPts[1], fPts[2], fPts[3]);
+        draw_stroke(canvas, path, width);
+
+        path.reset();
+        path.moveTo(fPts[4]);
+        path.quadTo(fPts[5], fPts[6]);
+        draw_stroke(canvas, path, width);
+
+        SkScalar rad = 32;
+        SkRect r;
+        r.set(&fPts[7], 2);
+        path.reset();
+        SkRRect rr;
+        rr.setRectXY(r, rad, rad);
+        path.addRRect(rr);
+        draw_stroke(canvas, path, width);
+
+        path.reset();
+        SkRRect rr2;
+        rr.inset(width/2, width/2, &rr2);
+        path.addRRect(rr2, SkPath::kCCW_Direction);
+        rr.inset(-width/2, -width/2, &rr2);
+        path.addRRect(rr2, SkPath::kCW_Direction);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0x40FF8844);
+        canvas->drawPath(path, paint);
+
+        fWidth += fDWidth;
+        if (fDWidth > 0 && fWidth > 100) {
+            fDWidth = -fDWidth;
+        } else if (fDWidth < 0 && fWidth < 10) {
+            fDWidth = -fDWidth;
+        }
+        this->inval(NULL);
+    }
+
+    class MyClick : public Click {
+    public:
+        int fIndex;
+        MyClick(SkView* target, int index) : Click(target), fIndex(index) {}
+    };
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
+            if (hittest(fPts[i], x, y)) {
+                return new MyClick(this, i);
+            }
+        }
+        return this->INHERITED::onFindClickHandler(x, y, modi);
+    }
+
+    virtual bool onClick(Click* click) {
+        int index = ((MyClick*)click)->fIndex;
+        fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
+                           SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
+        this->inval(NULL);
+        return true;
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* F0() { return new RotateCirclesView; }
+static SkViewRegister gR0(F0);
+static SkView* F1() { return new TestCirclesView; }
+static SkViewRegister gR1(F1);
+static SkView* F2() { return new TestStrokeView; }
+static SkViewRegister gR2(F2);
diff --git a/samplecode/SampleShaderText.cpp b/samplecode/SampleShaderText.cpp
index a83b099..603d67e 100644
--- a/samplecode/SampleShaderText.cpp
+++ b/samplecode/SampleShaderText.cpp
@@ -213,4 +213,3 @@
 
 static SkView* MyFactory() { return new ShaderTextView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleShaders.cpp b/samplecode/SampleShaders.cpp
index 78b63a3..253388d 100644
--- a/samplecode/SampleShaders.cpp
+++ b/samplecode/SampleShaders.cpp
@@ -121,9 +121,10 @@
         canvas->drawRect(r, paint);
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     virtual bool onClick(Click* click) {
@@ -138,4 +139,3 @@
 
 static SkView* MyFactory() { return new ShaderView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleSkLayer.cpp b/samplecode/SampleSkLayer.cpp
index e7c0ddb..8d30ff6 100644
--- a/samplecode/SampleSkLayer.cpp
+++ b/samplecode/SampleSkLayer.cpp
@@ -56,7 +56,7 @@
     if (dst[0] != x0 || dst[1] != x1 || dst[2] != x2 ||
         dst[3] != y0 || dst[4] != y1 || dst[5] != y2) {
         SkString str;
-        dst.toDumpString(&str);
+        dst.toString(&str);
         SkDebugf("3x3: expected 3x3 [%g %g %g] [%g %g %g] bug got %s\n",
                  x0, x1, x2, y0, y1, y2, str.c_str());
     }
@@ -116,7 +116,7 @@
     SkString matrixStr;
 
     layer->getLocalTransform(&matrix);
-    matrix.toDumpString(&matrixStr);
+    matrix.toString(&matrixStr);
 
     for (int j = 0; j < tab; j++) {
         SkDebugf(" ");
@@ -243,4 +243,3 @@
 
 static SkView* MyFactory() { return new SkLayerView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleSlides.cpp b/samplecode/SampleSlides.cpp
index b200473..0766d17 100644
--- a/samplecode/SampleSlides.cpp
+++ b/samplecode/SampleSlides.cpp
@@ -732,4 +732,3 @@
 
 static SkView* MyFactory() { return new SlideView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleSpiral.cpp b/samplecode/SampleSpiral.cpp
index be6e728..5eda3c6 100644
--- a/samplecode/SampleSpiral.cpp
+++ b/samplecode/SampleSpiral.cpp
@@ -61,4 +61,3 @@
 
 static SkView* MyFactory() { return new SpiralView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
index ca5135d..d289cc6 100644
--- a/samplecode/SampleStrokePath.cpp
+++ b/samplecode/SampleStrokePath.cpp
@@ -209,9 +209,10 @@
         drawSet(canvas, &paint);
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 private:
     typedef SampleView INHERITED;
@@ -221,4 +222,3 @@
 
 static SkView* MyFactory() { return new StrokePathView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleStrokeRect.cpp b/samplecode/SampleStrokeRect.cpp
index edf9228..59e0a46 100644
--- a/samplecode/SampleStrokeRect.cpp
+++ b/samplecode/SampleStrokeRect.cpp
@@ -73,4 +73,3 @@
 
 static SkView* MyFactory() { return new StrokeRectSample; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleStrokeText.cpp b/samplecode/SampleStrokeText.cpp
index 4ceb0d5..4e9fe0d 100644
--- a/samplecode/SampleStrokeText.cpp
+++ b/samplecode/SampleStrokeText.cpp
@@ -146,4 +146,3 @@
 
 static SkView* MyFactory() { return new StrokeTextView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTests.cpp b/samplecode/SampleTests.cpp
index ea317e1..d0fab41 100644
--- a/samplecode/SampleTests.cpp
+++ b/samplecode/SampleTests.cpp
@@ -115,4 +115,3 @@
 
 static SkView* MyFactory() { return new TestsView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleText.cpp b/samplecode/SampleText.cpp
index 6483218..ee447d3 100644
--- a/samplecode/SampleText.cpp
+++ b/samplecode/SampleText.cpp
@@ -120,10 +120,11 @@
     SkPowerMode(SkScalar exponent) { this->init(exponent); }
 
     virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]);
+                        const SkAlpha aa[]) const SK_OVERRIDE;
 
     typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&);
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPowerMode)
 
 private:
@@ -160,7 +161,7 @@
 }
 
 void SkPowerMode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                         const SkAlpha aa[]) {
+                         const SkAlpha aa[]) const {
     for (int i = 0; i < count; i++) {
         SkPMColor c = src[i];
         int r = SkGetPackedR32(c);
@@ -173,6 +174,13 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkPowerMode::toString(SkString* str) const {
+    str->append("SkPowerMode: exponent ");
+    str->appendScalar(fExp);
+}
+#endif
+
 static const struct {
     const char* fName;
     uint32_t    fFlags;
@@ -305,10 +313,11 @@
         }
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
+                                              unsigned modi) SK_OVERRIDE {
         fClickX = x;
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     virtual bool onClick(Click* click) {
@@ -327,4 +336,3 @@
 
 static SkView* MyFactory() { return new TextSpeedView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTextAlpha.cpp b/samplecode/SampleTextAlpha.cpp
index ac24f08..8a248c3 100644
--- a/samplecode/SampleTextAlpha.cpp
+++ b/samplecode/SampleTextAlpha.cpp
@@ -120,4 +120,3 @@
 
 static SkView* MyFactory() { return new TextAlphaView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTextBox.cpp b/samplecode/SampleTextBox.cpp
index a021dbd..61a18e2 100644
--- a/samplecode/SampleTextBox.cpp
+++ b/samplecode/SampleTextBox.cpp
@@ -113,4 +113,3 @@
 
 static SkView* MyFactory() { return new TextBoxView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTextOnPath.cpp b/samplecode/SampleTextOnPath.cpp
index 976f3c8..2337a08 100644
--- a/samplecode/SampleTextOnPath.cpp
+++ b/samplecode/SampleTextOnPath.cpp
@@ -150,10 +150,10 @@
             this->inval(NULL);
     }
 
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
         fHints += 1;
         this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
+        return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
     virtual bool onClick(Click* click) {
diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp
index 07dcb40..de84c35 100644
--- a/samplecode/SampleTextureDomain.cpp
+++ b/samplecode/SampleTextureDomain.cpp
@@ -61,10 +61,8 @@
         // the constrainted texture domain.
         // Note:  GPU-backed bitmaps follow a different rendering path
         // when copying from one GPU device to another.
-        SkRefPtr<SkDevice> primaryDevice(canvas->getDevice());
-        SkRefPtr<SkDevice> secondDevice(canvas->createCompatibleDevice(
+        SkAutoTUnref<SkDevice> secondDevice(canvas->createCompatibleDevice(
                 SkBitmap::kARGB_8888_Config, 5, 5, true));
-        secondDevice->unref();
         SkCanvas secondCanvas(secondDevice.get());
 
         srcRect.setXYWH(1, 1, 3, 3);
@@ -113,4 +111,3 @@
 
 static SkView* MyFactory() { return new TextureDomainView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTiling.cpp b/samplecode/SampleTiling.cpp
index 59e88ea..5c2a352 100644
--- a/samplecode/SampleTiling.cpp
+++ b/samplecode/SampleTiling.cpp
@@ -171,4 +171,3 @@
 
 static SkView* MyFactory() { return new TilingView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTinyBitmap.cpp b/samplecode/SampleTinyBitmap.cpp
index d682317..16cbce4 100644
--- a/samplecode/SampleTinyBitmap.cpp
+++ b/samplecode/SampleTinyBitmap.cpp
@@ -80,4 +80,3 @@
 
 static SkView* MyFactory() { return new TinyBitmapView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTriangles.cpp b/samplecode/SampleTriangles.cpp
deleted file mode 100644
index d6adc16..0000000
--- a/samplecode/SampleTriangles.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-
-/*
- * 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 "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkConcaveToTriangles.h"
-
-#define SIZE    SkIntToScalar(150)
-
-typedef void (*PathProc)(SkPath*);
-
-static void make_path0(SkPath* path) {
-    SkRect r;
-    r.set(0, 0, SIZE, SIZE);
-    path->addRect(r);
-}
-
-static void make_path1(SkPath* path) {
-    SkRect r;
-    r.set(0, 0, SIZE, SIZE);
-    path->addRoundRect(r, SIZE/4, SIZE/4);
-}
-
-static void make_path2(SkPath* path) {
-    SkRect r;
-    r.set(0, 0, SIZE, SIZE);
-    path->addOval(r);
-}
-
-static const PathProc gProcs[] = {
-    make_path0,
-    make_path1,
-    make_path2,
-};
-
-#define COUNT_PROCS SK_ARRAY_COUNT(gProcs)
-
-class TriangleView : public SkView {
-public:
-    SkPath fPaths[COUNT_PROCS];
-
-    TriangleView() {
-        for (size_t i = 0; i < COUNT_PROCS; i++) {
-            gProcs[i](&fPaths[i]);
-        }
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Triangles");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorGRAY);
-    }
-
-    static void draw_path(SkCanvas* canvas, const SkPaint& pathPaint,
-                          const SkPath& path, const SkPaint& triPaint) {
-        canvas->drawPath(path, pathPaint);
-
-        int n = path.getPoints(NULL, 0);
-        SkPoint* pts = new SkPoint[n];
-        path.getPoints(pts, n);
-
-        SkTDArray<SkPoint> triangles;
-        if (SkConcaveToTriangles(n, pts, &triangles)) {
-            canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
-                                 triangles.count(), triangles.begin(), NULL,
-                                 NULL, NULL, NULL, 0, triPaint);
-        }
-
-        SkPaint paint;
-        paint.setColor(SK_ColorGREEN);
-        paint.setStrokeWidth(SkIntToScalar(4));
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, paint);
-        delete[] pts;
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-
-        canvas->translate(SIZE/2, SIZE/2);
-
-        SkPaint pathPaint, triPaint;
-
-        pathPaint.setColor(SK_ColorBLUE);
-        pathPaint.setStrokeWidth(SIZE / 12);
-
-        triPaint.setColor(SK_ColorRED);
-        triPaint.setStyle(SkPaint::kStroke_Style);
-
-        for (size_t i = 0; i < COUNT_PROCS; i++) {
-            pathPaint.setStyle(SkPaint::kFill_Style);
-            draw_path(canvas, pathPaint, fPaths[i], triPaint);
-
-            canvas->save();
-            canvas->translate(0, SIZE * 6 / 5);
-
-            pathPaint.setStyle(SkPaint::kStroke_Style);
-            draw_path(canvas, pathPaint, fPaths[i], triPaint);
-
-            canvas->restore();
-            canvas->translate(SIZE * 6 / 5, 0);
-        }
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TriangleView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTypeface.cpp b/samplecode/SampleTypeface.cpp
index 9bc8762..ad1a16f 100644
--- a/samplecode/SampleTypeface.cpp
+++ b/samplecode/SampleTypeface.cpp
@@ -132,4 +132,3 @@
 
 static SkView* MyFactory() { return new TypefaceView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleUnitMapper.cpp b/samplecode/SampleUnitMapper.cpp
index 8cc9afb..c9a8f9f 100644
--- a/samplecode/SampleUnitMapper.cpp
+++ b/samplecode/SampleUnitMapper.cpp
@@ -170,4 +170,3 @@
 
 static SkView* MyFactory() { return new UnitMapperView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
index bc02256..152cb13 100644
--- a/samplecode/SampleVertices.cpp
+++ b/samplecode/SampleVertices.cpp
@@ -231,4 +231,3 @@
 
 static SkView* MyFactory() { return new VerticesView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleWarp.cpp b/samplecode/SampleWarp.cpp
index 323df80..e9455a6 100644
--- a/samplecode/SampleWarp.cpp
+++ b/samplecode/SampleWarp.cpp
@@ -471,4 +471,3 @@
 
 static SkView* MyFactory() { return new WarpView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleWritePixels.cpp b/samplecode/SampleWritePixels.cpp
index f9df8f5..04f3484 100644
--- a/samplecode/SampleWritePixels.cpp
+++ b/samplecode/SampleWritePixels.cpp
@@ -67,4 +67,3 @@
 
 static SkView* MyFactory() { return new WritePixelsView; }
 static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp
index 9d368a2..a4e410c 100644
--- a/samplecode/SampleXfermodesBlur.cpp
+++ b/samplecode/SampleXfermodesBlur.cpp
@@ -159,7 +159,7 @@
             { SkXfermode::kXor_Mode,      "Xor"       },
 
             { SkXfermode::kPlus_Mode,         "Plus"          },
-            /*{ SkXfermode::kMultiply_Mode,     "Multiply"      },
+            /*{ SkXfermode::kModulate_Mode,     "Modulate"      },
             { SkXfermode::kScreen_Mode,       "Screen"        },
             { SkXfermode::kOverlay_Mode,      "Overlay"       },
             { SkXfermode::kDarken_Mode,       "Darken"        },
diff --git a/src/animator/SkAnimate.h b/src/animator/SkAnimate.h
index b7018b9..1bbecf9 100644
--- a/src/animator/SkAnimate.h
+++ b/src/animator/SkAnimate.h
@@ -32,4 +32,3 @@
 };
 
 #endif // SkAnimateField_DEFINED
-
diff --git a/src/animator/SkAnimateActive.cpp b/src/animator/SkAnimateActive.cpp
index 00ee9df..46b849b 100644
--- a/src/animator/SkAnimateActive.cpp
+++ b/src/animator/SkAnimateActive.cpp
@@ -174,7 +174,7 @@
         if (animate->formula.size() > 0) {
             SkTDOperandArray values;
             values.setCount(count);
-            bool success = animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL,
+            SkDEBUGCODE(bool success = ) animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL,
                 animate->getValuesType(), animate->formula);
             SkASSERT(success);
             fApply.applyValues(index, values.begin(), count, animate->getValuesType(), time);
@@ -212,7 +212,7 @@
             if (animate->formula.size() > 0) {
                 SkTDOperandArray values;
                 values.setCount(count);
-                bool success = animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL,
+                SkDEBUGCODE(bool success = ) animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL,
                     animate->getValuesType(), animate->formula);
                 SkASSERT(success);
                 fApply.applyValues(index, values.begin(), count, animate->getValuesType(), time);
@@ -502,5 +502,3 @@
     }
     return result;
 }
-
-
diff --git a/src/animator/SkAnimateBase.cpp b/src/animator/SkAnimateBase.cpp
index 0e56faa..3d50144 100644
--- a/src/animator/SkAnimateBase.cpp
+++ b/src/animator/SkAnimateBase.cpp
@@ -231,5 +231,3 @@
 bool SkAnimateBase::targetNeedsInitialization() const {
     return false;
 }
-
-
diff --git a/src/animator/SkAnimateField.cpp b/src/animator/SkAnimateField.cpp
index 43f510e..0f92989 100644
--- a/src/animator/SkAnimateField.cpp
+++ b/src/animator/SkAnimateField.cpp
@@ -109,4 +109,3 @@
     fFieldInfo->setValue(maker, &fValues, fFieldOffset, max, this, outType, from);
     fFieldInfo->setValue(maker, &fValues, fComponents + fFieldOffset, max, this, outType, to);
 }
-
diff --git a/src/animator/SkAnimateMaker.h b/src/animator/SkAnimateMaker.h
index 53a5521..a5abff7 100644
--- a/src/animator/SkAnimateMaker.h
+++ b/src/animator/SkAnimateMaker.h
@@ -158,4 +158,3 @@
 };
 
 #endif // SkAnimateMaker_DEFINED
-
diff --git a/src/animator/SkAnimateSet.h b/src/animator/SkAnimateSet.h
index c735a08..b8f7bf5 100644
--- a/src/animator/SkAnimateSet.h
+++ b/src/animator/SkAnimateSet.h
@@ -25,4 +25,3 @@
 };
 
 #endif // SkAnimateSet_DEFINED
-
diff --git a/src/animator/SkAnimator.cpp b/src/animator/SkAnimator.cpp
index 5d1f220..09fccd9 100644
--- a/src/animator/SkAnimator.cpp
+++ b/src/animator/SkAnimator.cpp
@@ -700,6 +700,3 @@
 
 void SkAnimator::Term() {
 }
-
-
-
diff --git a/src/animator/SkAnimatorScript.cpp b/src/animator/SkAnimatorScript.cpp
index 0e639f2..67c53c7 100644
--- a/src/animator/SkAnimatorScript.cpp
+++ b/src/animator/SkAnimatorScript.cpp
@@ -466,7 +466,7 @@
             } break;
         default: {
             const char* id = NULL;
-            bool success = maker->findKey(displayable, &id);
+            SkDEBUGCODE(bool success = ) maker->findKey(displayable, &id);
             SkASSERT(success);
             scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (id));
             type = SkType_String;
@@ -592,5 +592,3 @@
 }
 
 #endif
-
-
diff --git a/src/animator/SkAnimatorScript.h b/src/animator/SkAnimatorScript.h
index c8802a3..8589388 100644
--- a/src/animator/SkAnimatorScript.h
+++ b/src/animator/SkAnimatorScript.h
@@ -73,4 +73,3 @@
 };
 
 #endif // SkAnimatorScript_DEFINED
-
diff --git a/src/animator/SkAnimatorScript2.cpp b/src/animator/SkAnimatorScript2.cpp
index 45ce3ce..80ae0c6 100644
--- a/src/animator/SkAnimatorScript2.cpp
+++ b/src/animator/SkAnimatorScript2.cpp
@@ -620,4 +620,3 @@
 }
 
 #endif
-
diff --git a/src/animator/SkBoundable.cpp b/src/animator/SkBoundable.cpp
index 7f36218..64a7005 100644
--- a/src/animator/SkBoundable.cpp
+++ b/src/animator/SkBoundable.cpp
@@ -53,4 +53,3 @@
     fMaker.fCanvas->setBounder(NULL);
     fBoundable->setBounds(fMaker.fDisplayList.fBounds);
 }
-
diff --git a/src/animator/SkBoundable.h b/src/animator/SkBoundable.h
index 1e70505..daeda23 100644
--- a/src/animator/SkBoundable.h
+++ b/src/animator/SkBoundable.h
@@ -39,4 +39,3 @@
 };
 
 #endif // SkBoundable_DEFINED
-
diff --git a/src/animator/SkBuildCondensedInfo.cpp b/src/animator/SkBuildCondensedInfo.cpp
index 8fb82c7..411be90 100644
--- a/src/animator/SkBuildCondensedInfo.cpp
+++ b/src/animator/SkBuildCondensedInfo.cpp
@@ -280,5 +280,3 @@
 #include "SkDisplayType.h"
 void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* ) {}
 #endif
-
-
diff --git a/src/animator/SkCondensedDebug.cpp b/src/animator/SkCondensedDebug.cpp
index 08764b6..dcebe00 100644
--- a/src/animator/SkCondensedDebug.cpp
+++ b/src/animator/SkCondensedDebug.cpp
@@ -1385,5 +1385,3 @@
 
 #endif
 #endif
-
-
diff --git a/src/animator/SkCondensedRelease.cpp b/src/animator/SkCondensedRelease.cpp
index 60fa991..1222496 100644
--- a/src/animator/SkCondensedRelease.cpp
+++ b/src/animator/SkCondensedRelease.cpp
@@ -1363,4 +1363,3 @@
 };
 #endif
 #endif
-
diff --git a/src/animator/SkDisplayAdd.cpp b/src/animator/SkDisplayAdd.cpp
index 7fd5026..2cb5e97 100644
--- a/src/animator/SkDisplayAdd.cpp
+++ b/src/animator/SkDisplayAdd.cpp
@@ -243,4 +243,3 @@
 #endif
 
 DEFINE_GET_MEMBER(SkReplace);
-
diff --git a/src/animator/SkDisplayAdd.h b/src/animator/SkDisplayAdd.h
index 5883963..d16492b 100644
--- a/src/animator/SkDisplayAdd.h
+++ b/src/animator/SkDisplayAdd.h
@@ -69,5 +69,3 @@
 };
 
 #endif // SkDisplayAdd_DEFINED
-
-
diff --git a/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
index baa10e7..8b4dfdc 100644
--- a/src/animator/SkDisplayApply.cpp
+++ b/src/animator/SkDisplayApply.cpp
@@ -471,7 +471,7 @@
             info->setValue(target, fActive->fSaveRestore[activeIndex], count);
     } else {
         SkScriptValue scriptValue;
-        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkDEBUGCODE(bool success = ) target->getProperty(info->propertyIndex(), &scriptValue);
         SkASSERT(success == true);
         last[0] = scriptValue.fOperand;
         scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0];
@@ -629,7 +629,7 @@
                 fLastTime = animate->dur;
             SkTypedArray formulaValues;
             formulaValues.setCount(count);
-            bool success = animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL,
+            SkDEBUGCODE(bool success = ) animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL,
                 animate->getValuesType(), animate->formula);
             SkASSERT(success);
             if (restore)
@@ -761,7 +761,7 @@
             info->setValue(target, last.begin(), count);
     } else {
         SkScriptValue scriptValue;
-        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkDEBUGCODE(bool success = ) target->getProperty(info->propertyIndex(), &scriptValue);
         SkASSERT(success == true);
         SkASSERT(scriptValue.fType == SkType_Float);
         fActive->fSaveRestore[activeIndex][0] = scriptValue.fOperand;
@@ -802,6 +802,3 @@
         fActive->validate();
 }
 #endif
-
-
-
diff --git a/src/animator/SkDisplayApply.h b/src/animator/SkDisplayApply.h
index 3a066a4..e128c29 100644
--- a/src/animator/SkDisplayApply.h
+++ b/src/animator/SkDisplayApply.h
@@ -104,5 +104,3 @@
 };
 
 #endif // SkDisplayApply_DEFINED
-
-
diff --git a/src/animator/SkDisplayBounds.cpp b/src/animator/SkDisplayBounds.cpp
index 4c89481..49ec9b9 100644
--- a/src/animator/SkDisplayBounds.cpp
+++ b/src/animator/SkDisplayBounds.cpp
@@ -41,6 +41,3 @@
     }
     return result;
 }
-
-
-
diff --git a/src/animator/SkDisplayBounds.h b/src/animator/SkDisplayBounds.h
index bc3a987..0511ed7 100644
--- a/src/animator/SkDisplayBounds.h
+++ b/src/animator/SkDisplayBounds.h
@@ -22,4 +22,3 @@
 };
 
 #endif // SkDisplayBounds_DEFINED
-
diff --git a/src/animator/SkDisplayEvent.cpp b/src/animator/SkDisplayEvent.cpp
index 52c874b..c926a8a 100644
--- a/src/animator/SkDisplayEvent.cpp
+++ b/src/animator/SkDisplayEvent.cpp
@@ -174,7 +174,7 @@
         return;
     maker.fEvents.addEvent(this);
     if (kind == kOnEnd) {
-        bool found = maker.find(target.c_str(), &fTarget);
+        SkDEBUGCODE(bool found = ) maker.find(target.c_str(), &fTarget);
         SkASSERT(found);
         SkASSERT(fTarget && fTarget->isAnimate());
         SkAnimateBase* animate = (SkAnimateBase*) fTarget;
diff --git a/src/animator/SkDisplayEvent.h b/src/animator/SkDisplayEvent.h
index 5701da2..952faea 100644
--- a/src/animator/SkDisplayEvent.h
+++ b/src/animator/SkDisplayEvent.h
@@ -64,4 +64,3 @@
 };
 
 #endif // SkDisplayEvent_DEFINED
-
diff --git a/src/animator/SkDisplayEvents.h b/src/animator/SkDisplayEvents.h
index 2874ac5..276955b 100644
--- a/src/animator/SkDisplayEvents.h
+++ b/src/animator/SkDisplayEvents.h
@@ -40,4 +40,3 @@
 };
 
 #endif // SkDisplayEvents_DEFINED
-
diff --git a/src/animator/SkDisplayInclude.h b/src/animator/SkDisplayInclude.h
index d3fe4da..41b5d29 100644
--- a/src/animator/SkDisplayInclude.h
+++ b/src/animator/SkDisplayInclude.h
@@ -23,4 +23,3 @@
 };
 
 #endif // SkDisplayInclude_DEFINED
-
diff --git a/src/animator/SkDisplayInput.h b/src/animator/SkDisplayInput.h
index 79d82b5..d9871e2 100644
--- a/src/animator/SkDisplayInput.h
+++ b/src/animator/SkDisplayInput.h
@@ -31,4 +31,3 @@
 };
 
 #endif // SkDisplayInput_DEFINED
-
diff --git a/src/animator/SkDisplayList.cpp b/src/animator/SkDisplayList.cpp
index 39465f1..6434601 100644
--- a/src/animator/SkDisplayList.cpp
+++ b/src/animator/SkDisplayList.cpp
@@ -156,5 +156,3 @@
     }
 }
 #endif
-
-
diff --git a/src/animator/SkDisplayList.h b/src/animator/SkDisplayList.h
index af98aef..b870598 100644
--- a/src/animator/SkDisplayList.h
+++ b/src/animator/SkDisplayList.h
@@ -68,4 +68,3 @@
 };
 
 #endif // SkDisplayList_DEFINED
-
diff --git a/src/animator/SkDisplayMath.h b/src/animator/SkDisplayMath.h
index faa929e..9153795 100644
--- a/src/animator/SkDisplayMath.h
+++ b/src/animator/SkDisplayMath.h
@@ -29,4 +29,3 @@
 };
 
 #endif // SkDisplayMath_DEFINED
-
diff --git a/src/animator/SkDisplayMovie.cpp b/src/animator/SkDisplayMovie.cpp
index 33123cb..f1f82e3 100644
--- a/src/animator/SkDisplayMovie.cpp
+++ b/src/animator/SkDisplayMovie.cpp
@@ -126,5 +126,3 @@
     buildMovie();
     *maker.fMovies.append() = this;
 }
-
-
diff --git a/src/animator/SkDisplayMovie.h b/src/animator/SkDisplayMovie.h
index a599fb6..6210602 100644
--- a/src/animator/SkDisplayMovie.h
+++ b/src/animator/SkDisplayMovie.h
@@ -49,4 +49,3 @@
 };
 
 #endif // SkDisplayMovie_DEFINED
-
diff --git a/src/animator/SkDisplayPost.cpp b/src/animator/SkDisplayPost.cpp
index a50d5cc..cc45b21 100644
--- a/src/animator/SkDisplayPost.cpp
+++ b/src/animator/SkDisplayPost.cpp
@@ -235,7 +235,7 @@
     const char* ch = sink.c_str();
     do {
         const char* end = strchr(ch, '.');
-        size_t len = end ? end - ch : strlen(ch);
+        size_t len = end ? (size_t) (end - ch) : strlen(ch);
         SkDisplayable* displayable = NULL;
         if (SK_LITERAL_STR_EQUAL("parent", ch, len)) {
             if (fTargetMaker->fParentMaker)
@@ -296,4 +296,3 @@
     }
     return true;
 }
-
diff --git a/src/animator/SkDisplayRandom.cpp b/src/animator/SkDisplayRandom.cpp
index 31a20b5..2efe8dc 100644
--- a/src/animator/SkDisplayRandom.cpp
+++ b/src/animator/SkDisplayRandom.cpp
@@ -63,4 +63,3 @@
     fRandom.setSeed(value.fOperand.fS32);
     return true;
 }
-
diff --git a/src/animator/SkDisplayRandom.h b/src/animator/SkDisplayRandom.h
index 87956d2..1c38653 100644
--- a/src/animator/SkDisplayRandom.h
+++ b/src/animator/SkDisplayRandom.h
@@ -38,4 +38,3 @@
 };
 
 #endif // SkDisplayRandom_DEFINED
-
diff --git a/src/animator/SkDisplayScreenplay.cpp b/src/animator/SkDisplayScreenplay.cpp
index 463f948..2663b43 100644
--- a/src/animator/SkDisplayScreenplay.cpp
+++ b/src/animator/SkDisplayScreenplay.cpp
@@ -18,5 +18,3 @@
 #endif
 
 DEFINE_GET_MEMBER(SkDisplayScreenplay);
-
-
diff --git a/src/animator/SkDisplayTypes.cpp b/src/animator/SkDisplayTypes.cpp
index d320fc0..287ca6e 100644
--- a/src/animator/SkDisplayTypes.cpp
+++ b/src/animator/SkDisplayTypes.cpp
@@ -212,6 +212,3 @@
     }
     return true;
 }
-
-
-
diff --git a/src/animator/SkDisplayTypes.h b/src/animator/SkDisplayTypes.h
index 614018f..1a3d0e5 100644
--- a/src/animator/SkDisplayTypes.h
+++ b/src/animator/SkDisplayTypes.h
@@ -104,4 +104,3 @@
 };
 
 #endif // SkDisplayTypes_DEFINED
-
diff --git a/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp
index 515d33c..3ebf9dc 100644
--- a/src/animator/SkDisplayXMLParser.cpp
+++ b/src/animator/SkDisplayXMLParser.cpp
@@ -314,5 +314,3 @@
         return lastResort;
     return NULL;
 }
-
-
diff --git a/src/animator/SkDisplayXMLParser.h b/src/animator/SkDisplayXMLParser.h
index c18e48f..9c561ed 100644
--- a/src/animator/SkDisplayXMLParser.h
+++ b/src/animator/SkDisplayXMLParser.h
@@ -89,5 +89,3 @@
 };
 
 #endif // SkDisplayXMLParser_DEFINED
-
-
diff --git a/src/animator/SkDisplayable.cpp b/src/animator/SkDisplayable.cpp
index ed2c626..aff3aae 100644
--- a/src/animator/SkDisplayable.cpp
+++ b/src/animator/SkDisplayable.cpp
@@ -538,5 +538,3 @@
 void SkDisplayable::validate() {
 }
 #endif
-
-
diff --git a/src/animator/SkDraw3D.cpp b/src/animator/SkDraw3D.cpp
index 6ec178f..9e1c2cc 100644
--- a/src/animator/SkDraw3D.cpp
+++ b/src/animator/SkDraw3D.cpp
@@ -104,6 +104,3 @@
 const SkFunctionParamType* Sk3D_Patch::getFunctionsParameters() {
     return fFunctionParameters;
 }
-
-
-
diff --git a/src/animator/SkDraw3D.h b/src/animator/SkDraw3D.h
index f4bd82b..a204044 100644
--- a/src/animator/SkDraw3D.h
+++ b/src/animator/SkDraw3D.h
@@ -48,4 +48,3 @@
 };
 
 #endif // SkDraw3D_DEFINED
-
diff --git a/src/animator/SkDrawBlur.cpp b/src/animator/SkDrawBlur.cpp
index 68996bc..d66fc56 100644
--- a/src/animator/SkDrawBlur.cpp
+++ b/src/animator/SkDrawBlur.cpp
@@ -29,4 +29,3 @@
         return NULL;
     return SkBlurMaskFilter::Create(radius, (SkBlurMaskFilter::BlurStyle) blurStyle);
 }
-
diff --git a/src/animator/SkDrawBlur.h b/src/animator/SkDrawBlur.h
index 220e211..24cb4ae 100644
--- a/src/animator/SkDrawBlur.h
+++ b/src/animator/SkDrawBlur.h
@@ -23,4 +23,3 @@
 };
 
 #endif // SkDrawBlur_DEFINED
-
diff --git a/src/animator/SkDrawClip.cpp b/src/animator/SkDrawClip.cpp
index 521bbc5..e26a1bf 100644
--- a/src/animator/SkDrawClip.cpp
+++ b/src/animator/SkDrawClip.cpp
@@ -37,4 +37,3 @@
     }
     return false;
 }
-
diff --git a/src/animator/SkDrawColor.cpp b/src/animator/SkDrawColor.cpp
index dfe0622..af0f04b 100644
--- a/src/animator/SkDrawColor.cpp
+++ b/src/animator/SkDrawColor.cpp
@@ -267,4 +267,3 @@
     }
     return true;
 }
-
diff --git a/src/animator/SkDrawDash.cpp b/src/animator/SkDrawDash.cpp
index 0d93293..8e73aa1 100644
--- a/src/animator/SkDrawDash.cpp
+++ b/src/animator/SkDrawDash.cpp
@@ -33,4 +33,3 @@
         return NULL;
     return new SkDashPathEffect(intervals.begin(), count, phase);
 }
-
diff --git a/src/animator/SkDrawDash.h b/src/animator/SkDrawDash.h
index 483d2a3..0000462 100644
--- a/src/animator/SkDrawDash.h
+++ b/src/animator/SkDrawDash.h
@@ -24,4 +24,3 @@
 };
 
 #endif // SkDrawDash_DEFINED
-
diff --git a/src/animator/SkDrawDiscrete.cpp b/src/animator/SkDrawDiscrete.cpp
index 655c83d..18c3ee0 100644
--- a/src/animator/SkDrawDiscrete.cpp
+++ b/src/animator/SkDrawDiscrete.cpp
@@ -32,4 +32,3 @@
     else
         return new SkDiscretePathEffect(segLength, deviation);
 }
-
diff --git a/src/animator/SkDrawEmboss.cpp b/src/animator/SkDrawEmboss.cpp
index 7e47ec2..5eed370 100644
--- a/src/animator/SkDrawEmboss.cpp
+++ b/src/animator/SkDrawEmboss.cpp
@@ -31,4 +31,3 @@
         return NULL;
     return SkBlurMaskFilter::CreateEmboss(direction.begin(), ambient, specular, radius);
 }
-
diff --git a/src/animator/SkDrawEmboss.h b/src/animator/SkDrawEmboss.h
index 50ce71a..6e61997 100644
--- a/src/animator/SkDrawEmboss.h
+++ b/src/animator/SkDrawEmboss.h
@@ -22,4 +22,3 @@
 };
 
 #endif // SkDrawEmboss_DEFINED
-
diff --git a/src/animator/SkDrawFull.cpp b/src/animator/SkDrawFull.cpp
index 762b3da..a1a5fc9 100644
--- a/src/animator/SkDrawFull.cpp
+++ b/src/animator/SkDrawFull.cpp
@@ -16,4 +16,3 @@
     maker.fCanvas->drawPaint(*maker.fPaint);
     return false;
 }
-
diff --git a/src/animator/SkDrawGradient.h b/src/animator/SkDrawGradient.h
index 7763c1f..ff79e3f 100644
--- a/src/animator/SkDrawGradient.h
+++ b/src/animator/SkDrawGradient.h
@@ -65,4 +65,3 @@
 };
 
 #endif // SkDrawGradient_DEFINED
-
diff --git a/src/animator/SkDrawGroup.cpp b/src/animator/SkDrawGroup.cpp
index ddd23d3..c569434 100644
--- a/src/animator/SkDrawGroup.cpp
+++ b/src/animator/SkDrawGroup.cpp
@@ -319,5 +319,3 @@
     maker.fCanvas->restore();
     return result;
 }
-
-
diff --git a/src/animator/SkDrawLine.h b/src/animator/SkDrawLine.h
index e90c997..b287802 100644
--- a/src/animator/SkDrawLine.h
+++ b/src/animator/SkDrawLine.h
@@ -26,4 +26,3 @@
 };
 
 #endif // SkDrawLine_DEFINED
-
diff --git a/src/animator/SkDrawMatrix.cpp b/src/animator/SkDrawMatrix.cpp
index 62507ea..80b04c1 100644
--- a/src/animator/SkDrawMatrix.cpp
+++ b/src/animator/SkDrawMatrix.cpp
@@ -266,4 +266,3 @@
     fConcat = fMatrix;
     return true;
 }
-
diff --git a/src/animator/SkDrawOval.cpp b/src/animator/SkDrawOval.cpp
index ab0fc81..e5efa7d 100644
--- a/src/animator/SkDrawOval.cpp
+++ b/src/animator/SkDrawOval.cpp
@@ -26,4 +26,3 @@
     maker.fCanvas->drawOval(fRect, *maker.fPaint);
     return false;
 }
-
diff --git a/src/animator/SkDrawOval.h b/src/animator/SkDrawOval.h
index afdc252..3c09e0f 100644
--- a/src/animator/SkDrawOval.h
+++ b/src/animator/SkDrawOval.h
@@ -20,4 +20,3 @@
 };
 
 #endif // SkDrawOval_DEFINED
-
diff --git a/src/animator/SkDrawPaint.h b/src/animator/SkDrawPaint.h
index db95d34..3caf6b6 100644
--- a/src/animator/SkDrawPaint.h
+++ b/src/animator/SkDrawPaint.h
@@ -77,4 +77,3 @@
 };
 
 #endif // SkDrawPaint_DEFINED
-
diff --git a/src/animator/SkDrawPath.cpp b/src/animator/SkDrawPath.cpp
index aaeeb0b..f62f7c0 100644
--- a/src/animator/SkDrawPath.cpp
+++ b/src/animator/SkDrawPath.cpp
@@ -218,4 +218,3 @@
     INHERITED::onEndElement(maker);
     fPath.close();
 }
-
diff --git a/src/animator/SkDrawPoint.cpp b/src/animator/SkDrawPoint.cpp
index 66d2b4e..41a6be4 100644
--- a/src/animator/SkDrawPoint.cpp
+++ b/src/animator/SkDrawPoint.cpp
@@ -42,5 +42,3 @@
     rect->fLeft = rect->fRight = fPoint.fX;
     rect->fTop = rect->fBottom = fPoint.fY;
 }
-
-
diff --git a/src/animator/SkDrawRectangle.cpp b/src/animator/SkDrawRectangle.cpp
index 7587e4b..c3fb7ff 100644
--- a/src/animator/SkDrawRectangle.cpp
+++ b/src/animator/SkDrawRectangle.cpp
@@ -140,6 +140,3 @@
             SkScalarToFloat(fRect.fBottom), SkScalarToFloat(rx), SkScalarToFloat(ry));
 }
 #endif
-
-
-
diff --git a/src/animator/SkDrawRectangle.h b/src/animator/SkDrawRectangle.h
index 321c4ee..42af02b 100644
--- a/src/animator/SkDrawRectangle.h
+++ b/src/animator/SkDrawRectangle.h
@@ -53,4 +53,3 @@
 };
 
 #endif // SkDrawRectangle_DEFINED
-
diff --git a/src/animator/SkDrawSaveLayer.cpp b/src/animator/SkDrawSaveLayer.cpp
index 43ec5c1..4e97a04 100644
--- a/src/animator/SkDrawSaveLayer.cpp
+++ b/src/animator/SkDrawSaveLayer.cpp
@@ -74,5 +74,3 @@
         maker.setErrorCode(SkDisplayXMLParserError::kSaveLayerNeedsBounds);
     INHERITED::onEndElement(maker);
 }
-
-
diff --git a/src/animator/SkDrawText.cpp b/src/animator/SkDrawText.cpp
index b8d38b4..e7e5fac 100644
--- a/src/animator/SkDrawText.cpp
+++ b/src/animator/SkDrawText.cpp
@@ -53,4 +53,3 @@
     value->fOperand.fS32 = (int32_t) text.size();
     return true;
 }
-
diff --git a/src/animator/SkDrawTextBox.cpp b/src/animator/SkDrawTextBox.cpp
index f920e8e..7a3251a 100644
--- a/src/animator/SkDrawTextBox.cpp
+++ b/src/animator/SkDrawTextBox.cpp
@@ -78,5 +78,3 @@
     box.draw(maker.fCanvas, fText.c_str(), fText.size(), *maker.fPaint);
     return false;
 }
-
-
diff --git a/src/animator/SkDrawTextBox.h b/src/animator/SkDrawTextBox.h
index d8d7f0c..6155bef 100644
--- a/src/animator/SkDrawTextBox.h
+++ b/src/animator/SkDrawTextBox.h
@@ -36,4 +36,3 @@
 };
 
 #endif // SkDrawTextBox_DEFINED
-
diff --git a/src/animator/SkDrawTo.cpp b/src/animator/SkDrawTo.cpp
index bc5cd6d..ef084d1 100644
--- a/src/animator/SkDrawTo.cpp
+++ b/src/animator/SkDrawTo.cpp
@@ -53,4 +53,3 @@
     dumpDrawables(maker);
 }
 #endif
-
diff --git a/src/animator/SkDrawTransparentShader.cpp b/src/animator/SkDrawTransparentShader.cpp
index bb5392a..2f286f4 100644
--- a/src/animator/SkDrawTransparentShader.cpp
+++ b/src/animator/SkDrawTransparentShader.cpp
@@ -13,4 +13,3 @@
 SkShader* SkDrawTransparentShader::getShader() {
     return new SkTransparentShader();
 }
-
diff --git a/src/animator/SkDrawTransparentShader.h b/src/animator/SkDrawTransparentShader.h
index df1c6eb..bf66174 100644
--- a/src/animator/SkDrawTransparentShader.h
+++ b/src/animator/SkDrawTransparentShader.h
@@ -18,4 +18,3 @@
 };
 
 #endif // SkDrawTransparentShader_DEFINED
-
diff --git a/src/animator/SkDrawable.cpp b/src/animator/SkDrawable.cpp
index 9e80c9d..610c397 100644
--- a/src/animator/SkDrawable.cpp
+++ b/src/animator/SkDrawable.cpp
@@ -22,4 +22,3 @@
 
 void SkDrawable::setSteps(int steps) {
 }
-
diff --git a/src/animator/SkDump.h b/src/animator/SkDump.h
index 3222f06..0a31b1c 100644
--- a/src/animator/SkDump.h
+++ b/src/animator/SkDump.h
@@ -40,4 +40,3 @@
 
 
 #endif // SkDump_DEFINED
-
diff --git a/src/animator/SkGetCondensedInfo.cpp b/src/animator/SkGetCondensedInfo.cpp
index 6d83b1d..f2471bb 100644
--- a/src/animator/SkGetCondensedInfo.cpp
+++ b/src/animator/SkGetCondensedInfo.cpp
@@ -119,4 +119,3 @@
 }
 
 #endif
-
diff --git a/src/animator/SkHitClear.cpp b/src/animator/SkHitClear.cpp
index 70b3e73..96b1747 100644
--- a/src/animator/SkHitClear.cpp
+++ b/src/animator/SkHitClear.cpp
@@ -30,4 +30,3 @@
 bool SkHitClear::hasEnable() const {
     return true;
 }
-
diff --git a/src/animator/SkHitTest.cpp b/src/animator/SkHitTest.cpp
index dc4e739..7506edc 100644
--- a/src/animator/SkHitTest.cpp
+++ b/src/animator/SkHitTest.cpp
@@ -72,4 +72,3 @@
         return getMember("bullets");
     return getMember("targets"); // !!! cwap! need to refer to member through enum like kScope instead
 }
-
diff --git a/src/animator/SkIntArray.h b/src/animator/SkIntArray.h
index 4dac75a..401e51b 100644
--- a/src/animator/SkIntArray.h
+++ b/src/animator/SkIntArray.h
@@ -53,6 +53,3 @@
 typedef SkLongArray(SkOperand*) SkTDOperandPtrArray;
 
 #endif // SkIntArray_DEFINED
-
-
-
diff --git a/src/animator/SkMatrixParts.cpp b/src/animator/SkMatrixParts.cpp
index 221ac0a..a2f3a9a 100644
--- a/src/animator/SkMatrixParts.cpp
+++ b/src/animator/SkMatrixParts.cpp
@@ -290,5 +290,3 @@
         return getMember("destination");
     }
 }
-
-
diff --git a/src/animator/SkMemberInfo.cpp b/src/animator/SkMemberInfo.cpp
index 582b29a..5e54b53 100644
--- a/src/animator/SkMemberInfo.cpp
+++ b/src/animator/SkMemberInfo.cpp
@@ -557,5 +557,3 @@
     return true;
 }
 #endif
-
-
diff --git a/src/animator/SkMemberInfo.h b/src/animator/SkMemberInfo.h
index 007df60..a1b1941 100644
--- a/src/animator/SkMemberInfo.h
+++ b/src/animator/SkMemberInfo.h
@@ -268,4 +268,3 @@
 #endif
 
 #endif // SkMemberInfo_DEFINED
-
diff --git a/src/animator/SkOperandInterpolator.h b/src/animator/SkOperandInterpolator.h
index 82fa272..adbe69f 100644
--- a/src/animator/SkOperandInterpolator.h
+++ b/src/animator/SkOperandInterpolator.h
@@ -45,4 +45,3 @@
 };
 
 #endif // SkOperandInterpolator_DEFINED
-
diff --git a/src/animator/SkOperandIterpolator.cpp b/src/animator/SkOperandIterpolator.cpp
index bc7d46b..7822ee2 100644
--- a/src/animator/SkOperandIterpolator.cpp
+++ b/src/animator/SkOperandIterpolator.cpp
@@ -147,5 +147,3 @@
 }
 
 #endif
-
-
diff --git a/src/animator/SkPaintParts.cpp b/src/animator/SkPaintParts.cpp
index 2959238..da47178 100644
--- a/src/animator/SkPaintParts.cpp
+++ b/src/animator/SkPaintParts.cpp
@@ -99,5 +99,3 @@
     SkDebugf("style=\"%s\" />\n", string.c_str());
 }
 #endif
-
-
diff --git a/src/animator/SkParseSVGPath.cpp b/src/animator/SkParseSVGPath.cpp
index f020e2e..4b548e2 100644
--- a/src/animator/SkParseSVGPath.cpp
+++ b/src/animator/SkParseSVGPath.cpp
@@ -232,4 +232,3 @@
         previousOp = op;
     } while (data[0] > 0);
 }
-
diff --git a/src/animator/SkPathParts.cpp b/src/animator/SkPathParts.cpp
index 3060bd4..efc7c33 100644
--- a/src/animator/SkPathParts.cpp
+++ b/src/animator/SkPathParts.cpp
@@ -316,5 +316,3 @@
         fPath->fPath.addPath(path->fPath);
     return false;
 }
-
-
diff --git a/src/animator/SkPathParts.h b/src/animator/SkPathParts.h
index cc81cdd..f82aa74 100644
--- a/src/animator/SkPathParts.h
+++ b/src/animator/SkPathParts.h
@@ -162,4 +162,3 @@
 };
 
 #endif // SkPathParts_DEFINED
-
diff --git a/src/animator/SkPostParts.cpp b/src/animator/SkPostParts.cpp
index b6549a2..7c0931e 100644
--- a/src/animator/SkPostParts.cpp
+++ b/src/animator/SkPostParts.cpp
@@ -54,4 +54,3 @@
 void SkDataInput::onEndElement(SkAnimateMaker&) {
     add();
 }
-
diff --git a/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
index 92e8f3c..14ca625 100644
--- a/src/animator/SkScript.cpp
+++ b/src/animator/SkScript.cpp
@@ -1514,7 +1514,7 @@
             break;
         case SkType_Float:
             if (type == SkType_Int) {
-                if ((uint32_t)operand.fS32 == SK_NaN32)
+                if (operand.fS32 == SK_NaN32)
                     operand.fScalar = SK_ScalarNaN;
                 else if (SkAbs32(operand.fS32) == SK_MaxS32)
                     operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax;
@@ -1562,7 +1562,7 @@
 
 SkScalar SkScriptEngine::IntToScalar(int32_t s32) {
     SkScalar scalar;
-    if ((uint32_t)s32 == SK_NaN32)
+    if (s32 == SK_NaN32)
         scalar = SK_ScalarNaN;
     else if (SkAbs32(s32) == SK_MaxS32)
         scalar = SkSign32(s32) * SK_ScalarMax;
@@ -1893,4 +1893,3 @@
     }
 }
 #endif
-
diff --git a/src/animator/SkScript2.h b/src/animator/SkScript2.h
index 03eb92c..33e2af7 100644
--- a/src/animator/SkScript2.h
+++ b/src/animator/SkScript2.h
@@ -289,4 +289,3 @@
 
 
 #endif // SkScript2_DEFINED
-
diff --git a/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
index b211a71..f287850 100644
--- a/src/animator/SkScriptRuntime.cpp
+++ b/src/animator/SkScriptRuntime.cpp
@@ -349,4 +349,3 @@
     SkASSERT(index >= 0);
     fTrackString.begin()[index] = NULL;
 }
-
diff --git a/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp
index a1de5e5..ca6cab0 100644
--- a/src/animator/SkScriptTokenizer.cpp
+++ b/src/animator/SkScriptTokenizer.cpp
@@ -993,7 +993,7 @@
                 SkASSERT(arrayValue.fType == SkOperand2::kArray);  // !!! add error handling
                 SkOpArray* array = arrayValue.fOperand.fArray;
                 SkOperand2 operand;
-                bool success = array->getIndex(index, &operand);
+                SkDEBUGCODE(bool success = ) array->getIndex(index, &operand);
                 SkASSERT(success); // !!! add error handling
                 SkScriptValue2 resultValue;
                 resultValue.fType = array->getType();
diff --git a/src/animator/SkSnapshot.cpp b/src/animator/SkSnapshot.cpp
index 4e78bbd..a253d8e 100644
--- a/src/animator/SkSnapshot.cpp
+++ b/src/animator/SkSnapshot.cpp
@@ -61,4 +61,3 @@
                         SkScalarFloor(quality));
     return false;
 }
-
diff --git a/src/animator/SkSnapshot.h b/src/animator/SkSnapshot.h
index beb26ca..5ae6917 100644
--- a/src/animator/SkSnapshot.h
+++ b/src/animator/SkSnapshot.h
@@ -28,4 +28,3 @@
 };
 
 #endif // SkSnapShot_DEFINED
-
diff --git a/src/animator/SkTextToPath.cpp b/src/animator/SkTextToPath.cpp
index b38daff..0c1b0e4 100644
--- a/src/animator/SkTextToPath.cpp
+++ b/src/animator/SkTextToPath.cpp
@@ -45,4 +45,3 @@
     realPaint.getTextPath(text->getText(), text->getSize(), text->x,
         text->y, &path->getPath());
 }
-
diff --git a/src/animator/SkTextToPath.h b/src/animator/SkTextToPath.h
index cb2a15e..ac44ad7 100644
--- a/src/animator/SkTextToPath.h
+++ b/src/animator/SkTextToPath.h
@@ -29,4 +29,3 @@
 };
 
 #endif // SkTextToPath_DEFINED
-
diff --git a/src/animator/SkTime.cpp b/src/animator/SkTime.cpp
index 06ad9bd..ffd6f38 100644
--- a/src/animator/SkTime.cpp
+++ b/src/animator/SkTime.cpp
@@ -78,4 +78,3 @@
 }
 
 #endif
-
diff --git a/src/animator/SkXMLAnimatorWriter.cpp b/src/animator/SkXMLAnimatorWriter.cpp
index 6d299b6..58f20f1 100644
--- a/src/animator/SkXMLAnimatorWriter.cpp
+++ b/src/animator/SkXMLAnimatorWriter.cpp
@@ -80,4 +80,3 @@
 }
 
 #endif
-
diff --git a/src/animator/SkXMLAnimatorWriter.h b/src/animator/SkXMLAnimatorWriter.h
index bd6ccf4..1b17f6f 100644
--- a/src/animator/SkXMLAnimatorWriter.h
+++ b/src/animator/SkXMLAnimatorWriter.h
@@ -31,4 +31,3 @@
 };
 
 #endif // SkXMLAnimatorWriter_DEFINED
-
diff --git a/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
index 720b479..87121cf 100644
--- a/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
+++ b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
@@ -175,4 +175,3 @@
         }
     }
 }
-
diff --git a/src/core/SkAAClip.cpp b/src/core/SkAAClip.cpp
index fce6683..988e2d7 100644
--- a/src/core/SkAAClip.cpp
+++ b/src/core/SkAAClip.cpp
@@ -2176,4 +2176,3 @@
 const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
     return NULL;
 }
-
diff --git a/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp
index 116d132..a80e9af 100644
--- a/src/core/SkAlphaRuns.cpp
+++ b/src/core/SkAlphaRuns.cpp
@@ -176,4 +176,3 @@
         SkASSERT(count == fWidth);
     }
 #endif
-
diff --git a/src/core/SkAnnotation.cpp b/src/core/SkAnnotation.cpp
index 8176761..5e4363e 100644
--- a/src/core/SkAnnotation.cpp
+++ b/src/core/SkAnnotation.cpp
@@ -62,4 +62,3 @@
 
     canvas->drawRect(rect, paint);
 }
-
diff --git a/src/core/SkAntiRun.h b/src/core/SkAntiRun.h
index 31b52e3..1239726 100644
--- a/src/core/SkAntiRun.h
+++ b/src/core/SkAntiRun.h
@@ -90,4 +90,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkAutoKern.h b/src/core/SkAutoKern.h
index 0a08432..0b22e56 100644
--- a/src/core/SkAutoKern.h
+++ b/src/core/SkAutoKern.h
@@ -51,4 +51,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkBBoxHierarchy.cpp b/src/core/SkBBoxHierarchy.cpp
index 7d061b5..5232fb7 100644
--- a/src/core/SkBBoxHierarchy.cpp
+++ b/src/core/SkBBoxHierarchy.cpp
@@ -9,4 +9,3 @@
 #include "SkBBoxHierarchy.h"
 
 SK_DEFINE_INST_COUNT(SkBBoxHierarchy)
-
diff --git a/src/core/SkBBoxHierarchy.h b/src/core/SkBBoxHierarchy.h
index 6625a84..4c8b2ae 100644
--- a/src/core/SkBBoxHierarchy.h
+++ b/src/core/SkBBoxHierarchy.h
@@ -54,4 +54,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkBBoxHierarchyRecord.h b/src/core/SkBBoxHierarchyRecord.h
index ac7ab1c..854e525 100644
--- a/src/core/SkBBoxHierarchyRecord.h
+++ b/src/core/SkBBoxHierarchyRecord.h
@@ -52,4 +52,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkBBoxRecord.cpp b/src/core/SkBBoxRecord.cpp
index 1ca2561..52d599f 100644
--- a/src/core/SkBBoxRecord.cpp
+++ b/src/core/SkBBoxRecord.cpp
@@ -274,9 +274,7 @@
         }
     }
 
-    SkRect clip;
-
-    if (this->getClipBounds(&clip) && outBounds.intersect(clip)) {
+    if (!outBounds.isEmpty() && !this->quickReject(outBounds)) {
         this->getTotalMatrix().mapRect(&outBounds);
         this->handleBBox(outBounds);
         return true;
@@ -284,4 +282,3 @@
 
     return false;
 }
-
diff --git a/src/core/SkBBoxRecord.h b/src/core/SkBBoxRecord.h
index 190d145..9f79671 100644
--- a/src/core/SkBBoxRecord.h
+++ b/src/core/SkBBoxRecord.h
@@ -76,4 +76,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 5554f46..e5a202c 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -701,7 +701,7 @@
             if (!table) {
                 return false;
             }
-            SkPMColor c = ~0;
+            SkPMColor c = (SkPMColor)~0;
             for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
                 c &= table[i];
             }
@@ -724,7 +724,7 @@
             return true;
         } break;
         case SkBitmap::kARGB_8888_Config: {
-            SkPMColor c = ~0;
+            SkPMColor c = (SkPMColor)~0;
             for (int y = 0; y < height; ++y) {
                 const SkPMColor* row = bm.getAddr32(0, y);
                 for (int x = 0; x < width; ++x) {
@@ -1638,3 +1638,43 @@
 #endif
 }
 #endif
+
+#ifdef SK_DEVELOPER
+void SkBitmap::toString(SkString* str) const {
+
+    static const char* gConfigNames[kConfigCount] = {
+        "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888", "RLE"
+    };
+
+    str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
+                 gConfigNames[this->config()]);
+
+    str->append(" (");
+    if (this->isOpaque()) {
+        str->append("opaque");
+    } else {
+        str->append("transparent");
+    }
+    if (this->isImmutable()) {
+        str->append(", immutable");
+    } else {
+        str->append(", not-immutable");
+    }
+    str->append(")");
+
+    SkPixelRef* pr = this->pixelRef();
+    if (NULL == pr) {
+        // show null or the explicit pixel address (rare)
+        str->appendf(" pixels:%p", this->getPixels());
+    } else {
+        const char* uri = pr->getURI();
+        if (NULL != uri) {
+            str->appendf(" uri:\"%s\"", uri);
+        } else {
+            str->appendf(" pixelref:%p", pr);
+        }
+    }
+
+    str->append(")");
+}
+#endif
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index 3e41166..96cfea6 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -86,11 +86,13 @@
     fState.fOrigBitmap.lockPixels();
     if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) {
         fState.fOrigBitmap.unlockPixels();
+        this->INHERITED::endContext();
         return false;
     }
 
     if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
         fState.fOrigBitmap.unlockPixels();
+        this->INHERITED::endContext();
         return false;
     }
 
@@ -125,7 +127,7 @@
         flags &= ~kHasSpan16_Flag;
     }
 
-    // if we're only 1-pixel heigh, and we don't rotate, then we can claim this
+    // if we're only 1-pixel high, and we don't rotate, then we can claim this
     if (1 == bitmap.height() &&
             only_scale_and_translate(this->getTotalInverse())) {
         flags |= kConstInY32_Flag;
@@ -304,36 +306,62 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static const char* gTileModeName[] = {
-    "clamp", "repeat", "mirror"
-};
+#ifdef SK_DEVELOPER
+void SkBitmapProcShader::toString(SkString* str) const {
+    static const char* gTileModeName[SkShader::kTileModeCount] = {
+        "clamp", "repeat", "mirror"
+    };
 
-bool SkBitmapProcShader::toDumpString(SkString* str) const {
-    str->printf("BitmapShader: [%d %d %d",
-                fRawBitmap.width(), fRawBitmap.height(),
-                fRawBitmap.bytesPerPixel());
+    str->append("BitmapShader: (");
 
-    // add the pixelref
-    SkPixelRef* pr = fRawBitmap.pixelRef();
-    if (pr) {
-        const char* uri = pr->getURI();
-        if (uri) {
-            str->appendf(" \"%s\"", uri);
-        }
-    }
-
-    // add the (optional) matrix
-    {
-        if (this->hasLocalMatrix()) {
-            SkString info;
-            this->getLocalMatrix().toDumpString(&info);
-            str->appendf(" %s", info.c_str());
-        }
-    }
-
-    str->appendf(" [%s %s]]",
+    str->appendf("(%s, %s)",
                  gTileModeName[fState.fTileModeX],
                  gTileModeName[fState.fTileModeY]);
-    return true;
-}
 
+    str->append(" ");
+    fRawBitmap.toString(str);
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrTextureAccess.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "SkGr.h"
+
+GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
+    SkMatrix matrix;
+    matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
+
+    if (this->hasLocalMatrix()) {
+        SkMatrix inverse;
+        if (!this->getLocalMatrix().invert(&inverse)) {
+            return NULL;
+        }
+        matrix.preConcat(inverse);
+    }
+    SkShader::TileMode tm[] = {
+        (TileMode)fState.fTileModeX,
+        (TileMode)fState.fTileModeY,
+    };
+
+    // Must set wrap and filter on the sampler before requesting a texture.
+    GrTextureParams params(tm, paint.isFilterBitmap());
+    GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, fRawBitmap, &params);
+
+    if (NULL == texture) {
+        SkDebugf("Couldn't convert bitmap to texture.\n");
+        return NULL;
+    }
+
+    GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
+    GrUnlockAndUnrefCachedBitmapTexture(texture);
+    return effect;
+}
+#endif
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
index cb791d0..5a599c3 100644
--- a/src/core/SkBitmapProcShader.h
+++ b/src/core/SkBitmapProcShader.h
@@ -19,20 +19,23 @@
 
     // overrides from SkShader
     virtual bool isOpaque() const SK_OVERRIDE;
-    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
-    virtual void endContext();
-    virtual uint32_t getFlags() { return fFlags; }
-    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
+    virtual void endContext() SK_OVERRIDE;
+    virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
     virtual ShadeProc asAShadeProc(void** ctx) SK_OVERRIDE;
-    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
-    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const;
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
 
     static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);
 
-    // override from flattenable
-    virtual bool toDumpString(SkString* str) const;
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapProcShader)
 
+#if SK_SUPPORT_GPU
+    GrEffectRef* asNewEffect(GrContext*, const SkPaint&) const SK_OVERRIDE;
+#endif
+
 protected:
     SkBitmapProcShader(SkFlattenableReadBuffer& );
     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index ec2e0dd..97ffef5 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -24,11 +24,9 @@
 extern void  Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
 #endif
 
-#if !SK_ARM_NEON_IS_ALWAYS
 #define   NAME_WRAP(x)  x
 #include "SkBitmapProcState_filter.h"
 #include "SkBitmapProcState_procs.h"
-#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -91,7 +89,7 @@
     const SkMatrix* m;
     bool trivial_matrix = (inv.getType() & ~SkMatrix::kTranslate_Mask) == 0;
     bool clamp_clamp = SkShader::kClamp_TileMode == fTileModeX &&
-    SkShader::kClamp_TileMode == fTileModeY;
+                       SkShader::kClamp_TileMode == fTileModeY;
 
     if (clamp_clamp || trivial_matrix) {
         m = &inv;
@@ -126,12 +124,28 @@
         bool fixupMatrix = clamp_clamp ?
         just_trans_clamp(*m, *fBitmap) : just_trans_general(*m);
         if (fixupMatrix) {
+#ifdef SK_IGNORE_TRANS_CLAMP_FIX
             if (m != &fUnitInvMatrix) {    // can't mutate the original
                 fUnitInvMatrix = inv;
                 m = &fUnitInvMatrix;
             }
             fUnitInvMatrix.set(SkMatrix::kMScaleX, SK_Scalar1);
             fUnitInvMatrix.set(SkMatrix::kMScaleY, SK_Scalar1);
+#else
+            // If we can be treated just like translate, construct that inverse
+            // such that we landed in the proper place. Given that m may have
+            // some slight scale, we have to invert it to compute this new
+            // matrix.
+            SkMatrix forward;
+            if (m->invert(&forward)) {
+                SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
+                SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
+                fUnitInvMatrix.setTranslate(tx, ty);
+                m = &fUnitInvMatrix;
+                // now the following code will sniff m, and decide to take the
+                // fast case (since m is purely translate).
+            }
+#endif
         }
     }
 
@@ -239,7 +253,7 @@
         S4444_opaque_D32_filter_DX,
         S4444_alpha_D32_filter_DX,
 
-        // A8 treats alpha/opauqe the same (equally efficient)
+        // A8 treats alpha/opaque the same (equally efficient)
         SA8_alpha_D32_nofilter_DXDY,
         SA8_alpha_D32_nofilter_DXDY,
         SA8_alpha_D32_nofilter_DX,
@@ -364,6 +378,14 @@
     return x;
 }
 
+static inline int sk_int_mirror(int x, int n) {
+    x = sk_int_mod(x, 2 * n);
+    if (x >= n) {
+        x = n + ~(x - n);
+    }
+    return x;
+}
+
 static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
                                                      int x, int y,
                                                      SkPMColor* SK_RESTRICT colors,
@@ -404,6 +426,117 @@
     }
 }
 
+static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
+                                      int x, int y,
+                                      SkPMColor* SK_RESTRICT colors,
+                                      int count) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(1 == s.fBitmap->width());
+
+    int iY0, iY1, iSubY;
+
+    if (s.fDoFilter) {
+        SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
+        uint32_t xy[2];
+
+        mproc(s, xy, 1, x, y);
+
+        iY0 = xy[0] >> 18;
+        iY1 = xy[0] & 0x3FFF;
+        iSubY = (xy[0] >> 14) & 0xF;
+    } else {
+        int yTemp;
+
+        if (s.fInvType > SkMatrix::kTranslate_Mask) {
+            SkPoint pt;
+            s.fInvProc(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf,
+                       &pt);
+            // When the matrix has a scale component the setup code in
+            // chooseProcs multiples the inverse matrix by the inverse of the
+            // bitmap's width and height. Since this method is going to do
+            // its own tiling and sampling we need to undo that here.
+            if (SkShader::kClamp_TileMode != s.fTileModeX ||
+                SkShader::kClamp_TileMode != s.fTileModeY) {
+                yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
+            } else {
+                yTemp = SkScalarFloorToInt(pt.fY);
+            }
+        } else {
+            yTemp = s.fFilterOneY + y;
+        }
+
+        const int stopY = s.fBitmap->height();
+        switch (s.fTileModeY) {
+            case SkShader::kClamp_TileMode:
+                iY0 = SkClampMax(yTemp, stopY-1);
+                break;
+            case SkShader::kRepeat_TileMode:
+                iY0 = sk_int_mod(yTemp, stopY);
+                break;
+            case SkShader::kMirror_TileMode:
+            default:
+                iY0 = sk_int_mirror(yTemp, stopY);
+                break;
+        }
+
+#ifdef SK_DEBUG
+        {
+            SkPoint pt;
+            s.fInvProc(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf,
+                       &pt);
+            if (s.fInvType > SkMatrix::kTranslate_Mask &&
+                (SkShader::kClamp_TileMode != s.fTileModeX ||
+                 SkShader::kClamp_TileMode != s.fTileModeY)) {
+                pt.fY *= s.fBitmap->height();
+            }
+            int iY2;
+
+            switch (s.fTileModeY) {
+            case SkShader::kClamp_TileMode:
+                iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
+                break;
+            case SkShader::kRepeat_TileMode:
+                iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
+                break;
+            case SkShader::kMirror_TileMode:
+            default:
+                iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
+                break;
+            }
+
+            SkASSERT(iY0 == iY2);
+        }
+#endif
+    }
+
+    const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
+    SkPMColor color;
+
+    if (s.fDoFilter) {
+        const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
+
+        if (s.fAlphaScale < 256) {
+            Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
+        } else {
+            Filter_32_opaque(iSubY, *row0, *row1, &color);
+        }
+    } else {
+        if (s.fAlphaScale < 256) {
+            color = SkAlphaMulQ(*row0, s.fAlphaScale);
+        } else {
+            color = *row0;
+        }
+    }
+
+    sk_memset32(colors, color, count);
+}
+
 static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
                                  SkPMColor* SK_RESTRICT colors, int count) {
     // if we get called, the matrix is too tricky, so we just draw nothing
@@ -433,6 +566,22 @@
 }
 
 SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
+
+    if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
+        return NULL;
+    }
+
+#ifndef SK_IGNORE_1XN_BITMAP_OPT
+    static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+
+    if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
+        if (!fDoFilter && fInvType <= SkMatrix::kTranslate_Mask && !this->setupForTranslate()) {
+            return DoNothing_shaderproc;
+        }
+        return S32_D32_constX_shaderproc;
+    }
+#endif
+
     if (fAlphaScale < 256) {
         return NULL;
     }
@@ -442,9 +591,6 @@
     if (fDoFilter) {
         return NULL;
     }
-    if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
-        return NULL;
-    }
 
     SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
     SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
@@ -581,4 +727,3 @@
 
     return size;
 }
-
diff --git a/src/core/SkBitmapProcState_filter.h b/src/core/SkBitmapProcState_filter.h
index f7848f7..1260665 100644
--- a/src/core/SkBitmapProcState_filter.h
+++ b/src/core/SkBitmapProcState_filter.h
@@ -79,3 +79,47 @@
     *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
 }
 
+// Two color version, where we filter only along 1 axis
+static inline void Filter_32_opaque(unsigned t,
+                                    SkPMColor color0,
+                                    SkPMColor color1,
+                                    SkPMColor* dstColor) {
+    SkASSERT((unsigned)t <= 0xF);
+
+    static const uint32_t mask = gMask_00FF00FF; //0x00FF00FF;
+
+    int scale = 256 - 16*t;
+    uint32_t lo = (color0 & mask) * scale;
+    uint32_t hi = ((color0 >> 8) & mask) * scale;
+
+    scale = 16*t;
+    lo += (color1 & mask) * scale;
+    hi += ((color1 >> 8) & mask) * scale;
+
+    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
+}
+
+// Two color version, where we filter only along 1 axis
+static inline void Filter_32_alpha(unsigned t,
+                                   SkPMColor color0,
+                                   SkPMColor color1,
+                                   SkPMColor* dstColor,
+                                   unsigned alphaScale) {
+    SkASSERT((unsigned)t <= 0xF);
+    SkASSERT(alphaScale <= 256);
+
+    static const uint32_t mask = gMask_00FF00FF; //0x00FF00FF;
+
+    int scale = 256 - 16*t;
+    uint32_t lo = (color0 & mask) * scale;
+    uint32_t hi = ((color0 >> 8) & mask) * scale;
+
+    scale = 16*t;
+    lo += (color1 & mask) * scale;
+    hi += ((color1 >> 8) & mask) * scale;
+
+    lo = ((lo >> 8) & mask) * alphaScale;
+    hi = ((hi >> 8) & mask) * alphaScale;
+
+    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
+}
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
index 9fa3cd6..15c17b6 100644
--- a/src/core/SkBitmapProcState_matrixProcs.cpp
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -96,10 +96,10 @@
 #endif
 
 #define MAKENAME(suffix)        GeneralXY ## suffix
-#define PREAMBLE(state)         SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; \
-                                SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY; \
-                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcX = (state).fTileLowBitsProcX; \
-                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcY = (state).fTileLowBitsProcY
+#define PREAMBLE(state)         SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; (void) tileProcX; \
+                                SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY; (void) tileProcY; \
+                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcX = (state).fTileLowBitsProcX; (void) tileLowBitsProcX; \
+                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcY = (state).fTileLowBitsProcY; (void) tileLowBitsProcY
 #define PREAMBLE_PARAM_X        , SkBitmapProcState::FixedTileProc tileProcX, SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcX
 #define PREAMBLE_PARAM_Y        , SkBitmapProcState::FixedTileProc tileProcY, SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcY
 #define PREAMBLE_ARG_X          , tileProcX, tileLowBitsProcX
@@ -519,4 +519,3 @@
     fTileLowBitsProcY = choose_tile_lowbits_proc(fTileModeY);
     return GeneralXY_Procs[index];
 }
-
diff --git a/src/core/SkBitmapProcState_procs.h b/src/core/SkBitmapProcState_procs.h
index d43305b..da9ca89 100644
--- a/src/core/SkBitmapProcState_procs.h
+++ b/src/core/SkBitmapProcState_procs.h
@@ -341,4 +341,3 @@
 #include "SkBitmapProcState_shaderproc.h"
 
 #undef NAME_WRAP
-
diff --git a/src/core/SkBitmapSampler.cpp b/src/core/SkBitmapSampler.cpp
index 755f717..da8aa8a 100644
--- a/src/core/SkBitmapSampler.cpp
+++ b/src/core/SkBitmapSampler.cpp
@@ -414,4 +414,3 @@
     }
     return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy));
 }
-
diff --git a/src/core/SkBlitMask_D32.cpp b/src/core/SkBlitMask_D32.cpp
index 4fb974b..80cc787 100644
--- a/src/core/SkBlitMask_D32.cpp
+++ b/src/core/SkBlitMask_D32.cpp
@@ -594,4 +594,3 @@
     }
     return NULL;
 }
-
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
index 9453488..309e459 100644
--- a/src/core/SkBlitRow_D16.cpp
+++ b/src/core/SkBlitRow_D16.cpp
@@ -251,4 +251,3 @@
     }
     return proc;
 }
-
diff --git a/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp
index c2cb855..c858af6 100644
--- a/src/core/SkBlitRow_D32.cpp
+++ b/src/core/SkBlitRow_D32.cpp
@@ -247,4 +247,3 @@
     SkASSERT(proc);
     return proc;
 }
-
diff --git a/src/core/SkBlitRow_D4444.cpp b/src/core/SkBlitRow_D4444.cpp
index c32e812..026d156 100644
--- a/src/core/SkBlitRow_D4444.cpp
+++ b/src/core/SkBlitRow_D4444.cpp
@@ -216,5 +216,3 @@
 
     return gProcs4444[flags];
 }
-
-
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index efe9d11..f43ad75 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -19,6 +19,7 @@
 #include "SkTLazy.h"
 #include "SkUtils.h"
 #include "SkXfermode.h"
+#include "SkString.h"
 
 SkBlitter::~SkBlitter() {}
 
@@ -663,6 +664,21 @@
         }
     }
 
+#ifdef SK_DEVELOPER
+    virtual void toString(SkString* str) const SK_OVERRIDE {
+        str->append("Sk3DShader: (");
+
+        if (NULL != fProxy) {
+            str->append("Proxy: ");
+            fProxy->toString(str);
+        }
+
+        this->INHERITED::toString(str);
+
+        str->append(")");
+    }
+#endif
+
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sk3DShader)
 
 protected:
@@ -1002,4 +1018,3 @@
     fShader->endContext();
     fShader->unref();
 }
-
diff --git a/src/core/SkBlitter_4444.cpp b/src/core/SkBlitter_4444.cpp
index ec62523..3844b76 100644
--- a/src/core/SkBlitter_4444.cpp
+++ b/src/core/SkBlitter_4444.cpp
@@ -478,4 +478,3 @@
     }
     return blitter;
 }
-
diff --git a/src/core/SkBlitter_A1.cpp b/src/core/SkBlitter_A1.cpp
index 5638477..b64afe2 100644
--- a/src/core/SkBlitter_A1.cpp
+++ b/src/core/SkBlitter_A1.cpp
@@ -48,4 +48,3 @@
         *dst |= rite_mask;
     }
 }
-
diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
index 92a4971..4213c6b 100644
--- a/src/core/SkBlitter_A8.cpp
+++ b/src/core/SkBlitter_A8.cpp
@@ -343,4 +343,3 @@
         alpha += mask.fRowBytes;
     }
 }
-
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
index 8b47481..16487ad 100644
--- a/src/core/SkBlitter_ARGB32.cpp
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -636,4 +636,3 @@
         }
     }
 }
-
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
index c2a0062..db7cc69 100644
--- a/src/core/SkBlitter_Sprite.cpp
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -85,4 +85,3 @@
     }
     return blitter;
 }
-
diff --git a/src/core/SkBuffer.h b/src/core/SkBuffer.h
index 6722eb8..9633389 100644
--- a/src/core/SkBuffer.h
+++ b/src/core/SkBuffer.h
@@ -136,4 +136,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 096b42d..63f783a 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -58,6 +58,22 @@
 #ifdef SK_DEBUG
 #include "SkPixelRef.h"
 
+/*
+ *  Some pixelref subclasses can support being "locked" from another thread
+ *  during the lock-scope of skia calling them. In these instances, this balance
+ *  check will fail, but may not be indicative of a problem, so we allow a build
+ *  flag to disable this check.
+ *
+ *  Potentially another fix would be to have a (debug-only) virtual or flag on
+ *  pixelref, which could tell us at runtime if this check is valid. That would
+ *  eliminate the need for this heavy-handed build check.
+ */
+#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
+class AutoCheckLockCountBalance {
+public:
+    AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
+};
+#else
 class AutoCheckLockCountBalance {
 public:
     AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
@@ -72,6 +88,7 @@
     const SkPixelRef* fPixelRef;
     int               fLockCount;
 };
+#endif
 
 class AutoCheckNoSetContext {
 public:
@@ -1542,10 +1559,13 @@
         }
     }
 
-    SkPath  path;
-    path.addOval(oval);
-    // call the non-virtual version
-    this->SkCanvas::drawPath(path, paint);
+    LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawOval(iter, oval, looper.paint());
+    }
+
+    LOOPER_END
 }
 
 void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
@@ -1708,10 +1728,10 @@
     c.fBottom = SkPin32(center.fBottom, c.fTop, h);
 
     const SkScalar srcX[4] = {
-        0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), w
+        0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
     };
     const SkScalar srcY[4] = {
-        0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), h
+        0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
     };
     SkScalar dstX[4] = {
         dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
@@ -2115,5 +2135,3 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkCanvas::ClipVisitor::~ClipVisitor() { }
-
-
diff --git a/src/core/SkChunkAlloc.cpp b/src/core/SkChunkAlloc.cpp
index 25ef4bc..30cc4e1 100644
--- a/src/core/SkChunkAlloc.cpp
+++ b/src/core/SkChunkAlloc.cpp
@@ -136,4 +136,3 @@
     }
     return false;
 }
-
diff --git a/src/core/SkColor.cpp b/src/core/SkColor.cpp
index cb5f15c..6fa239f 100644
--- a/src/core/SkColor.cpp
+++ b/src/core/SkColor.cpp
@@ -111,4 +111,3 @@
     }
     return SkColorSetARGB(a, r, g, b);
 }
-
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 747b2ff..383397d 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -10,6 +10,7 @@
 #include "SkFlattenableBuffers.h"
 #include "SkShader.h"
 #include "SkUnPreMultiply.h"
+#include "SkString.h"
 
 SK_DEFINE_INST_COUNT(SkColorFilter)
 
@@ -40,7 +41,7 @@
     return SkUnPreMultiply::PMColorToColor(dst);
 }
 
-GrEffect* SkColorFilter::asNewEffect(GrContext*) const {
+GrEffectRef* SkColorFilter::asNewEffect(GrContext*) const {
     return NULL;
 }
 
@@ -117,3 +118,17 @@
     fFilter->filterSpan16(result, count, result);
 }
 
+#ifdef SK_DEVELOPER
+void SkFilterShader::toString(SkString* str) const {
+    str->append("SkFilterShader: (");
+
+    str->append("Shader: ");
+    fShader->toString(str);
+    str->append(" Filter: ");
+    // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp
index 1375c58..e1ebf92 100644
--- a/src/core/SkColorTable.cpp
+++ b/src/core/SkColorTable.cpp
@@ -146,9 +146,11 @@
     fFlags = buffer.readUInt();
     fCount = buffer.getArrayCount();
     fColors = (SkPMColor*)sk_malloc_throw(fCount * sizeof(SkPMColor));
-    const uint32_t countRead = buffer.readColorArray(fColors);
+    SkDEBUGCODE(const uint32_t countRead =) buffer.readColorArray(fColors);
+#ifdef SK_DEBUG
     SkASSERT((unsigned)fCount <= 256);
     SkASSERT(countRead == fCount);
+#endif
 }
 
 void SkColorTable::flatten(SkFlattenableWriteBuffer& buffer) const {
diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
index 92ffaf7..fd3b21e 100644
--- a/src/core/SkComposeShader.cpp
+++ b/src/core/SkComposeShader.cpp
@@ -13,6 +13,7 @@
 #include "SkColorShader.h"
 #include "SkFlattenableBuffers.h"
 #include "SkXfermode.h"
+#include "SkString.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -172,3 +173,20 @@
         } while (count > 0);
     }
 }
+
+#ifdef SK_DEVELOPER
+void SkComposeShader::toString(SkString* str) const {
+    str->append("SkComposeShader: (");
+
+    str->append("ShaderA: ");
+    fShaderA->toString(str);
+    str->append(" ShaderB: ");
+    fShaderB->toString(str);
+    str->append(" Xfermode: ");
+    // TODO: add "fMode->toString(str);" once SkXfermode::toString is added
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/core/SkConcaveToTriangles.cpp b/src/core/SkConcaveToTriangles.cpp
deleted file mode 100644
index 4821e69..0000000
--- a/src/core/SkConcaveToTriangles.cpp
+++ /dev/null
@@ -1,962 +0,0 @@
-
-/*
- * Copyright 2009 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.
- */
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// This is an implementation of the triangulation algorithm described by Alain
-// Fournier and Delfin Montuno, in "Triangulating Simple Polygons and Equivalent
-// Problems", in the ACM Transactions on Graphics, vol. 3, no. 2, April 1984,
-// pp. 153-174.
-//
-// No new vertices are created in the triangulation: triangles are constructed
-// only from the points in the original polygon, so there is no possibility for
-// cracks to develop from finite precision arithmetic.
-////////////////////////////////////////////////////////////////////////////////
-
-// TODO:
-// - RemoveDegenerateTrapezoids() was added to make the code robust, but it
-//   unfortunately introduces T-vertices. Make it robust without T-vertices.
-// - It should be easy enough to detect whether the outer contour is right- or
-//   left-handed by looking at the top vertex, which is available in the
-//   pre-sort during trapezoidization. Use this information in angleIsConvex()
-//   to allowed either handedness outer contour. In either case, though, holes
-//   need to have the opposite orientation.
-// - SkTHeapSort was broken, so I wrote a bubble sort so that I could make other
-//   things work. Use SkQSort() instead.
-// - The ActiveTrapezoid array does a linear search which is O(n) inefficient.
-//   Use SkSearch to implement O(log n) binary search and insertion sort.
-// - There is no need to use SkTDArray for everything. Use SkAutoTMalloc for
-//   everything else.
-
-#include "SkConcaveToTriangles.h"
-#include "SkTDArray.h"
-#include "SkGeometry.h"
-#include "SkTSort.h"
-
-// This is used to prevent runaway code bugs, and can probably be removed after
-// the code has been proven robust.
-#define kMaxCount 1000
-
-#define DEBUG
-#ifdef DEBUG
-//------------------------------------------------------------------------------
-// Debugging support
-//------------------------------------------------------------------------------
-
-#include <cstdio>
-#include <stdarg.h>
-
-static int gDebugLevel = 0;   // This enables debug reporting.
-
-static void DebugPrintf(const char *format, ...) {
-    if (gDebugLevel > 0) {
-        va_list ap;
-        va_start(ap, format);
-        vprintf(format, ap);
-        va_end(ap);
-    }
-}
-
-static void FailureMessage(const char *format, ...) {
-    if (1) {
-        printf("FAILURE: ");
-        va_list ap;
-        va_start(ap, format);
-        vprintf(format, ap);
-        va_end(ap);
-    }
-}
-#else  // !DEBUG
-inline void DebugPrintf(const char *format, ...) {}
-inline void FailureMessage(const char *format, ...) {}
-#endif  // DEBUG
-
-
-// Forward declaration.
-class Vertex;
-
-
-//------------------------------------------------------------------------------
-// The Trapezoid (actually, up to two of them) is embedded into a Vertex, whose
-// point() provides the top of the Trapezoid, whereas the bottom, and the left
-// and right edges, are stored in the Trapezoid. The edges are represented by
-// their tail point; the head is the successive point modulo the number of
-// points in the polygon. Only the Y coordinate of the top and bottom are
-// relevant.
-//------------------------------------------------------------------------------
-class Trapezoid {
-public:
-    const Vertex* left()   const    { return fLeft;   }
-    const Vertex* right()  const    { return fRight;  }
-    const Vertex* bottom() const    { return fBottom; }
-    Vertex* left()                  { return fLeft;   }
-    Vertex* right()                 { return fRight;  }
-    Vertex* bottom()                { return fBottom; }
-    void   setLeft(Vertex *left)    { fLeft   = left;   }
-    void  setRight(Vertex *right)   { fRight  = right;  }
-    void setBottom(Vertex *bottom)  { fBottom = bottom; }
-    void nullify()                  { setBottom(NULL); }
-
-    bool operator<(Trapezoid &t1)   { return compare(t1) < 0; }
-    bool operator>(Trapezoid &t1)   { return compare(t1) > 0; }
-
-private:
-    Vertex *fLeft, *fRight, *fBottom;
-
-    // These return a number that is less than, equal to, or greater than zero
-    // depending on whether the trapezoid or point is to the left, on, or to the
-    // right.
-    SkScalar compare(const Trapezoid &t1) const;
-    SkScalar compare(const SkPoint &p) const;
-};
-
-
-//------------------------------------------------------------------------------
-// The ActiveTrapezoids are a sorted list containing the currently active
-// trapezoids, i.e. those that have the top, left, and right, but still need the
-// bottom. This could use some optimization, to reduce search time from O(n) to
-// O(log n).
-//------------------------------------------------------------------------------
-class ActiveTrapezoids {
-public:
-    ActiveTrapezoids() { fTrapezoids.setCount(0); }
-
-    size_t count() const { return fTrapezoids.count(); }
-
-    // Select an unused trapezoid from the Vertex vt, initialize it with the
-    // left and right edges, and insert it into the sorted list.
-    bool insertNewTrapezoid(Vertex *vt, Vertex *left, Vertex *right);
-
-    // Remove the specified Trapezoids from the active list.
-    void remove(Trapezoid *t);
-
-    // Determine whether the given point lies within any active trapezoid, and
-    // return a pointer to that Trapezoid.
-    bool withinActiveTrapezoid(const SkPoint &pt, Trapezoid **tp);
-
-    // Find an active trapezoid that contains the given edge.
-    Trapezoid* getTrapezoidWithEdge(const Vertex *edge);
-
-private:
-    // Insert the specified Trapezoid into the sorted list.
-    void insert(Trapezoid *t);
-
-    // The sorted list of active trapezoids. This is O(n), and would benefit
-    // a 2-3 tree o achieve O(log n).
-    SkTDArray<Trapezoid*> fTrapezoids;  // Fournier suggests a 2-3 tree instead.
-};
-
-
-//------------------------------------------------------------------------------
-// The Vertex is used to communicate information between the various phases of
-// triangulation.
-//------------------------------------------------------------------------------
-class Vertex {
-public:
-    enum VertexType { MONOTONE, CONVEX, CONCAVE };
-
-    Trapezoid fTrap0;
-    Trapezoid fTrap1;
-
-    const SkPoint &point() const        { return fPoint; }
-    void setPoint(const SkPoint &point) { fPoint = point; }
-
-    // The next and previous vertices around the polygon.
-    Vertex *next()                      { return fNext; }
-    Vertex *prev()                      { return fPrev; }
-    const Vertex *next() const          { return fNext; }
-    const Vertex *prev() const          { return fPrev; }
-    void setNext(Vertex *next)          { fNext = next; }
-    void setPrev(Vertex *prev)          { fPrev = prev; }
-
-    void setDone(bool done)             { fDone = done; }
-    bool done() const                   { return fDone; }
-
-    // Trapezoid accessors return non-null for any complete trapezoids.
-    void trapezoids(Trapezoid **trap0, Trapezoid **trap1) {
-        *trap0 = (fTrap0.bottom() != NULL) ? &fTrap0 : NULL;
-        *trap1 = (fTrap1.bottom() != NULL) ? &fTrap1 : NULL;
-    }
-
-    // Eliminate a trapezoid.
-    void nullifyTrapezoid() {
-        if (fTrap1.bottom() != NULL) {
-            DebugPrintf("Unexpected non-null second trapezoid.\n");
-            fTrap1.nullify();
-            return;
-        }
-        fTrap0.nullify();
-    }
-
-    // Determine whether the edge specified by this Vertex shares the given top
-    // and bottom.
-    bool shareEdge(Vertex *top, Vertex *bottom);
-
-    // Determines whether the angle specified by { prev, this, next } is convex.
-    // Note that collinear is considered to be convex.
-    bool angleIsConvex();
-
-    // Remove this Vertex from the { prev, next } linked list.
-    void delink() {
-        Vertex *p = prev(),
-               *n = next();
-        p->setNext(n);
-        n->setPrev(p);
-    }
-
-    // Return a number that is less than, equal to, or greater than zero
-    // depending on whether the point is to the left, on, or to the right of the
-    // edge that has this Vertex as a base.
-    SkScalar compare(const SkPoint &pt) const;
-
-    // Classify the vertex, and return its sorted edges.
-    VertexType classify(Vertex **e0, Vertex **e1);
-
-    // This helps determine unimonotonicity.
-    Vertex *diagonal();
-
-private:
-    SkPoint fPoint;
-    Vertex *fNext;
-    Vertex *fPrev;
-    bool fDone;
-};
-
-
-bool Vertex::angleIsConvex() {
-    SkPoint vPrev = prev()->point() - point(),
-            vNext = next()->point() - point();
-    // TODO(turk): There might be overflow in fixed-point.
-    return SkPoint::CrossProduct(vNext, vPrev) >= 0;
-}
-
-
-bool Vertex::shareEdge(Vertex *top, Vertex *bottom) {
-    return (((this == top)    && (this->next() == bottom)) ||
-            ((this == bottom) && (this->next() == top)));
-}
-
-
-SkScalar Vertex::compare(const SkPoint &pt) const  {
-    SkPoint ve = next()->point() - point(),
-            vp = pt              - point();
-    SkScalar d;
-    if (ve.fY == 0) {
-        // Return twice the distance to the center of the horizontal edge.
-        d = ve.fX + pt.fX - next()->point().fX;
-    } else {
-        // Return the distance to the edge, scaled by the edge length.
-        d = SkPoint::CrossProduct(ve, vp);
-        if (ve.fY > 0) d = -d;
-    }
-    return d;
-}
-
-
-SkScalar Trapezoid::compare(const SkPoint &pt) const {
-    SkScalar d = left()->compare(pt);
-    if (d <= 0)
-        return d;   // Left of the left edge.
-    d = right()->compare(pt);
-    if (d >= 0)
-        return d;   // Right of the right edge.
-    return 0;       // Somewhere between the left and the right edges.
-}
-
-
-
-SkScalar Trapezoid::compare(const Trapezoid &t1) const {
-#if 1
-    SkScalar d = left()->compare(t1.left()->point());
-    if (d == 0)
-        d = right()->compare(t1.right()->point());
-    return d;
-#else
-    SkScalar dl =  left()->compare( t1.left()->point()),
-             dr = right()->compare(t1.right()->point());
-    if (dl < 0 && dr < 0)
-        return dr;
-    if (dl > 0 && dr > 0)
-        return dl;
-    return 0;
-#endif
-}
-
-
-Trapezoid* ActiveTrapezoids::getTrapezoidWithEdge(const Vertex *edge) {
-    DebugPrintf("Entering getTrapezoidWithEdge(): looking through %d\n",
-           fTrapezoids.count());
-    DebugPrintf("trying to find %p: ", edge);
-    Trapezoid **tp;
-    for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) {
-        SkASSERT(tp != NULL);
-        SkASSERT(*tp != NULL);
-        DebugPrintf("%p and %p, ", (**tp).left(), (**tp).right());
-        if ((**tp).left() == edge || (**tp).right() == edge) {
-            DebugPrintf("\ngetTrapezoidWithEdge found the trapezoid\n");
-            return *tp;
-        }
-    }
-    DebugPrintf("getTrapezoidWithEdge found no trapezoid\n");
-    return NULL;
-}
-
-
-bool ActiveTrapezoids::insertNewTrapezoid(Vertex *vt,
-                                          Vertex *left,
-                                          Vertex *right) {
-    DebugPrintf("Inserting a trapezoid...");
-    if (vt->fTrap0.left() == NULL && vt->fTrap0.right() == NULL) {
-        vt->fTrap0.setLeft(left);
-        vt->fTrap0.setRight(right);
-        insert(&vt->fTrap0);
-    } else if (vt->fTrap1.left() == NULL && vt->fTrap1.right() == NULL) {
-        DebugPrintf("a second one...");
-        vt->fTrap1.setLeft(left);
-        vt->fTrap1.setRight(right);
-        if (vt->fTrap1 < vt->fTrap0) {  // Keep trapezoids sorted.
-            remove(&vt->fTrap0);
-            Trapezoid t = vt->fTrap0;
-            vt->fTrap0 = vt->fTrap1;
-            vt->fTrap1 = t;
-            insert(&vt->fTrap0);
-        }
-        insert(&vt->fTrap1);
-    } else {
-        FailureMessage("More than 2 trapezoids requested for a vertex\n");
-        return false;
-    }
-    DebugPrintf(" done. %d incomplete trapezoids\n", fTrapezoids.count());
-    return true;
-}
-
-
-void ActiveTrapezoids::insert(Trapezoid *t) {
-    Trapezoid **tp;
-    for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp)
-        if (**tp > *t)
-            break;
-    fTrapezoids.insert(tp - fTrapezoids.begin(), 1, &t);
-    // SHOULD VERIFY THAT ALL TRAPEZOIDS ARE PROPERLY SORTED
-}
-
-
-void ActiveTrapezoids::remove(Trapezoid *t) {
-    DebugPrintf("Removing a trapezoid...");
-    for (Trapezoid **tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) {
-        if (*tp == t) {
-            fTrapezoids.remove(tp - fTrapezoids.begin());
-            DebugPrintf(" done.\n");
-            return;
-        }
-    }
-    DebugPrintf(" Arghh! Panic!\n");
-    SkASSERT(t == 0);   // Cannot find t in active trapezoid list.
-}
-
-
-bool ActiveTrapezoids::withinActiveTrapezoid(const SkPoint &pt,
-                                             Trapezoid **trap) {
-    DebugPrintf("Entering withinActiveTrapezoid()\n");
-    // This is where a good search data structure would be helpful.
-    Trapezoid **t;
-    for (t = fTrapezoids.begin(); t < fTrapezoids.end(); ++t) {
-        if ((**t).left()->compare(pt) <= 0) {
-            // The point is to the left of the left edge of this trapezoid.
-            DebugPrintf("withinActiveTrapezoid: Before a trapezoid\n");
-            *trap = *t;     // Return the place where a new trapezoid would go.
-// We have a bug with the sorting -- look through everything.
-            continue;
-//             return false;   // Outside all trapezoids, since they are sorted.
-        }
-        // The point is to the right of the left edge of this trapezoid.
-
-        if ((**t).right()->compare(pt) < 0) {
-            // The point is to the left of the right edge.
-            DebugPrintf("withinActiveTrapezoid: Within an Active Trapezoid\n");
-            *trap = *t;
-            return true;
-        }
-    }
-
-    // The point is to the right of all trapezoids.
-    DebugPrintf("withinActiveTrapezoid: After all trapezoids\n");
-    *trap = NULL;
-    return false;
-}
-
-
-Vertex* Vertex::diagonal() {
-    Vertex *diag = NULL;
-    if (fTrap0.bottom() != NULL) {
-        if (!fTrap0.left() ->shareEdge(this, fTrap0.bottom()) &&
-            !fTrap0.right()->shareEdge(this, fTrap0.bottom())
-        ) {
-            diag = fTrap0.bottom();
-            fTrap0 = fTrap1;
-            fTrap1.nullify();
-        } else if (fTrap1.bottom() != NULL &&
-                  !fTrap1.left() ->shareEdge(this, fTrap1.bottom()) &&
-                  !fTrap1.right()->shareEdge(this, fTrap1.bottom())
-        ) {
-            diag = fTrap1.bottom();
-            fTrap1.nullify();
-        }
-    }
-    return diag;
-}
-
-
-// We use this to sort the edges vertically for a scan-conversion type of
-// operation.
-class VertexPtr {
-public:
-    Vertex *vt;
-};
-
-
-static bool operator<(VertexPtr &v0, VertexPtr &v1) {
-    // DebugPrintf("< %p %p\n", &v0, &v1);
-    if (v0.vt->point().fY < v1.vt->point().fY)  return true;
-    if (v0.vt->point().fY > v1.vt->point().fY)  return false;
-    if (v0.vt->point().fX < v1.vt->point().fX)  return true;
-    else                                        return false;
-}
-
-
-#if 0 // UNUSED
-static bool operator>(VertexPtr &v0, VertexPtr &v1) {
-    // DebugPrintf("> %p %p\n", &v0, &v1);
-    if (v0.vt->point().fY > v1.vt->point().fY)  return true;
-    if (v0.vt->point().fY < v1.vt->point().fY)  return false;
-    if (v0.vt->point().fX > v1.vt->point().fX)  return true;
-    else                                        return false;
-}
-#endif
-
-static void SetVertexPoints(size_t numPts, const SkPoint *pt, Vertex *vt) {
-    for (; numPts-- != 0; ++pt, ++vt)
-        vt->setPoint(*pt);
-}
-
-
-static void InitializeVertexTopology(size_t numPts, Vertex *v1) {
-    Vertex *v0 = v1 + numPts - 1, *v_1 = v0 - 1;
-    for (; numPts-- != 0; v_1 = v0, v0 = v1++) {
-        v0->setPrev(v_1);
-        v0->setNext(v1);
-    }
-}
-
-Vertex::VertexType Vertex::classify(Vertex **e0, Vertex **e1) {
-    VertexType type;
-    SkPoint vPrev, vNext;
-    vPrev.fX = prev()->point().fX - point().fX;
-    vPrev.fY = prev()->point().fY - point().fY;
-    vNext.fX = next()->point().fX - point().fX;
-    vNext.fY = next()->point().fY - point().fY;
-
-    // This can probably be simplified, but there are enough potential bugs,
-    // we will leave it expanded until all cases are tested appropriately.
-    if (vPrev.fY < 0) {
-        if (vNext.fY > 0) {
-            // Prev comes from above, Next goes below.
-            type = MONOTONE;
-            *e0 = prev();
-            *e1 = this;
-        } else if (vNext.fY < 0) {
-            // The are both above: sort so that e0 is on the left.
-            type = CONCAVE;
-            if (SkPoint::CrossProduct(vPrev, vNext) <= 0) {
-                *e0 = this;
-                *e1 = prev();
-            } else {
-                *e0 = prev();
-                *e1 = this;
-            }
-        } else {
-            DebugPrintf("### py < 0, ny = 0\n");
-            if (vNext.fX < 0) {
-                type = CONCAVE;
-                *e0 = this;     // flat to the left
-                *e1 = prev();   // concave on the right
-            } else {
-                type = CONCAVE;
-                *e0 = prev();   // concave to the left
-                *e1 = this;     // flat to the right
-            }
-        }
-    } else if (vPrev.fY > 0) {
-        if (vNext.fY < 0) {
-            // Next comes from above, Prev goes below.
-            type = MONOTONE;
-            *e0 = this;
-            *e1 = prev();
-        } else if (vNext.fY > 0) {
-            // They are both below: sort so that e0 is on the left.
-            type = CONVEX;
-            if (SkPoint::CrossProduct(vPrev, vNext) <= 0) {
-                *e0 = prev();
-                *e1 = this;
-            } else {
-                *e0 = this;
-                *e1 = prev();
-            }
-        } else {
-            DebugPrintf("### py > 0, ny = 0\n");
-            if (vNext.fX < 0) {
-                type = MONOTONE;
-                *e0 = this;     // flat to the left
-                *e1 = prev();   // convex on the right - try monotone first
-            } else {
-                type = MONOTONE;
-                *e0 = prev();   // convex to the left - try monotone first
-                *e1 = this;     // flat to the right
-            }
-        }
-    } else {  // vPrev.fY == 0
-        if (vNext.fY < 0) {
-            DebugPrintf("### py = 0, ny < 0\n");
-            if (vPrev.fX < 0) {
-                type = CONCAVE;
-                *e0 = prev();   // flat to the left
-                *e1 = this;     // concave on the right
-            } else {
-                type = CONCAVE;
-                *e0 = this;     // concave on the left - defer
-                *e1 = prev();   // flat to the right
-            }
-        } else if (vNext.fY > 0) {
-            DebugPrintf("### py = 0, ny > 0\n");
-            if (vPrev.fX < 0) {
-                type = MONOTONE;
-                *e0 = prev();   // flat to the left
-                *e1 = this;     // convex on the right - try monotone first
-            } else {
-                type = MONOTONE;
-                *e0 = this;     // convex to the left - try monotone first
-                *e1 = prev();   // flat to the right
-            }
-        } else {
-            DebugPrintf("### py = 0, ny = 0\n");
-            // First we try concave, then monotone, then convex.
-            if (vPrev.fX <= vNext.fX) {
-                type = CONCAVE;
-                *e0 = prev();   // flat to the left
-                *e1 = this;     // flat to the right
-            } else {
-                type = CONCAVE;
-                *e0 = this;     // flat to the left
-                *e1 = prev();   // flat to the right
-            }
-        }
-    }
-    return type;
-}
-
-
-#ifdef DEBUG
-static const char* GetVertexTypeString(Vertex::VertexType type) {
-    const char *typeStr = NULL;
-    switch (type) {
-        case Vertex::MONOTONE:
-            typeStr = "MONOTONE";
-            break;
-        case Vertex::CONCAVE:
-            typeStr = "CONCAVE";
-            break;
-        case Vertex::CONVEX:
-            typeStr = "CONVEX";
-            break;
-    }
-    return typeStr;
-}
-
-
-static void PrintVertices(size_t numPts, Vertex *vt) {
-    DebugPrintf("\nVertices:\n");
-    for (size_t i = 0; i < numPts; i++) {
-        Vertex *e0, *e1;
-        Vertex::VertexType type = vt[i].classify(&e0, &e1);
-        DebugPrintf("%2d: (%.7g, %.7g), prev(%d), next(%d), "
-                    "type(%s), left(%d), right(%d)",
-                    i, vt[i].point().fX, vt[i].point().fY,
-                    vt[i].prev() - vt, vt[i].next() - vt,
-                    GetVertexTypeString(type), e0 - vt, e1 - vt);
-        Trapezoid *trap[2];
-        vt[i].trapezoids(trap, trap+1);
-        for (int j = 0; j < 2; ++j) {
-            if (trap[j] != NULL) {
-                DebugPrintf(", trap(L=%d, R=%d, B=%d)",
-                            trap[j]->left()   - vt,
-                            trap[j]->right()  - vt,
-                            trap[j]->bottom() - vt);
-            }
-        }
-        DebugPrintf("\n");
-    }
-}
-
-
-static void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {
-    DebugPrintf("\nSorted Vertices:\n");
-    for (size_t i = 0; i < numPts; i++) {
-        Vertex *e0, *e1;
-        Vertex *vt = vp[i].vt;
-        Vertex::VertexType type = vt->classify(&e0, &e1);
-        DebugPrintf("%2d: %2d: (%.7g, %.7g), prev(%d), next(%d), "
-                    "type(%s), left(%d), right(%d)",
-                    i, vt - vtBase, vt->point().fX, vt->point().fY,
-                    vt->prev() - vtBase, vt->next() - vtBase,
-                    GetVertexTypeString(type), e0 - vtBase, e1 - vtBase);
-        Trapezoid *trap[2];
-        vt->trapezoids(trap, trap+1);
-        for (int j = 0; j < 2; ++j) {
-            if (trap[j] != NULL) {
-                DebugPrintf(", trap(L=%d, R=%d, B=%d)",
-                            trap[j]->left()   - vtBase,
-                            trap[j]->right()  - vtBase,
-                            trap[j]->bottom() - vtBase);
-            }
-        }
-        DebugPrintf("\n");
-    }
-}
-#else  // !DEBUG
-inline void PrintVertices(size_t numPts, Vertex *vt) {}
-inline void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {}
-#endif  // !DEBUG
-
-
-// SkTHeapSort is broken, so we use a bubble sort in the meantime.
-template <typename T>
-void BubbleSort(T *array, size_t count) {
-    bool sorted;
-    size_t count_1 = count - 1;
-    do {
-        sorted = true;
-        for (size_t i = 0; i < count_1; ++i) {
-            if (array[i + 1] < array[i]) {
-                T t = array[i];
-                array[i] = array[i + 1];
-                array[i + 1] = t;
-                sorted = false;
-            }
-        }
-    } while (!sorted);
-}
-
-
-// Remove zero-height trapezoids.
-static void RemoveDegenerateTrapezoids(size_t numVt, Vertex *vt) {
-    for (; numVt-- != 0; vt++) {
-        Trapezoid *traps[2];
-        vt->trapezoids(traps, traps+1);
-        if (traps[1] != NULL &&
-                vt->point().fY >= traps[1]->bottom()->point().fY) {
-            traps[1]->nullify();
-            traps[1] = NULL;
-        }
-        if (traps[0] != NULL &&
-                vt->point().fY >= traps[0]->bottom()->point().fY) {
-            if (traps[1] != NULL) {
-                *traps[0] = *traps[1];
-                traps[1]->nullify();
-            } else {
-                traps[0]->nullify();
-            }
-        }
-    }
-}
-
-
-// Enhance the polygon with trapezoids.
-static bool ConvertPointsToVertices(size_t numPts, const SkPoint *pts, Vertex *vta) {
-    DebugPrintf("ConvertPointsToVertices()\n");
-
-    // Clear everything.
-    DebugPrintf("Zeroing vertices\n");
-    sk_bzero(vta, numPts * sizeof(*vta));
-
-    // Initialize vertices.
-    DebugPrintf("Initializing vertices\n");
-    SetVertexPoints(numPts, pts, vta);
-    InitializeVertexTopology(numPts, vta);
-
-    PrintVertices(numPts, vta);
-
-    SkTDArray<VertexPtr> vtptr;
-    vtptr.setCount(numPts);
-    for (int i = numPts; i-- != 0;)
-        vtptr[i].vt = vta + i;
-    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
-    DebugPrintf("Sorting vertrap ptr array [%d] %p %p\n", vtptr.count(),
-        &vtptr[0], &vtptr[vtptr.count() - 1]
-    );
-//  SkTHeapSort(vtptr.begin(), vtptr.count());
-    BubbleSort(vtptr.begin(), vtptr.count());
-    DebugPrintf("Done sorting\n");
-    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
-
-    DebugPrintf("Traversing sorted vertrap ptrs\n");
-    ActiveTrapezoids incompleteTrapezoids;
-    for (VertexPtr *vtpp = vtptr.begin(); vtpp < vtptr.end(); ++vtpp) {
-        DebugPrintf("%d: sorted vertrap %d\n",
-                    vtpp - vtptr.begin(), vtpp->vt - vta);
-        Vertex *vt = vtpp->vt;
-        Vertex *e0, *e1;
-        Trapezoid *t;
-        switch (vt->classify(&e0, &e1)) {
-            case Vertex::MONOTONE:
-            monotone:
-                DebugPrintf("MONOTONE %d %d\n", e0 - vta, e1 - vta);
-                // We should find one edge.
-                t = incompleteTrapezoids.getTrapezoidWithEdge(e0);
-                if (t == NULL) {      // One of the edges is flat.
-                    DebugPrintf("Monotone: cannot find a trapezoid with e0: "
-                                "trying convex\n");
-                    goto convex;
-                }
-                t->setBottom(vt);     // This trapezoid is now complete.
-                incompleteTrapezoids.remove(t);
-
-                if (e0 == t->left())  // Replace the left edge.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right());
-                else                  // Replace the right edge.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e1);
-                break;
-
-            case Vertex::CONVEX:      // Start of a new trapezoid.
-            convex:
-                // We don't expect to find any edges.
-                DebugPrintf("CONVEX %d %d\n", e0 - vta, e1 - vta);
-                if (incompleteTrapezoids.withinActiveTrapezoid(
-                        vt->point(), &t)) {
-                    // Complete trapezoid.
-                    SkASSERT(t != NULL);
-                    t->setBottom(vt);
-                    incompleteTrapezoids.remove(t);
-                    // Introduce two new trapezoids.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e0);
-                    incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right());
-                } else {
-                    // Insert a new trapezoid.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, e0, e1);
-                }
-                break;
-
-            case Vertex::CONCAVE:   // End of a trapezoid.
-                DebugPrintf("CONCAVE %d %d\n", e0 - vta, e1 - vta);
-                // We should find two edges.
-                t = incompleteTrapezoids.getTrapezoidWithEdge(e0);
-                if (t == NULL) {
-                    DebugPrintf("Concave: cannot find a trapezoid with e0: "
-                                " trying monotone\n");
-                    goto monotone;
-                }
-                SkASSERT(t != NULL);
-                if (e0 == t->left() && e1 == t->right()) {
-                    DebugPrintf(
-                            "Concave edges belong to the same trapezoid.\n");
-                    // Edges belong to the same trapezoid.
-                    // Complete trapezoid & transfer it from the active list.
-                    t->setBottom(vt);
-                    incompleteTrapezoids.remove(t);
-                } else {  // Edges belong to different trapezoids
-                    DebugPrintf(
-                            "Concave edges belong to different trapezoids.\n");
-                    // Complete left and right trapezoids.
-                    Trapezoid *s = incompleteTrapezoids.getTrapezoidWithEdge(
-                                                                            e1);
-                    if (s == NULL) {
-                        DebugPrintf(
-                                "Concave: cannot find a trapezoid with e1: "
-                                " trying monotone\n");
-                        goto monotone;
-                    }
-                    t->setBottom(vt);
-                    s->setBottom(vt);
-                    incompleteTrapezoids.remove(t);
-                    incompleteTrapezoids.remove(s);
-
-                    // Merge the two trapezoids into one below this vertex.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(),
-                                                                s->right());
-                }
-                break;
-        }
-    }
-
-    RemoveDegenerateTrapezoids(numPts, vta);
-
-    DebugPrintf("Done making trapezoids\n");
-    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
-
-    size_t k = incompleteTrapezoids.count();
-    if (k > 0) {
-        FailureMessage("%d incomplete trapezoids\n", k);
-        return false;
-    }
-    return true;
-}
-
-
-inline void appendTriangleAtVertex(const Vertex *v,
-                                   SkTDArray<SkPoint> *triangles) {
-    SkPoint *p = triangles->append(3);
-    p[0] = v->prev()->point();
-    p[1] = v->point();
-    p[2] = v->next()->point();
-    DebugPrintf(
-          "Appending triangle: { (%.7g, %.7g), (%.7g, %.7g), (%.7g, %.7g) }\n",
-          p[0].fX, p[0].fY,
-          p[1].fX, p[1].fY,
-          p[2].fX, p[2].fY
-    );
-}
-
-
-static size_t CountVertices(const Vertex *first, const Vertex *last) {
-    DebugPrintf("Counting vertices: ");
-    size_t count = 1;
-    for (; first != last; first = first->next()) {
-        ++count;
-        SkASSERT(count <= kMaxCount);
-        if (count >= kMaxCount) {
-            FailureMessage("Vertices do not seem to be in a linked chain\n");
-            break;
-        }
-    }
-    return count;
-}
-
-
-static bool operator<(const SkPoint &p0, const SkPoint &p1) {
-    if (p0.fY < p1.fY)  return true;
-    if (p0.fY > p1.fY)  return false;
-    if (p0.fX < p1.fX)  return true;
-    else                return false;
-}
-
-
-static void PrintLinkedVertices(size_t n, Vertex *vertices) {
-    DebugPrintf("%d vertices:\n", n);
-    Vertex *v;
-    for (v = vertices; n-- != 0; v = v->next())
-        DebugPrintf("   (%.7g, %.7g)\n", v->point().fX, v->point().fY);
-    if (v != vertices)
-        FailureMessage("Vertices are not in a linked chain\n");
-}
-
-
-// Triangulate an unimonotone chain.
-static bool TriangulateMonotone(Vertex *first, Vertex *last,
-                         SkTDArray<SkPoint> *triangles) {
-    DebugPrintf("TriangulateMonotone()\n");
-
-    size_t numVertices = CountVertices(first, last);
-    if (numVertices == kMaxCount) {
-        FailureMessage("Way too many vertices: %d:\n", numVertices);
-        PrintLinkedVertices(numVertices, first);
-        return false;
-    }
-
-    Vertex *start = first;
-    // First find the point with the smallest Y.
-    DebugPrintf("TriangulateMonotone: finding bottom\n");
-    int count = kMaxCount;    // Maximum number of vertices.
-    for (Vertex *v = first->next(); v != first && count-- > 0; v = v->next())
-        if (v->point() < start->point())
-            start = v;
-    if (count <= 0) {
-        FailureMessage("TriangulateMonotone() was given disjoint chain\n");
-        return false;   // Something went wrong.
-    }
-
-    // Start at the far end of the long edge.
-    if (start->prev()->point() < start->next()->point())
-        start = start->next();
-
-    Vertex *current = start->next();
-    while (numVertices >= 3) {
-        if (current->angleIsConvex()) {
-            DebugPrintf("Angle %p is convex\n", current);
-            // Print the vertices
-            PrintLinkedVertices(numVertices, start);
-
-            appendTriangleAtVertex(current, triangles);
-            if (triangles->count() > kMaxCount * 3) {
-                FailureMessage("An extraordinarily large number of triangles "
-                               "were generated\n");
-                return false;
-            }
-            Vertex *save = current->prev();
-            current->delink();
-            current = (save == start || save == start->prev()) ? start->next()
-                                                               : save;
-            --numVertices;
-        } else {
-            if (numVertices == 3) {
-                FailureMessage("Convexity error in TriangulateMonotone()\n");
-                appendTriangleAtVertex(current, triangles);
-                return false;
-            }
-            DebugPrintf("Angle %p is concave\n", current);
-            current = current->next();
-        }
-    }
-    return true;
-}
-
-
-// Split the polygon into sets of unimonotone chains, and eventually call
-// TriangulateMonotone() to convert them into triangles.
-static bool Triangulate(Vertex *first, Vertex *last, SkTDArray<SkPoint> *triangles) {
-    DebugPrintf("Triangulate()\n");
-    Vertex *currentVertex = first;
-    while (!currentVertex->done()) {
-        currentVertex->setDone(true);
-        Vertex *bottomVertex = currentVertex->diagonal();
-        if (bottomVertex != NULL) {
-            Vertex *saveNext = currentVertex->next();
-            Vertex *savePrev = bottomVertex->prev();
-            currentVertex->setNext(bottomVertex);
-            bottomVertex->setPrev(currentVertex);
-            currentVertex->nullifyTrapezoid();
-            bool success = Triangulate(bottomVertex, currentVertex, triangles);
-            currentVertex->setDone(false);
-            bottomVertex->setDone(false);
-            currentVertex->setNext(saveNext);
-            bottomVertex->setPrev(savePrev);
-            bottomVertex->setNext(currentVertex);
-            currentVertex->setPrev(bottomVertex);
-            return Triangulate(currentVertex, bottomVertex, triangles)
-                   && success;
-        } else {
-            currentVertex = currentVertex->next();
-        }
-    }
-    return TriangulateMonotone(first, last, triangles);
-}
-
-
-bool SkConcaveToTriangles(size_t numPts,
-                          const SkPoint pts[],
-                          SkTDArray<SkPoint> *triangles) {
-    DebugPrintf("SkConcaveToTriangles()\n");
-
-    SkTDArray<Vertex> vertices;
-    vertices.setCount(numPts);
-    if (!ConvertPointsToVertices(numPts, pts, vertices.begin()))
-        return false;
-
-    triangles->setReserve(numPts);
-    triangles->setCount(0);
-    return Triangulate(vertices.begin(), vertices.end() - 1, triangles);
-}
diff --git a/src/core/SkConcaveToTriangles.h b/src/core/SkConcaveToTriangles.h
deleted file mode 100644
index e7eb09b..0000000
--- a/src/core/SkConcaveToTriangles.h
+++ /dev/null
@@ -1,31 +0,0 @@
-
-/*
- * Copyright 2009 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.
- */
-
-
-#ifndef SkConcaveToTriangles_DEFINED
-#define SkConcaveToTriangles_DEFINED
-
-#include "SkPoint.h"
-#include "SkTDArray.h"
-
-
-// Triangulate a polygon.
-// The polygon can be convex or concave, and can have holes or multiple contours
-// of arbitrary recursion.
-// The holes must have opposite orientation of the outer contours, whereas
-// islands within the holes must have the same orientation as the outer contour.
-// Contours should be joined by zero-thickness double-edges, to mimic a single
-// polygon.  The polygon should not be self-intersecting.
-// Currently, the outer contour must be right-handed, i.e. it should be oriented
-// in the direction that rotates the X-axis to the Y-axis.
-bool SkConcaveToTriangles(size_t count,
-                          const SkPoint pts[],
-                          SkTDArray<SkPoint> *triangles);
-
-
-#endif  // SkConcaveToTriangles_DEFINED
diff --git a/src/core/SkConfig8888.h b/src/core/SkConfig8888.h
index a891370..96eaef2 100644
--- a/src/core/SkConfig8888.h
+++ b/src/core/SkConfig8888.h
@@ -31,34 +31,17 @@
                           uint32_t g,
                           uint32_t b);
 
+///////////////////////////////////////////////////////////////////////////////
+// Implementation
+
 namespace {
 
 /**
   Copies all pixels from a bitmap to a dst ptr with a given rowBytes and
   Config8888. The bitmap must have kARGB_8888_Config.
  */
-inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels,
-                                     size_t dstRowBytes,
-                                     SkCanvas::Config8888 dstConfig8888,
-                                     const SkBitmap& srcBmp);
 
-/**
-  Copies over all pixels in a bitmap from a src ptr with a given rowBytes and
-  Config8888. The bitmap must have pixels and be kARGB_8888_Config.
- */
-inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp,
-                                     const uint32_t* srcPixels,
-                                     size_t srcRowBytes,
-                                     SkCanvas::Config8888 srcConfig8888);
-
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementation
-
-namespace {
-
-inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels,
+static inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels,
                                      size_t dstRowBytes,
                                      SkCanvas::Config8888 dstConfig8888,
                                      const SkBitmap& srcBmp) {
@@ -72,7 +55,11 @@
     SkConvertConfig8888Pixels(dstPixels, dstRowBytes, dstConfig8888, srcPixels, srcRowBytes, SkCanvas::kNative_Premul_Config8888, w, h);
 }
 
-inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp,
+/**
+  Copies over all pixels in a bitmap from a src ptr with a given rowBytes and
+  Config8888. The bitmap must have pixels and be kARGB_8888_Config.
+ */
+static inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp,
                                      const uint32_t* srcPixels,
                                      size_t srcRowBytes,
                                      SkCanvas::Config8888 srcConfig8888) {
diff --git a/src/core/SkCordic.h b/src/core/SkCordic.h
index aa703eb..fecf645 100644
--- a/src/core/SkCordic.h
+++ b/src/core/SkCordic.h
@@ -26,4 +26,3 @@
 #endif
 
 #endif // SkCordic
-
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
index 47a0aa4..823cb41 100644
--- a/src/core/SkCoreBlitters.h
+++ b/src/core/SkCoreBlitters.h
@@ -189,4 +189,3 @@
                                        void* storage, size_t storageSize);
 
 #endif
-
diff --git a/src/core/SkCubicClipper.cpp b/src/core/SkCubicClipper.cpp
index 3db2feb..aed681b 100644
--- a/src/core/SkCubicClipper.cpp
+++ b/src/core/SkCubicClipper.cpp
@@ -160,4 +160,3 @@
     }
     return true;
 }
-
diff --git a/src/core/SkDebug.cpp b/src/core/SkDebug.cpp
index 4b33836..a10d443 100644
--- a/src/core/SkDebug.cpp
+++ b/src/core/SkDebug.cpp
@@ -48,4 +48,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkDescriptor.h b/src/core/SkDescriptor.h
index 071a89c..79b086f 100644
--- a/src/core/SkDescriptor.h
+++ b/src/core/SkDescriptor.h
@@ -162,4 +162,3 @@
 
 
 #endif
-
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 56b1841..9aa652d 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 #include "SkDevice.h"
+#include "SkDeviceProperties.h"
 #include "SkDraw.h"
 #include "SkImageFilter.h"
 #include "SkMetaData.h"
@@ -23,7 +24,17 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkDevice::SkDevice(const SkBitmap& bitmap)
-    : fBitmap(bitmap)
+    : fBitmap(bitmap), fLeakyProperties(SkDeviceProperties::MakeDefault())
+#ifdef SK_DEBUG
+    , fAttachedToCanvas(false)
+#endif
+{
+    fOrigin.setZero();
+    fMetaData = NULL;
+}
+
+SkDevice::SkDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties)
+    : fBitmap(bitmap), fLeakyProperties(deviceProperties)
 #ifdef SK_DEBUG
     , fAttachedToCanvas(false)
 #endif
@@ -33,8 +44,27 @@
 }
 
 SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque)
+    : fLeakyProperties(SkDeviceProperties::MakeDefault())
 #ifdef SK_DEBUG
-    : fAttachedToCanvas(false)
+    , fAttachedToCanvas(false)
+#endif
+{
+    fOrigin.setZero();
+    fMetaData = NULL;
+
+    fBitmap.setConfig(config, width, height);
+    fBitmap.allocPixels();
+    fBitmap.setIsOpaque(isOpaque);
+    if (!isOpaque) {
+        fBitmap.eraseColor(SK_ColorTRANSPARENT);
+    }
+}
+
+SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
+                   const SkDeviceProperties& deviceProperties)
+    : fLeakyProperties(deviceProperties)
+#ifdef SK_DEBUG
+    , fAttachedToCanvas(false)
 #endif
 {
     fOrigin.setZero();
@@ -77,7 +107,7 @@
                                              int width, int height,
                                              bool isOpaque,
                                              Usage usage) {
-    return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque));
+    return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque, fLeakyProperties));
 }
 
 SkMetaData& SkDevice::getMetaData() {
@@ -317,16 +347,24 @@
 }
 
 void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
-                              const SkPoint pts[], const SkPaint& paint) {
+                          const SkPoint pts[], const SkPaint& paint) {
     draw.drawPoints(mode, count, pts, paint);
 }
 
-void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
-                            const SkPaint& paint) {
+void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
     CHECK_FOR_NODRAW_ANNOTATION(paint);
     draw.drawRect(r, paint);
 }
 
+void SkDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+
+    SkPath path;
+    path.addOval(oval);
+    // call the non-virtual version
+    this->SkDevice::drawPath(draw, path, paint, NULL, true);
+}
+
 void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
                         const SkPaint& paint, const SkMatrix* prePathMatrix,
                         bool pathIsMutable) {
@@ -352,51 +390,11 @@
 void SkDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
                               const SkRect* src, const SkRect& dst,
                               const SkPaint& paint) {
-#ifdef SK_SUPPORT_INT_SRCRECT_DRAWBITMAPRECT
-    SkMatrix matrix;
-    // Compute matrix from the two rectangles
-    {
-        SkRect tmpSrc;
-        if (src) {
-            tmpSrc = *src;
-            // if the extract process clipped off the top or left of the
-            // original, we adjust for that here to get the position right.
-            if (tmpSrc.fLeft > 0) {
-                tmpSrc.fRight -= tmpSrc.fLeft;
-                tmpSrc.fLeft = 0;
-            }
-            if (tmpSrc.fTop > 0) {
-                tmpSrc.fBottom -= tmpSrc.fTop;
-                tmpSrc.fTop = 0;
-            }
-        } else {
-            tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
-                       SkIntToScalar(bitmap.height()));
-        }
-        matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
-    }
-
-    // ensure that src is "valid" before we pass it to our internal routines
-    // and to SkDevice. i.e. sure it is contained inside the original bitmap.
-    SkIRect isrcStorage;
-    SkIRect* isrcPtr = NULL;
-    if (src) {
-        src->roundOut(&isrcStorage);
-        if (!isrcStorage.intersect(0, 0, bitmap.width(), bitmap.height())) {
-            return;
-        }
-        isrcPtr = &isrcStorage;
-    }
-
-    this->drawBitmap(draw, bitmap, isrcPtr, matrix, paint);
-#else
     SkMatrix    matrix;
     SkRect      bitmapBounds, tmpSrc, tmpDst;
     SkBitmap    tmpBitmap;
 
-    bitmapBounds.set(0, 0,
-                     SkIntToScalar(bitmap.width()),
-                     SkIntToScalar(bitmap.height()));
+    bitmapBounds.isetWH(bitmap.width(), bitmap.height());
 
     // Compute matrix from the two rectangles
     if (src) {
@@ -441,6 +439,20 @@
         if (dx || dy) {
             matrix.preTranslate(dx, dy);
         }
+
+        SkRect extractedBitmapBounds;
+        extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
+        if (extractedBitmapBounds == tmpSrc) {
+            // no fractional part in src, we can just call drawBitmap
+            goto USE_DRAWBITMAP;
+        }
+    } else {
+        USE_DRAWBITMAP:
+        // We can go faster by just calling drawBitmap, which will concat the
+        // matrix with the CTM, and try to call drawSprite if it can. If not,
+        // it will make a shader and call drawRect, as we do below.
+        this->drawBitmap(draw, *bitmapPtr, NULL, matrix, paint);
+        return;
     }
 
     // construct a shader, so we can call drawRect with the dst
@@ -459,7 +471,6 @@
     // Call ourself, in case the subclass wanted to share this setup code
     // but handle the drawRect code themselves.
     this->drawRect(draw, *dstPtr, paintWithShader);
-#endif
 }
 
 void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
@@ -531,4 +542,3 @@
     // we're cool with the paint as is
     return false;
 }
-
diff --git a/src/core/SkDeviceProfile.cpp b/src/core/SkDeviceProfile.cpp
index abb78ac..ce3e566 100644
--- a/src/core/SkDeviceProfile.cpp
+++ b/src/core/SkDeviceProfile.cpp
@@ -76,5 +76,3 @@
 
     SkRefCnt_SafeAssign(gGlobalProfile, profile);
 }
-
-
diff --git a/src/core/SkDeviceProfile.h b/src/core/SkDeviceProfile.h
index 2a3f4bb..6ef8dfb 100644
--- a/src/core/SkDeviceProfile.h
+++ b/src/core/SkDeviceProfile.h
@@ -96,4 +96,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkDither.cpp b/src/core/SkDither.cpp
index e733aad..546d479 100644
--- a/src/core/SkDither.cpp
+++ b/src/core/SkDither.cpp
@@ -53,4 +53,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 281c69c..4b06b88 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -20,6 +20,7 @@
 #include "SkRasterizer.h"
 #include "SkScan.h"
 #include "SkShader.h"
+#include "SkString.h"
 #include "SkStroke.h"
 #include "SkTemplatesPriv.h"
 #include "SkTLazy.h"
@@ -28,6 +29,7 @@
 #include "SkAutoKern.h"
 #include "SkBitmapProcShader.h"
 #include "SkDrawProcs.h"
+#include "SkMatrixUtils.h"
 
 //#define TRACE_BITMAP_DRAWS
 
@@ -120,6 +122,23 @@
     memcpy(this, &src, sizeof(*this));
 }
 
+bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
+    if (fRC->isEmpty()) {
+        return false;
+    }
+
+    SkMatrix inverse;
+    if (!fMatrix->invert(&inverse)) {
+        return false;
+    }
+
+    SkIRect devBounds = fRC->getBounds();
+    // outset to have slop for antialasing and hairlines
+    devBounds.outset(1, 1);
+    inverse.mapRect(localBounds, SkRect::Make(devBounds));
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
@@ -666,7 +685,10 @@
                     path.moveTo(pts[0]);
                     path.lineTo(pts[1]);
 
-                    if (paint.getPathEffect()->asPoints(&pointData, path, rec, *fMatrix)) {
+                    SkRect cullRect = SkRect::Make(fRC->getBounds());
+
+                    if (paint.getPathEffect()->asPoints(&pointData, path, rec,
+                                                        *fMatrix, &cullRect)) {
                         // 'asPoints' managed to find some fast path
 
                         SkPaint newP(paint);
@@ -1055,7 +1077,12 @@
     }
 
     if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
-        doFill = paint->getFillPath(*pathPtr, &tmpPath);
+        SkRect cullRect;
+        const SkRect* cullRectPtr = NULL;
+        if (this->computeConservativeLocalClipBounds(&cullRect)) {
+            cullRectPtr = &cullRect;
+        }
+        doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr);
         pathPtr = &tmpPath;
     }
 
@@ -1113,6 +1140,7 @@
     go ahead and treat it as if it were, so that subsequent code can go fast.
  */
 static bool just_translate(const SkMatrix& matrix, const SkBitmap& bitmap) {
+#ifdef SK_IGNORE_TRANS_CLAMP_FIX
     SkMatrix::TypeMask mask = matrix.getType();
 
     if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
@@ -1129,6 +1157,11 @@
     }
     // if we got here, we're either kTranslate_Mask or identity
     return true;
+#else
+    unsigned bits = 0;  // TODO: find a way to allow the caller to tell us to
+                        // respect filtering.
+    return SkTreatAsSprite(matrix, bitmap.width(), bitmap.height(), bits);
+#endif
 }
 
 void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
@@ -1650,7 +1683,7 @@
 
     const SkMatrix* matrix = fMatrix;
 
-    SkAutoGlyphCache    autoCache(paint, matrix);
+    SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, matrix);
     SkGlyphCache*       cache = autoCache.getCache();
 
     // transform our starting point
@@ -1844,7 +1877,7 @@
     const SkMatrix* matrix = fMatrix;
 
     SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
-    SkAutoGlyphCache    autoCache(paint, matrix);
+    SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, matrix);
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkAAClipBlitterWrapper wrapper;
@@ -1875,13 +1908,9 @@
 
                 tmsProc(tms, pos);
 
-#ifdef SK_DRAW_POS_TEXT_IGNORE_SUBPIXEL_LEFT_ALIGN_FIX
-                SkFixed fx = SkScalarToFixed(tms.fLoc.fX);
-                SkFixed fy = SkScalarToFixed(tms.fLoc.fY);
-#else
                 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + (SK_FixedHalf >> SkGlyph::kSubBits);
                 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + (SK_FixedHalf >> SkGlyph::kSubBits);
-#endif
+
                 SkFixed fxMask = ~0;
                 SkFixed fyMask = ~0;
 
@@ -2137,7 +2166,7 @@
     // End copied from SkTextToPathIter constructor
 
     // detach cache
-    SkGlyphCache* cache = tempPaint.detachCache(NULL);
+    SkGlyphCache* cache = tempPaint.detachCache(NULL, NULL);
 
     // Must set scale, even if 1
     SkScalar scale = SK_Scalar1;
@@ -2328,6 +2357,7 @@
 
     virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTriColorShader)
 
 protected:
@@ -2400,6 +2430,16 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkTriColorShader::toString(SkString* str) const {
+    str->append("SkTriColorShader: (");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
 void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
                           const SkPoint vertices[], const SkPoint textures[],
                           const SkColor colors[], SkXfermode* xmode,
@@ -2459,7 +2499,7 @@
             SkASSERT(shader);
             bool releaseMode = false;
             if (NULL == xmode) {
-                xmode = SkXfermode::Create(SkXfermode::kMultiply_Mode);
+                xmode = SkXfermode::Create(SkXfermode::kModulate_Mode);
                 releaseMode = true;
             }
             SkShader* compose = SkNEW_ARGS(SkComposeShader,
diff --git a/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h
index ba1a875..ec086dd 100644
--- a/src/core/SkDrawProcs.h
+++ b/src/core/SkDrawProcs.h
@@ -43,4 +43,3 @@
 bool SkDrawTreatAsHairline(const SkPaint&, const SkMatrix&, SkScalar* coverage);
 
 #endif
-
diff --git a/src/core/SkEdge.cpp b/src/core/SkEdge.cpp
index e646c47..8904ca7 100644
--- a/src/core/SkEdge.cpp
+++ b/src/core/SkEdge.cpp
@@ -495,6 +495,3 @@
     fCurveCount = SkToS8(count);
     return success;
 }
-
-
-
diff --git a/src/core/SkEdge.h b/src/core/SkEdge.h
index c36ba24..a8ce2af 100644
--- a/src/core/SkEdge.h
+++ b/src/core/SkEdge.h
@@ -14,8 +14,7 @@
 #include "SkFDot6.h"
 #include "SkMath.h"
 
-//#ifdef SK_IGNORE_SETLINE_FIX
-#if 1
+#ifdef SK_IGNORE_SETLINE_FIX
     #define SkEdge_Compute_DY(top, y0)  ((32 - (y0)) & 63)
 #else
     // This is correct, as it favors the lower-pixel when y0 is on a 1/2 pixel
diff --git a/src/core/SkEdgeBuilder.cpp b/src/core/SkEdgeBuilder.cpp
index ad9eeb7..608a83c 100644
--- a/src/core/SkEdgeBuilder.cpp
+++ b/src/core/SkEdgeBuilder.cpp
@@ -238,4 +238,3 @@
     fEdgeList = fList.begin();
     return fList.count();
 }
-
diff --git a/src/core/SkEdgeClipper.cpp b/src/core/SkEdgeClipper.cpp
index 9fd6a0e..1f0e382 100644
--- a/src/core/SkEdgeClipper.cpp
+++ b/src/core/SkEdgeClipper.cpp
@@ -246,7 +246,11 @@
     SkScalar maxT = SK_Scalar1;
     SkScalar mid;
     int i;
+#ifdef SK_IGNORE_CLIP_BUG_FIX
     for (i = 0; i < 16; i++) {
+#else
+    for (i = 0; i < 24; i++) {
+#endif
         mid = SkScalarAve(minT, maxT);
         SkScalar delta = eval_cubic_coeff(A, B, C, D, mid);
         if (delta < 0) {
@@ -282,12 +286,11 @@
             SkPoint tmp[7];
             SkChopCubicAt(pts, tmp, t);
 
-            // tmp[3, 4, 5].fY should all be to the below clip.fTop, and
+            // tmp[3, 4].fY should all be to the below clip.fTop, and
             // still be monotonic in Y. Since we can't trust the numerics of
             // the chopper, we force those conditions now
             tmp[3].fY = clip.fTop;
             clamp_ge(tmp[4].fY, clip.fTop);
-            clamp_ge(tmp[5].fY, tmp[4].fY);
 
             pts[0] = tmp[3];
             pts[1] = tmp[4];
@@ -309,7 +312,6 @@
             SkChopCubicAt(pts, tmp, t);
             tmp[3].fY = clip.fBottom;
             clamp_le(tmp[2].fY, clip.fBottom);
-            clamp_le(tmp[1].fY, tmp[2].fY);
 
             pts[1] = tmp[1];
             pts[2] = tmp[2];
diff --git a/src/core/SkFDot6.h b/src/core/SkFDot6.h
index 9b8e4d0..a88ea92 100644
--- a/src/core/SkFDot6.h
+++ b/src/core/SkFDot6.h
@@ -58,4 +58,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkFilterProc.h b/src/core/SkFilterProc.h
index 4dfdf60..3b20d59 100644
--- a/src/core/SkFilterProc.h
+++ b/src/core/SkFilterProc.h
@@ -133,5 +133,3 @@
 }
 
 #endif
-
-
diff --git a/src/core/SkFilterShader.h b/src/core/SkFilterShader.h
index ded3125..be19640 100644
--- a/src/core/SkFilterShader.h
+++ b/src/core/SkFilterShader.h
@@ -24,6 +24,7 @@
     virtual void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
     virtual void shadeSpan16(int x, int y, uint16_t[], int count) SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkFilterShader)
 
 protected:
diff --git a/src/core/SkFlate.cpp b/src/core/SkFlate.cpp
index 39fc724..8258cdc 100644
--- a/src/core/SkFlate.cpp
+++ b/src/core/SkFlate.cpp
@@ -138,4 +138,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkFloatBits.cpp b/src/core/SkFloatBits.cpp
index 54a1d20..39b51ab 100644
--- a/src/core/SkFloatBits.cpp
+++ b/src/core/SkFloatBits.cpp
@@ -204,4 +204,3 @@
     data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
     return data.fFloat;
 }
-
diff --git a/src/ports/SkFontDescriptor.cpp b/src/core/SkFontDescriptor.cpp
similarity index 100%
rename from src/ports/SkFontDescriptor.cpp
rename to src/core/SkFontDescriptor.cpp
diff --git a/src/ports/SkFontDescriptor.h b/src/core/SkFontDescriptor.h
similarity index 100%
rename from src/ports/SkFontDescriptor.h
rename to src/core/SkFontDescriptor.h
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
index 137cd79..1ca40be 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -1267,7 +1267,7 @@
 //    Switch over to using cubics rather then quads
 //    Use a different method to create the mid-point (e.g., compute
 //             the two side points, average them, then move it out as needed
-#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
     #define SK_ScalarRoot2Over2_QuadCircle    0.7072f
 #else
     #define SK_ScalarRoot2Over2_QuadCircle    SK_ScalarRoot2Over2
@@ -1383,4 +1383,3 @@
     matrix.mapPoints(quadPoints, pointCount);
     return pointCount;
 }
-
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
index b878df8..2673aba 100644
--- a/src/core/SkGlyphCache.cpp
+++ b/src/core/SkGlyphCache.cpp
@@ -741,4 +741,3 @@
         SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes);
     }
 }
-
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
index 472bcde..537b447 100644
--- a/src/core/SkGlyphCache.h
+++ b/src/core/SkGlyphCache.h
@@ -18,6 +18,7 @@
 #include "SkTemplates.h"
 #include "SkTDArray.h"
 
+struct SkDeviceProperties;
 class SkPaint;
 
 class SkGlyphCache_Globals;
@@ -275,8 +276,10 @@
     SkAutoGlyphCache(const SkDescriptor* desc) {
         fCache = SkGlyphCache::DetachCache(desc);
     }
-    SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix) {
-        fCache = paint.detachCache(matrix);
+    SkAutoGlyphCache(const SkPaint& paint,
+                     const SkDeviceProperties* deviceProperties,
+                     const SkMatrix* matrix) {
+        fCache = paint.detachCache(deviceProperties, matrix);
     }
     ~SkAutoGlyphCache() {
         if (fCache) {
@@ -300,4 +303,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
index 53d0318..873d546 100644
--- a/src/core/SkGraphics.cpp
+++ b/src/core/SkGraphics.cpp
@@ -21,6 +21,7 @@
 #include "SkPixelRef.h"
 #include "SkRandom.h"
 #include "SkRefCnt.h"
+#include "SkRTConf.h"
 #include "SkScalerContext.h"
 #include "SkShader.h"
 #include "SkStream.h"
@@ -52,6 +53,13 @@
 #endif
 
 void SkGraphics::Init() {
+#ifdef SK_DEVELOPER
+    skRTConfRegistry().possiblyDumpFile();
+    skRTConfRegistry().validate();
+    SkDebugf("Non-default runtime configuration options:\n");
+    skRTConfRegistry().printNonDefault( );
+#endif
+
     SkFlattenable::InitializeFlattenables();
 #ifdef BUILD_EMBOSS_TABLE
     SkEmbossMask_BuildTable();
@@ -116,6 +124,7 @@
              GetFontCacheLimit() >> 10);
 
 #endif
+
 }
 
 void SkGraphics::Term() {
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 05e682c..616b8b9 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -107,8 +107,9 @@
     return false;
 }
 
-GrTexture* SkImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* texture, const SkRect& rect) {
-    return NULL;
+bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkASSERT(false);  // Should never be called, since canFilterImageGPU() returned false.
+    return false;
 }
 
 bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
@@ -117,7 +118,7 @@
     return true;
 }
 
-bool SkImageFilter::asNewEffect(GrEffect**, GrTexture*) const {
+bool SkImageFilter::asNewEffect(GrEffectRef**, GrTexture*) const {
     return false;
 }
 
diff --git a/src/core/SkInstCnt.cpp b/src/core/SkInstCnt.cpp
index d22639e..2f9a57d 100644
--- a/src/core/SkInstCnt.cpp
+++ b/src/core/SkInstCnt.cpp
@@ -7,6 +7,6 @@
 
 #include "SkInstCnt.h"
 
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
 bool gPrintInstCount = false;
 #endif
diff --git a/src/core/SkLineClipper.cpp b/src/core/SkLineClipper.cpp
index 7b7746f..fc4e3d2 100644
--- a/src/core/SkLineClipper.cpp
+++ b/src/core/SkLineClipper.cpp
@@ -168,6 +168,7 @@
 #endif
 
 #ifdef SK_SCALAR_IS_FLOAT
+#ifdef SK_DEBUG
 // This is an example of why we need to pin the result computed in
 // sect_with_horizontal. If we didn't explicitly pin, is_between_unsorted would
 // fail.
@@ -181,6 +182,7 @@
     SkASSERT(is_between_unsorted(x, pts[0].fX, pts[1].fX));
 }
 #endif
+#endif
 
 int SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip,
                             SkPoint lines[]) {
@@ -293,4 +295,3 @@
     }
     return lineCount;
 }
-
diff --git a/src/core/SkMMapStream.cpp b/src/core/SkMMapStream.cpp
index 1579044..7388e81 100644
--- a/src/core/SkMMapStream.cpp
+++ b/src/core/SkMMapStream.cpp
@@ -75,4 +75,3 @@
         fAddr = NULL;
     }
 }
-
diff --git a/src/core/SkMask.cpp b/src/core/SkMask.cpp
index c290b57..0974419 100644
--- a/src/core/SkMask.cpp
+++ b/src/core/SkMask.cpp
@@ -74,4 +74,3 @@
     addr += (x - fBounds.fLeft) << maskFormatToShift(fFormat);
     return addr;
 }
-
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index 5de76c7..9805bf9 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -277,5 +277,3 @@
         dst->set(srcM.fBounds);
     }
 }
-
-
diff --git a/src/core/SkMaskGamma.h b/src/core/SkMaskGamma.h
index ba3be64..fafe4ac 100644
--- a/src/core/SkMaskGamma.h
+++ b/src/core/SkMaskGamma.h
@@ -118,8 +118,8 @@
         }
     }
 
-    /** Given a color, returns the closest cannonical color. */
-    static SkColor CannonicalColor(SkColor color) {
+    /** Given a color, returns the closest canonical color. */
+    static SkColor CanonicalColor(SkColor color) {
         return SkColorSetRGB(
                    sk_t_scale255<R_LUM_BITS>(SkColorGetR(color) >> (8 - R_LUM_BITS)),
                    sk_t_scale255<G_LUM_BITS>(SkColorGetG(color) >> (8 - G_LUM_BITS)),
diff --git a/src/core/SkMath.cpp b/src/core/SkMath.cpp
index ccd96e9..0efedd7 100644
--- a/src/core/SkMath.cpp
+++ b/src/core/SkMath.cpp
@@ -15,6 +15,7 @@
 #ifdef SK_SCALAR_IS_FLOAT
     const uint32_t gIEEENotANumber = 0x7FFFFFFF;
     const uint32_t gIEEEInfinity = 0x7F800000;
+    const uint32_t gIEEENegativeInfinity = 0xFF800000;
 #endif
 
 #define sub_shift(zeros, x, n)  \
@@ -533,4 +534,3 @@
 SkFixed SkFixedATan2(SkFixed y, SkFixed x) { return SkCordicATan2(y, x); }
 SkFixed SkFixedExp(SkFixed x) { return SkCordicExp(x); }
 SkFixed SkFixedLog(SkFixed x) { return SkCordicLog(x); }
-
diff --git a/src/core/SkMathPriv.h b/src/core/SkMathPriv.h
index 11ad1ba..53cf430 100644
--- a/src/core/SkMathPriv.h
+++ b/src/core/SkMathPriv.h
@@ -87,4 +87,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 8dee0cb..31a8e37 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkMatrix.h"
 #include "Sk64.h"
 #include "SkFloatBits.h"
@@ -172,6 +170,43 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+bool SkMatrix::isSimilarity(SkScalar tol) const {
+    // if identity or translate matrix
+    TypeMask mask = this->getType();
+    if (mask <= kTranslate_Mask) {
+        return true;
+    }
+    if (mask & kPerspective_Mask) {
+        return false;
+    }
+
+    SkScalar mx = fMat[kMScaleX];
+    SkScalar my = fMat[kMScaleY];
+    // if no skew, can just compare scale factors
+    if (!(mask & kAffine_Mask)) {
+        return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
+    }
+    SkScalar sx = fMat[kMSkewX];
+    SkScalar sy = fMat[kMSkewY];
+
+    // degenerate matrix, non-similarity
+    if (SkScalarNearlyZero(mx) && SkScalarNearlyZero(my)
+        && SkScalarNearlyZero(sx) && SkScalarNearlyZero(sy)) {
+        return false;
+    }
+
+    // it has scales and skews, but it could also be rotation, check it out.
+    SkVector vec[2];
+    vec[0].set(mx, sx);
+    vec[1].set(sy, my);
+
+    return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
+           SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
+                SkScalarSquare(tol));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
     if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
         fMat[kMTransX] = dx;
@@ -850,7 +885,49 @@
 
 bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
     SkASSERT(!this->isIdentity());
-    int         isPersp = this->hasPerspective();
+
+    TypeMask mask = this->getType();
+
+#ifndef SK_IGNORE_FAST_SCALEMATRIX_INVERT
+    if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
+        bool invertible = true;
+        if (inv) {
+            if (mask & kScale_Mask) {
+                SkScalar invX = fMat[kMScaleX];
+                SkScalar invY = fMat[kMScaleY];
+                if (0 == invX || 0 == invY) {
+                    return false;
+                }
+                invX = SkScalarInvert(invX);
+                invY = SkScalarInvert(invY);
+
+                // Must be careful when writing to inv, since it may be the
+                // same memory as this.
+
+                inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
+                inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
+
+                inv->fMat[kMScaleX] = invX;
+                inv->fMat[kMScaleY] = invY;
+                inv->fMat[kMPersp2] = kMatrix22Elem;
+                inv->fMat[kMTransX] = -SkScalarMul(fMat[kMTransX], invX);
+                inv->fMat[kMTransY] = -SkScalarMul(fMat[kMTransY], invY);
+
+                inv->setTypeMask(mask | kRectStaysRect_Mask);
+            } else {
+                // translate only
+                inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
+            }
+        } else {    // inv is NULL, just check if we're invertible
+            if (!fMat[kMScaleX] || !fMat[kMScaleY]) {
+                invertible = false;
+            }
+        }
+        return invertible;
+    }
+#endif
+
+    int         isPersp = mask & kPerspective_Mask;
     int         shift;
     SkDetScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
 
@@ -1785,14 +1862,15 @@
     return 9 * sizeof(SkScalar);
 }
 
+#ifdef SK_DEVELOPER
 void SkMatrix::dump() const {
     SkString str;
-    this->toDumpString(&str);
+    this->toString(&str);
     SkDebugf("%s\n", str.c_str());
 }
 
-void SkMatrix::toDumpString(SkString* str) const {
-    str->printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
+void SkMatrix::toString(SkString* str) const {
+    str->appendf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
 #ifdef SK_SCALAR_IS_FLOAT
              fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
              fMat[6], fMat[7], fMat[8]);
@@ -1802,3 +1880,56 @@
     SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8]));
 #endif
 }
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkMatrixUtils.h"
+
+bool SkTreatAsSprite(const SkMatrix& mat, int width, int height,
+                     unsigned subpixelBits) {
+    // quick reject on affine or perspective
+    if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
+        return false;
+    }
+
+    // quick success check
+    if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
+        return true;
+    }
+
+    // mapRect supports negative scales, so we eliminate those first
+    if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
+        return false;
+    }
+
+    SkRect dst;
+    SkIRect isrc = { 0, 0, width, height };
+
+    {
+        SkRect src;
+        src.set(isrc);
+        mat.mapRect(&dst, src);
+    }
+
+    // just apply the translate to isrc
+    isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
+                SkScalarRoundToInt(mat.getTranslateY()));
+
+    if (subpixelBits) {
+        isrc.fLeft <<= subpixelBits;
+        isrc.fTop <<= subpixelBits;
+        isrc.fRight <<= subpixelBits;
+        isrc.fBottom <<= subpixelBits;
+
+        const float scale = 1 << subpixelBits;
+        dst.fLeft *= scale;
+        dst.fTop *= scale;
+        dst.fRight *= scale;
+        dst.fBottom *= scale;
+    }
+
+    SkIRect idst;
+    dst.round(&idst);
+    return isrc == idst;
+}
diff --git a/src/core/SkMatrixUtils.h b/src/core/SkMatrixUtils.h
new file mode 100644
index 0000000..2074267
--- /dev/null
+++ b/src/core/SkMatrixUtils.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMatrixUtils_DEFINED
+#define SkMatrixUtils_DEFINED
+
+#include "SkMatrix.h"
+
+/**
+ *  Number of subpixel bits used in skia's bilerp.
+ *  See SkBitmapProcState_procs.h and SkBitmapProcState_filter.h
+ */
+#define kSkSubPixelBitsForBilerp   4
+
+/**
+ *  Given a matrix and width/height, return true if the computed dst-rect would
+ *  align such that there is a 1-to-1 coorspondence between src and dst pixels.
+ *  This can be called by drawing code to see if drawBitmap can be turned into
+ *  drawSprite (which is faster).
+ *
+ *  The src-rect is defined to be { 0, 0, width, height }
+ *
+ *  The "closeness" test is based on the subpixelBits parameter. Pass 0 for
+ *  round-to-nearest behavior (e.g. nearest neighbor sampling). Pass the number
+ *  of subpixel-bits to simulate filtering.
+ */
+bool SkTreatAsSprite(const SkMatrix&, int width, int height,
+                     unsigned subpixelBits);
+
+/**
+ *  Calls SkTreatAsSprite() with default subpixelBits value to match Skia's
+ *  filter-bitmap implementation (i.e. kSkSubPixelBitsForBilerp).
+ */
+static inline bool SkTreatAsSpriteFilter(const SkMatrix& matrix,
+                                         int width, int height) {
+    return SkTreatAsSprite(matrix, width, height, kSkSubPixelBitsForBilerp);
+}
+
+#endif
diff --git a/src/core/SkMemory_stdlib.cpp b/src/core/SkMemory_stdlib.cpp
index 17bf6a9..fa13e9c 100644
--- a/src/core/SkMemory_stdlib.cpp
+++ b/src/core/SkMemory_stdlib.cpp
@@ -265,4 +265,3 @@
     ValidateHeap();
     return p;
 }
-
diff --git a/src/core/SkMetaData.cpp b/src/core/SkMetaData.cpp
index 338ec5e..5a494b3 100644
--- a/src/core/SkMetaData.cpp
+++ b/src/core/SkMetaData.cpp
@@ -334,4 +334,3 @@
 void SkMetaData::Rec::Free(Rec* rec) {
     sk_free(rec);
 }
-
diff --git a/src/core/SkOrderedReadBuffer.cpp b/src/core/SkOrderedReadBuffer.cpp
index 29c036e..daca74c 100644
--- a/src/core/SkOrderedReadBuffer.cpp
+++ b/src/core/SkOrderedReadBuffer.cpp
@@ -95,7 +95,7 @@
 }
 
 void* SkOrderedReadBuffer::readEncodedString(size_t* length, SkPaint::TextEncoding encoding) {
-    int32_t encodingType = fReader.readInt();
+    SkDEBUGCODE(int32_t encodingType = ) fReader.readInt();
     SkASSERT(encodingType == encoding);
     *length =  fReader.readInt();
     void* data = sk_malloc_throw(*length);
diff --git a/src/core/SkOrderedWriteBuffer.cpp b/src/core/SkOrderedWriteBuffer.cpp
index 7db0312..de26587 100644
--- a/src/core/SkOrderedWriteBuffer.cpp
+++ b/src/core/SkOrderedWriteBuffer.cpp
@@ -132,7 +132,12 @@
 }
 
 size_t SkOrderedWriteBuffer::writeStream(SkStream* stream, size_t length) {
-    return fWriter.readFromStream(stream, length);
+    fWriter.write32(length);
+    size_t bytesWritten = fWriter.readFromStream(stream, length);
+    if (bytesWritten < length) {
+        fWriter.reservePad(length - bytesWritten);
+    }
+    return bytesWritten;
 }
 
 bool SkOrderedWriteBuffer::writeToStream(SkWStream* stream) {
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 41c2f75..8acdd8d 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -9,6 +9,7 @@
 #include "SkPaint.h"
 #include "SkAnnotation.h"
 #include "SkColorFilter.h"
+#include "SkDeviceProperties.h"
 #include "SkFontHost.h"
 #include "SkImageFilter.h"
 #include "SkMaskFilter.h"
@@ -175,7 +176,7 @@
 
 #ifdef SK_BUILD_FOR_ANDROID
 unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache* cache = autoCache.getCache();
     return cache->getBaseGlyphCount(text);
 }
@@ -412,7 +413,7 @@
 #ifdef SK_BUILD_FOR_ANDROID
 const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text) {
     SkGlyphCache* cache;
-    descriptorProc(NULL, DetachDescProc, &cache, true);
+    descriptorProc(NULL, NULL, DetachDescProc, &cache, true);
 
     const SkGlyph& glyph = cache->getUnicharMetrics(text);
 
@@ -422,7 +423,7 @@
 
 const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId) {
     SkGlyphCache* cache;
-    descriptorProc(NULL, DetachDescProc, &cache, true);
+    descriptorProc(NULL, NULL, DetachDescProc, &cache, true);
 
     const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
 
@@ -433,7 +434,7 @@
 const void* SkPaint::findImage(const SkGlyph& glyph) {
     // See ::detachCache()
     SkGlyphCache* cache;
-    descriptorProc(NULL, DetachDescProc, &cache, true);
+    descriptorProc(NULL, NULL, DetachDescProc, &cache, true);
 
     const void* image = cache->findImage(glyph);
 
@@ -476,7 +477,7 @@
         return byteLength >> 1;
     }
 
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
 
     const char* text = (const char*)textData;
@@ -530,7 +531,7 @@
         return true;
     }
 
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
 
     switch (this->getTextEncoding()) {
@@ -580,7 +581,7 @@
     SkASSERT(glyphs != NULL);
     SkASSERT(textData != NULL);
 
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
 
     for (int index = 0; index < count; index++) {
@@ -1048,7 +1049,7 @@
         zoomPtr = &zoomMatrix;
     }
 
-    SkAutoGlyphCache    autoCache(*this, zoomPtr);
+    SkAutoGlyphCache    autoCache(*this, NULL, zoomPtr);
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkScalar width = 0;
@@ -1124,7 +1125,7 @@
         ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
     }
 
-    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkAutoGlyphCache    autoCache(*this, NULL, NULL);
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
@@ -1212,7 +1213,7 @@
         metrics = &storage;
     }
 
-    this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics, true);
+    this->descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
 
     if (scale) {
         metrics->fTop = SkScalarMul(metrics->fTop, scale);
@@ -1254,7 +1255,7 @@
         ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
     }
 
-    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkAutoGlyphCache    autoCache(*this, NULL, NULL);
     SkGlyphCache*       cache = autoCache.getCache();
     SkMeasureCacheProc  glyphCacheProc;
     glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
@@ -1487,22 +1488,10 @@
 #endif
 }
 
-//#define SK_GAMMA_SRGB
-#ifndef SK_GAMMA_CONTRAST
-    /**
-     * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
-     * With lower values small text appears washed out (though correctly so).
-     * With higher values lcd fringing is worse and the smoothing effect of
-     * partial coverage is diminished.
-     */
-    #define SK_GAMMA_CONTRAST (0.5f)
-#endif
-#ifndef SK_GAMMA_EXPONENT
-    #define SK_GAMMA_EXPONENT (2.2f)
-#endif
-
 void SkScalerContext::MakeRec(const SkPaint& paint,
-                              const SkMatrix* deviceMatrix, Rec* rec) {
+                              const SkDeviceProperties* deviceProperties,
+                              const SkMatrix* deviceMatrix,
+                              Rec* rec) {
     SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
 
     SkTypeface* typeface = paint.getTypeface();
@@ -1572,19 +1561,18 @@
 
     rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
 
-    if (SkMask::kLCD16_Format == rec->fMaskFormat ||
-        SkMask::kLCD32_Format == rec->fMaskFormat)
-    {
-        SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
-        SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
-        if (SkFontHost::kNONE_LCDOrder == order || tooBigForLCD(*rec)) {
+    SkDeviceProperties::Geometry geometry = deviceProperties
+                                          ? deviceProperties->fGeometry
+                                          : SkDeviceProperties::Geometry::MakeDefault();
+    if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
+        if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) {
             // eeek, can't support LCD
             rec->fMaskFormat = SkMask::kA8_Format;
         } else {
-            if (SkFontHost::kVertical_LCDOrientation == orient) {
+            if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) {
                 flags |= SkScalerContext::kLCD_Vertical_Flag;
             }
-            if (SkFontHost::kBGR_LCDOrder == order) {
+            if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) {
                 flags |= SkScalerContext::kLCD_BGROrder_Flag;
             }
         }
@@ -1611,14 +1599,32 @@
     rec->setHinting(computeHinting(paint));
 
     rec->setLuminanceColor(computeLuminanceColor(paint));
-#ifdef SK_GAMMA_SRGB
-    rec->setDeviceGamma(0);
-    rec->setPaintGamma(0);
+
+    if (NULL == deviceProperties) {
+        rec->setDeviceGamma(SK_GAMMA_EXPONENT);
+        rec->setPaintGamma(SK_GAMMA_EXPONENT);
+    } else {
+        rec->setDeviceGamma(deviceProperties->fGamma);
+
+        //For now always set the paint gamma equal to the device gamma.
+        //The math in SkMaskGamma can handle them being different,
+        //but it requires superluminous masks when
+        //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
+        rec->setPaintGamma(deviceProperties->fGamma);
+    }
+
+#ifdef SK_GAMMA_CONTRAST
+    rec->setContrast(SK_GAMMA_CONTRAST);
 #else
-    rec->setDeviceGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
-    rec->setPaintGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
+    /**
+     * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
+     * With lower values small text appears washed out (though correctly so).
+     * With higher values lcd fringing is worse and the smoothing effect of
+     * partial coverage is diminished.
+     */
+    rec->setContrast(SkFloatToScalar(0.5f));
 #endif
-    rec->setContrast(SkFloatToScalar(SK_GAMMA_CONTRAST));
+
     rec->fReservedAlign = 0;
 
     /*  Allow the fonthost to modify our rec before we use it as a key into the
@@ -1690,7 +1696,7 @@
         case SkMask::kLCD32_Format: {
             // filter down the luminance color to a finite number of bits
             SkColor color = rec->getLuminanceColor();
-            rec->setLuminanceColor(SkMaskGamma::CannonicalColor(color));
+            rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
             break;
         }
         case SkMask::kA8_Format: {
@@ -1707,12 +1713,12 @@
 
             // reduce to our finite number of bits
             color = SkColorSetRGB(lum, lum, lum);
-            rec->setLuminanceColor(SkMaskGamma::CannonicalColor(color));
+            rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
             break;
         }
         case SkMask::kBW_Format:
             // No need to differentiate gamma if we're BW
-            rec->setLuminanceColor(0);
+            rec->ignorePreBlend();
             break;
     }
 }
@@ -1730,12 +1736,13 @@
  *  would be for the fontcache lookup to know to ignore the luminance field
  *  entirely, but not sure how to do that and keep it fast.
  */
-void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
+void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
+                             const SkMatrix* deviceMatrix,
                              void (*proc)(const SkDescriptor*, void*),
                              void* context, bool ignoreGamma) const {
     SkScalerContext::Rec    rec;
 
-    SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
+    SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
     if (ignoreGamma) {
         rec.setLuminanceColor(0);
     }
@@ -1846,9 +1853,10 @@
     proc(desc, context);
 }
 
-SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
+SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
+                                   const SkMatrix* deviceMatrix) const {
     SkGlyphCache* cache;
-    this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
+    this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
     return cache;
 }
 
@@ -2137,13 +2145,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
+bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
+                          const SkRect* cullRect) const {
     SkStrokeRec rec(*this);
 
     const SkPath* srcPtr = &src;
     SkPath tmpPath;
 
-    if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec)) {
+    if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
         srcPtr = &tmpPath;
     }
 
@@ -2241,7 +2250,7 @@
         fPaint.setPathEffect(NULL);
     }
 
-    fCache = fPaint.detachCache(NULL);
+    fCache = fPaint.detachCache(NULL, NULL);
 
     SkPaint::Style  style = SkPaint::kFill_Style;
     SkPathEffect*   pe = NULL;
@@ -2374,4 +2383,3 @@
         }
     }
 }
-
diff --git a/src/core/SkPaintPriv.cpp b/src/core/SkPaintPriv.cpp
new file mode 100644
index 0000000..ce05389
--- /dev/null
+++ b/src/core/SkPaintPriv.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPaintPriv.h"
+
+#include "SkBitmap.h"
+#include "SkColorFilter.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+bool isPaintOpaque(const SkPaint* paint,
+                   const SkBitmap* bmpReplacesShader) {
+    // TODO: SkXfermode should have a virtual isOpaque method, which would
+    // make it possible to test modes that do not have a Coeff representation.
+
+    if (!paint) {
+        return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
+    }
+
+    SkXfermode::Coeff srcCoeff, dstCoeff;
+    if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
+        if (SkXfermode::kDA_Coeff == srcCoeff || SkXfermode::kDC_Coeff == srcCoeff ||
+            SkXfermode::kIDA_Coeff == srcCoeff || SkXfermode::kIDC_Coeff == srcCoeff) {
+            return false;
+        }
+        switch (dstCoeff) {
+        case SkXfermode::kZero_Coeff:
+            return true;
+        case SkXfermode::kISA_Coeff:
+            if (paint->getAlpha() != 255) {
+                break;
+            }
+            if (bmpReplacesShader) {
+                if (!bmpReplacesShader->isOpaque()) {
+                    break;
+                }
+            } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
+                break;
+            }
+            if (paint->getColorFilter() &&
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSA_Coeff:
+            if (paint->getAlpha() != 0) {
+                break;
+            }
+            if (paint->getColorFilter() &&
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSC_Coeff:
+            if (paint->getColor() != 0) { // all components must be 0
+                break;
+            }
+            if (bmpReplacesShader || paint->getShader()) {
+                break;
+            }
+            if (paint->getColorFilter() && (
+                (paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        default:
+            break;
+        }
+    }
+    return false;
+}
diff --git a/src/core/SkPaintPriv.h b/src/core/SkPaintPriv.h
new file mode 100644
index 0000000..38c9063
--- /dev/null
+++ b/src/core/SkPaintPriv.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPaintPriv_DEFINED
+#define SkPaintPriv_DEFINED
+
+class SkBitmap;
+class SkPaint;
+
+#include "SkTypes.h"
+/** Returns true if draw calls that use the paint will completely occlude
+    canvas contents that are covered by the draw.
+    @param paint The paint to be analyzed, NULL is equivalent to
+        the default paint.
+    @param bmpReplacesShader a bitmap to be used in place of the paint's
+        shader.
+    @return true if paint is opaque
+*/
+bool isPaintOpaque(const SkPaint* paint,
+                   const SkBitmap* bmpReplacesShader = NULL);
+#endif
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 89150a3..aa35e74 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -939,6 +939,96 @@
     SkDEBUGCODE(this->validate();)
 }
 
+static void add_corner_arc(SkPath* path, const SkRect& rect,
+                           SkScalar rx, SkScalar ry, int startAngle,
+                           SkPath::Direction dir, bool forceMoveTo) {
+    // These two asserts are not sufficient, since really we want to know
+    // that the pair of radii (e.g. left and right, or top and bottom) sum
+    // to <= dimension, but we don't have that data here, so we just have
+    // these conservative asserts.
+    SkASSERT(0 <= rx && rx <= rect.width());
+    SkASSERT(0 <= ry && ry <= rect.height());
+
+    SkRect   r;
+    r.set(-rx, -ry, rx, ry);
+
+    switch (startAngle) {
+        case   0:
+            r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
+            break;
+        case  90:
+            r.offset(rect.fLeft - r.fLeft,   rect.fBottom - r.fBottom);
+            break;
+        case 180: r.offset(rect.fLeft - r.fLeft,   rect.fTop - r.fTop); break;
+        case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
+        default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");
+    }
+
+    SkScalar start = SkIntToScalar(startAngle);
+    SkScalar sweep = SkIntToScalar(90);
+    if (SkPath::kCCW_Direction == dir) {
+        start += sweep;
+        sweep = -sweep;
+    }
+
+    path->arcTo(r, start, sweep, forceMoveTo);
+}
+
+void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
+                          Direction dir) {
+    SkRRect rrect;
+    rrect.setRectRadii(rect, (const SkVector*) radii);
+    this->addRRect(rrect, dir);
+}
+
+void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
+    assert_known_direction(dir);
+
+    if (rrect.isEmpty()) {
+        return;
+    }
+
+    const SkRect& bounds = rrect.getBounds();
+
+    if (rrect.isRect()) {
+        this->addRect(bounds, dir);
+    } else if (rrect.isOval()) {
+        this->addOval(bounds, dir);
+    } else if (rrect.isSimple()) {
+        const SkVector& rad = rrect.getSimpleRadii();
+        this->addRoundRect(bounds, rad.x(), rad.y(), dir);
+    } else {
+        SkAutoPathBoundsUpdate apbu(this, bounds);
+
+        if (kCW_Direction == dir) {
+            add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true);
+            add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,   0, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,  90, dir, false);
+        } else {
+            add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true);
+            add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,  90, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,   0, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false);
+        }
+        this->close();
+    }
+}
+
+bool SkPath::hasOnlyMoveTos() const {
+    int count = fPathRef->countVerbs();
+    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
+    for (int i = 0; i < count; ++i) {
+        if (*verbs == kLine_Verb ||
+            *verbs == kQuad_Verb ||
+            *verbs == kCubic_Verb) {
+            return false;
+        }
+        ++verbs;
+    }
+    return true;
+}
+
 #define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
 
 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
@@ -1032,96 +1122,6 @@
     this->close();
 }
 
-static void add_corner_arc(SkPath* path, const SkRect& rect,
-                           SkScalar rx, SkScalar ry, int startAngle,
-                           SkPath::Direction dir, bool forceMoveTo) {
-    // These two asserts are not sufficient, since really we want to know
-    // that the pair of radii (e.g. left and right, or top and bottom) sum
-    // to <= dimension, but we don't have that data here, so we just have
-    // these conservative asserts.
-    SkASSERT(0 <= rx && rx <= rect.width());
-    SkASSERT(0 <= ry && ry <= rect.height());
-
-    SkRect   r;
-    r.set(-rx, -ry, rx, ry);
-
-    switch (startAngle) {
-        case   0:
-            r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
-            break;
-        case  90:
-            r.offset(rect.fLeft - r.fLeft,   rect.fBottom - r.fBottom);
-            break;
-        case 180: r.offset(rect.fLeft - r.fLeft,   rect.fTop - r.fTop); break;
-        case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
-        default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");
-    }
-
-    SkScalar start = SkIntToScalar(startAngle);
-    SkScalar sweep = SkIntToScalar(90);
-    if (SkPath::kCCW_Direction == dir) {
-        start += sweep;
-        sweep = -sweep;
-    }
-
-    path->arcTo(r, start, sweep, forceMoveTo);
-}
-
-void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
-                          Direction dir) {
-    SkRRect rrect;
-    rrect.setRectRadii(rect, (const SkVector*) radii);
-    this->addRRect(rrect, dir);
-}
-
-void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
-    assert_known_direction(dir);
-
-    if (rrect.isEmpty()) {
-        return;
-    }
-
-    const SkRect& bounds = rrect.getBounds();
-
-    if (rrect.isRect()) {
-        this->addRect(bounds, dir);
-    } else if (rrect.isOval()) {
-        this->addOval(bounds, dir);
-    } else if (rrect.isSimple()) {
-        const SkVector& rad = rrect.getSimpleRadii();
-        this->addRoundRect(bounds, rad.x(), rad.y(), dir);
-    } else {
-        SkAutoPathBoundsUpdate apbu(this, bounds);
-
-        if (kCW_Direction == dir) {
-            add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true);
-            add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false);
-            add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,   0, dir, false);
-            add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,  90, dir, false);
-        } else {
-            add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true);
-            add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,  90, dir, false);
-            add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,   0, dir, false);
-            add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false);
-        }
-        this->close();
-    }
-}
-
-bool SkPath::hasOnlyMoveTos() const {
-    int count = fPathRef->countVerbs();
-    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
-    for (int i = 0; i < count; ++i) {
-        if (*verbs == kLine_Verb ||
-            *verbs == kQuad_Verb ||
-            *verbs == kCubic_Verb) {
-            return false;
-        }
-        ++verbs;
-    }
-    return true;
-}
-
 void SkPath::addOval(const SkRect& oval, Direction dir) {
     assert_known_direction(dir);
 
@@ -1147,24 +1147,7 @@
     SkScalar    cy = oval.centerY();
     SkScalar    rx = SkScalarHalf(oval.width());
     SkScalar    ry = SkScalarHalf(oval.height());
-#if 0   // these seem faster than using quads (1/2 the number of edges)
-    SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
-    SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
 
-    this->incReserve(13);
-    this->moveTo(cx + rx, cy);
-    if (dir == kCCW_Direction) {
-        this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
-        this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
-        this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
-        this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
-    } else {
-        this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
-        this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
-        this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
-        this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
-    }
-#else
     SkScalar    sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
     SkScalar    sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
     SkScalar    mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
@@ -1202,7 +1185,6 @@
         this->quadTo(cx + sx,       T, cx + mx, cy - my);
         this->quadTo(      R, cy - sy,       R, cy     );
     }
-#endif
     this->close();
 }
 
@@ -2124,6 +2106,34 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#include "SkString.h"
+
+static void append_scalar(SkString* str, SkScalar value) {
+    SkString tmp;
+    tmp.printf("%g", value);
+    if (tmp.contains('.')) {
+        tmp.appendUnichar('f');
+    }
+    str->append(tmp);
+}
+
+static void append_params(SkString* str, const char label[], const SkPoint pts[],
+                          int count) {
+    str->append(label);
+    str->append("(");
+
+    const SkScalar* values = &pts[0].fX;
+    count *= 2;
+
+    for (int i = 0; i < count; ++i) {
+        append_scalar(str, values[i]);
+        if (i < count - 1) {
+            str->append(", ");
+        }
+    }
+    str->append(");\n");
+}
+
 void SkPath::dump(bool forceClose, const char title[]) const {
     Iter    iter(*this, forceClose);
     SkPoint pts[4];
@@ -2132,29 +2142,25 @@
     SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
              title ? title : "");
 
+    SkString builder;
+
     while ((verb = iter.next(pts, false)) != kDone_Verb) {
         switch (verb) {
             case kMove_Verb:
-                SkDebugf("  path: moveTo [%g %g]\n",
-                        SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
+                append_params(&builder, "path.moveTo", &pts[0], 1);
                 break;
             case kLine_Verb:
-                SkDebugf("  path: lineTo [%g %g]\n",
-                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
+                append_params(&builder, "path.lineTo", &pts[1], 1);
+                append_params(&builder, "path.lineTo", &pts[1], 1);
                 break;
             case kQuad_Verb:
-                SkDebugf("  path: quadTo [%g %g] [%g %g]\n",
-                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
-                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
+                append_params(&builder, "path.quadTo", &pts[1], 2);
                 break;
             case kCubic_Verb:
-                SkDebugf("  path: cubeTo [%g %g] [%g %g] [%g %g]\n",
-                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
-                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
-                        SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
+                append_params(&builder, "path.cubicTo", &pts[1], 3);
                 break;
             case kClose_Verb:
-                SkDebugf("  path: close\n");
+                builder.append("path.close();\n");
                 break;
             default:
                 SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
@@ -2162,7 +2168,7 @@
                 break;
         }
     }
-    SkDebugf("path: done %s\n", title ? title : "");
+    SkDebugf("%s\n", builder.c_str());
 }
 
 void SkPath::dump() const {
@@ -2946,4 +2952,3 @@
     }
     return SkToBool(w);
 }
-
diff --git a/src/core/SkPathEffect.cpp b/src/core/SkPathEffect.cpp
index ebbfd6d..8306d7a 100644
--- a/src/core/SkPathEffect.cpp
+++ b/src/core/SkPathEffect.cpp
@@ -19,7 +19,7 @@
 }
 
 bool SkPathEffect::asPoints(PointData* results, const SkPath& src,
-                            const SkStrokeRec&, const SkMatrix&) const {
+                    const SkStrokeRec&, const SkMatrix&, const SkRect*) const {
     return false;
 }
 
@@ -56,7 +56,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkComposePathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                     SkStrokeRec* rec) const {
+                             SkStrokeRec* rec, const SkRect* cullRect) const {
     // we may have failed to unflatten these, so we have to check
     if (!fPE0 || !fPE1) {
         return false;
@@ -65,16 +65,17 @@
     SkPath          tmp;
     const SkPath*   ptr = &src;
 
-    if (fPE1->filterPath(&tmp, src, rec)) {
+    if (fPE1->filterPath(&tmp, src, rec, cullRect)) {
         ptr = &tmp;
     }
-    return fPE0->filterPath(dst, *ptr, rec);
+    return fPE0->filterPath(dst, *ptr, rec, cullRect);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                 SkStrokeRec* rec) const {
+                             SkStrokeRec* rec, const SkRect* cullRect) const {
     // use bit-or so that we always call both, even if the first one succeeds
-    return fPE0->filterPath(dst, src, rec) | fPE1->filterPath(dst, src, rec);
+    return fPE0->filterPath(dst, src, rec, cullRect) |
+           fPE1->filterPath(dst, src, rec, cullRect);
 }
diff --git a/src/core/SkPathHeap.cpp b/src/core/SkPathHeap.cpp
index bd5269d..b8ca40b 100644
--- a/src/core/SkPathHeap.cpp
+++ b/src/core/SkPathHeap.cpp
@@ -61,4 +61,3 @@
         iter++;
     }
 }
-
diff --git a/src/core/SkPathHeap.h b/src/core/SkPathHeap.h
index f043222..095e84a 100644
--- a/src/core/SkPathHeap.h
+++ b/src/core/SkPathHeap.h
@@ -48,4 +48,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index b9273df..3e464e0 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -321,11 +321,11 @@
     }
 }
 
+#ifdef SK_BUILD_FOR_ANDROID
 void SkPicture::abortPlayback() {
     if (NULL == fPlayback) {
         return;
     }
     fPlayback->abort();
 }
-
-
+#endif
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index e104a40..9221c1e 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -120,7 +120,7 @@
     SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize);
 
     result->fIndex = index;
-    result->fTopBot[0] = result->fTopBot[1] = 0; // initial to sentinel values
+    result->setTopBotUnwritten();
     result->fFlatSize = size;
 
     // put the serialized contents into the data section of the new allocation
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index 9594a59..6354e9f 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -333,20 +333,20 @@
     }
 
     // returns true if fTopBot[] has been recorded
-    bool isTopBotValid() const {
-        return fTopBot[0] < fTopBot[1];
+    bool isTopBotWritten() const {
+        return !SkScalarIsNaN(fTopBot[0]);
     }
 
     // Returns fTopBot array, so it can be passed to a routine to compute them.
     // For efficiency, we assert that fTopBot have not been recorded yet.
-    SkScalar* writableTopBot() {
-        SkASSERT(!this->isTopBotValid());
+    SkScalar* writableTopBot() const {
+        SkASSERT(!this->isTopBotWritten());
         return fTopBot;
     }
 
     // return the topbot[] after it has been recorded
     const SkScalar* topBot() const {
-        SkASSERT(this->isTopBotValid());
+        SkASSERT(this->isTopBotWritten());
         return fTopBot;
     }
 
@@ -355,10 +355,15 @@
     int fIndex;
 
     // Cache of paint's FontMetrics fTop,fBottom
-    // initialied to [0,0] as a sentinel that they have not been recorded yet
+    // initialied to [NaN,NaN] as a sentinel that they have not been recorded yet
     //
     // This is *not* part of the key for search/sort
-    SkScalar fTopBot[2];
+    mutable SkScalar fTopBot[2];
+
+    // marks fTopBot[] as unrecorded
+    void setTopBotUnwritten() {
+        this->fTopBot[0] = this->fTopBot[1] = SK_ScalarNaN; // initial to sentinel values
+    }
 
     // From here down is the data we look at in the search/sort. We always begin
     // with the checksum and then length.
@@ -489,29 +494,6 @@
         return array;
     }
 
-protected:
-    void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*);
-    void (*fUnflattenProc)(SkOrderedReadBuffer&, void*);
-
-private:
-    void unflattenIntoArray(T* array) const {
-        const int count = fData.count();
-        const SkFlatData** iter = fData.begin();
-        for (int i = 0; i < count; ++i) {
-            const SkFlatData* element = iter[i];
-            int index = element->index() - 1;
-            SkASSERT((unsigned)index < (unsigned)count);
-            element->unflatten(&array[index], fUnflattenProc,
-                               fController->getBitmapHeap(),
-                               fController->getTypefacePlayback());
-        }
-    }
-
-
-    SkFlatController * const     fController;
-    int                          fNextIndex;
-    SkTDArray<const SkFlatData*> fData;
-
     const SkFlatData* findAndReturnFlat(const T& element) {
         SkFlatData* flat = SkFlatData::Create(fController, &element, fNextIndex, fFlattenProc);
 
@@ -540,6 +522,27 @@
         return flat;
     }
 
+protected:
+    void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*);
+    void (*fUnflattenProc)(SkOrderedReadBuffer&, void*);
+
+private:
+    void unflattenIntoArray(T* array) const {
+        const int count = fData.count();
+        const SkFlatData** iter = fData.begin();
+        for (int i = 0; i < count; ++i) {
+            const SkFlatData* element = iter[i];
+            int index = element->index() - 1;
+            SkASSERT((unsigned)index < (unsigned)count);
+            element->unflatten(&array[index], fUnflattenProc,
+                               fController->getBitmapHeap(),
+                               fController->getTypefacePlayback());
+        }
+    }
+
+    SkFlatController * const     fController;
+    int                          fNextIndex;
+    SkTDArray<const SkFlatData*> fData;
 
     enum {
         // Determined by trying diff values on picture-recording benchmarks
@@ -649,10 +652,6 @@
         fFlattenProc = &SkFlattenObjectProc<SkPaint>;
         fUnflattenProc = &SkUnflattenObjectProc<SkPaint>;
     }
-
-    SkFlatData* writableFlatData(int index) {
-        return const_cast<SkFlatData*>((*this)[index]);
-    }
 };
 
 class SkRegionDictionary : public SkFlatDictionary<SkRegion> {
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index e7f5ce8..3ca26d4 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -181,6 +181,7 @@
     SkSafeRef(fStateTree);
 
     if (deepCopyInfo) {
+        int paintCount = SafeCount(src.fPaints);
 
         if (src.fBitmaps) {
             fBitmaps = SkTRefArray<SkBitmap>::Create(src.fBitmaps->begin(), src.fBitmaps->count());
@@ -192,7 +193,7 @@
              * this point we would need to pass the SkBitmapHeap so that we don't unnecessarily
              * flatten the pixels in a bitmap shader.
              */
-            deepCopyInfo->paintData.setCount(src.fPaints->count());
+            deepCopyInfo->paintData.setCount(paintCount);
 
             /* Use an SkBitmapHeap to avoid flattening bitmaps in shaders. If there already is one,
              * use it. If this SkPicturePlayback was created from a stream, fBitmapHeap will be
@@ -209,7 +210,7 @@
             }
 
             SkDEBUGCODE(int heapSize = SafeCount(fBitmapHeap.get());)
-            for (int i = 0; i < src.fPaints->count(); i++) {
+            for (int i = 0; i < paintCount; i++) {
                 if (needs_deep_copy(src.fPaints->at(i))) {
                     deepCopyInfo->paintData[i] = SkFlatData::Create(&deepCopyInfo->controller,
                                                                     &src.fPaints->at(i), 0,
@@ -226,11 +227,11 @@
             deepCopyInfo->initialized = true;
         }
 
-        fPaints = SkTRefArray<SkPaint>::Create(src.fPaints->count());
-        SkASSERT(deepCopyInfo->paintData.count() == src.fPaints->count());
+        fPaints = SkTRefArray<SkPaint>::Create(paintCount);
+        SkASSERT(deepCopyInfo->paintData.count() == paintCount);
         SkBitmapHeap* bmHeap = deepCopyInfo->controller.getBitmapHeap();
         SkTypefacePlayback* tfPlayback = deepCopyInfo->controller.getTypefacePlayback();
-        for (int i = 0; i < src.fPaints->count(); i++) {
+        for (int i = 0; i < paintCount; i++) {
             if (deepCopyInfo->paintData[i]) {
                 deepCopyInfo->paintData[i]->unflatten(&fPaints->writableAt(i),
                                                       &SkUnflattenObjectProc<SkPaint>,
@@ -619,7 +620,7 @@
 };
 #endif
 
-#ifdef SK_PICTURE_PROFILING_STUBS
+#ifdef SK_DEVELOPER
 size_t SkPicturePlayback::preDraw(size_t offset, int type) {
     return 0;
 }
@@ -679,12 +680,22 @@
     // Record this, so we can concat w/ it if we encounter a setMatrix()
     SkMatrix initialMatrix = canvas.getTotalMatrix();
 
+#ifdef SK_BUILD_FOR_ANDROID
+    fAbortCurrentPlayback = false;
+#endif
+
     while (!reader.eof()) {
-#ifdef SK_PICTURE_PROFILING_STUBS
+#ifdef SK_BUILD_FOR_ANDROID
+        if (fAbortCurrentPlayback) {
+            return;
+        }
+#endif
+
+#ifdef SK_DEVELOPER
         size_t curOffset = reader.offset();
 #endif
         int type = reader.readInt();
-#ifdef SK_PICTURE_PROFILING_STUBS
+#ifdef SK_DEVELOPER
         size_t skipTo = this->preDraw(curOffset, type);
         if (0 != skipTo) {
             if (kDrawComplete == skipTo) {
@@ -962,7 +973,7 @@
                 SkASSERT(0);
         }
 
-#ifdef SK_PICTURE_PROFILING_STUBS
+#ifdef SK_DEVELOPER
         this->postDraw(curOffset);
 #endif
 
@@ -986,11 +997,6 @@
 //    this->dumpSize();
 }
 
-void SkPicturePlayback::abort() {
-    SkASSERT(!"not supported");
-//    fReader.skip(fReader.size() - fReader.offset());
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifdef SK_DEBUG_SIZE
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
index b04ba07..cd3a6a5 100644
--- a/src/core/SkPicturePlayback.h
+++ b/src/core/SkPicturePlayback.h
@@ -74,12 +74,14 @@
 
     void dumpSize() const;
 
+#ifdef SK_BUILD_FOR_ANDROID
     // Can be called in the middle of playback (the draw() call). WIll abort the
     // drawing and return from draw() after the "current" op code is done
-    void abort();
+    void abort() { fAbortCurrentPlayback = true; }
+#endif
 
 protected:
-#ifdef SK_PICTURE_PROFILING_STUBS
+#ifdef SK_DEVELOPER
     virtual size_t preDraw(size_t offset, int type);
     virtual void postDraw(size_t offset);
 #endif
@@ -219,6 +221,7 @@
     SkFactoryPlayback* fFactoryPlayback;
 #ifdef SK_BUILD_FOR_ANDROID
     SkMutex fDrawMutex;
+    bool fAbortCurrentPlayback;
 #endif
 };
 
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 4010387..6fea77b 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -535,15 +535,14 @@
     topbot[1] = bounds.fBottom;
 }
 
-void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, int index,
+void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
                                               SkScalar minY, SkScalar maxY) {
-    SkFlatData* flat = fPaints.writableFlatData(index);
-    if (!flat->isTopBotValid()) {
-        computeFontMetricsTopBottom(paint, flat->writableTopBot());
-        SkASSERT(flat->isTopBotValid());
+    if (!flat.isTopBotWritten()) {
+        computeFontMetricsTopBottom(paint, flat.writableTopBot());
+        SkASSERT(flat.isTopBotWritten());
     }
-    addScalar(flat->topBot()[0] + minY);
-    addScalar(flat->topBot()[1] + maxY);
+    addScalar(flat.topBot()[0] + minY);
+    addScalar(flat.topBot()[1] + maxY);
 }
 
 void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
@@ -551,12 +550,13 @@
     bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
 
     addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
-    int paintIndex = addPaint(paint);
+    const SkFlatData* flatPaintData = addPaint(paint);
+    SkASSERT(flatPaintData);
     addText(text, byteLength);
     addScalar(x);
     addScalar(y);
     if (fast) {
-        addFontMetricsTopBottom(paint, paintIndex - 1, y, y);
+        addFontMetricsTopBottom(paint, *flatPaintData, y, y);
     }
     validate();
 }
@@ -597,7 +597,8 @@
     } else {
         addDraw(DRAW_POS_TEXT);
     }
-    int paintIndex = addPaint(paint);
+    const SkFlatData* flatPaintData = addPaint(paint);
+    SkASSERT(flatPaintData);
     addText(text, byteLength);
     addInt(points);
 
@@ -606,7 +607,7 @@
 #endif
     if (canUseDrawH) {
         if (fast) {
-            addFontMetricsTopBottom(paint, paintIndex - 1, pos[0].fY, pos[0].fY);
+            addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
         }
         addScalar(pos[0].fY);
         SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
@@ -616,7 +617,7 @@
     else {
         fWriter.writeMul4(pos, points * sizeof(SkPoint));
         if (fastBounds) {
-            addFontMetricsTopBottom(paint, paintIndex - 1, minY, maxY);
+            addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
         }
     }
 #ifdef SK_DEBUG_SIZE
@@ -636,7 +637,8 @@
     bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
 
     addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
-    int paintIndex = this->addPaint(paint);
+    const SkFlatData* flatPaintData = addPaint(paint);
+    SkASSERT(flatPaintData);
     addText(text, byteLength);
     addInt(points);
 
@@ -644,7 +646,7 @@
     size_t start = fWriter.size();
 #endif
     if (fast) {
-        addFontMetricsTopBottom(paint, paintIndex - 1, constY, constY);
+        addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
     }
     addScalar(constY);
     fWriter.writeMul4(xpos, points * sizeof(SkScalar));
@@ -731,10 +733,11 @@
     this->addInt(matrix ? fMatrices.find(*matrix) : 0);
 }
 
-int SkPictureRecord::addPaintPtr(const SkPaint* paint) {
-    int index = paint ? fPaints.find(*paint) : 0;
+const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
+    const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
+    int index = data ? data->index() : 0;
     this->addInt(index);
-    return index;
+    return data;
 }
 
 void SkPictureRecord::addPath(const SkPath& path) {
@@ -950,4 +953,3 @@
     }
 }
 #endif
-
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index abaa22d..84fbe85 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -75,7 +75,7 @@
     virtual void drawData(const void*, size_t) SK_OVERRIDE;
     virtual bool isDrawingToLayer() const SK_OVERRIDE;
 
-    void addFontMetricsTopBottom(const SkPaint& paint, int paintIndex,
+    void addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData&,
                                  SkScalar minY, SkScalar maxY);
 
     const SkTDArray<SkPicture* >& getPictureRefs() const {
@@ -122,8 +122,8 @@
     void addBitmap(const SkBitmap& bitmap);
     void addMatrix(const SkMatrix& matrix);
     void addMatrixPtr(const SkMatrix* matrix);
-    int  addPaint(const SkPaint& paint) { return this->addPaintPtr(&paint); }
-    int  addPaintPtr(const SkPaint* paint);
+    const SkFlatData* addPaint(const SkPaint& paint) { return this->addPaintPtr(&paint); }
+    const SkFlatData* addPaintPtr(const SkPaint* paint);
     void addPath(const SkPath& path);
     void addPicture(SkPicture& picture);
     void addPoint(const SkPoint& point);
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
index 9299f8c..2abed19 100644
--- a/src/core/SkPictureStateTree.cpp
+++ b/src/core/SkPictureStateTree.cpp
@@ -175,4 +175,3 @@
     ++fPlaybackIndex;
     return draw->fOffset;
 }
-
diff --git a/src/core/SkPictureStateTree.h b/src/core/SkPictureStateTree.h
index bcf2aee..798e552 100644
--- a/src/core/SkPictureStateTree.h
+++ b/src/core/SkPictureStateTree.h
@@ -36,7 +36,7 @@
         SkMatrix* fMatrix;
         Node* fNode;
         uint32_t fOffset;
-        bool operator<(const Draw& other) { return fOffset < other.fOffset; }
+        bool operator<(const Draw& other) const { return fOffset < other.fOffset; }
     };
 
     class Iterator;
@@ -133,4 +133,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkPtrRecorder.cpp b/src/core/SkPtrRecorder.cpp
index 896e62d..d473cab 100644
--- a/src/core/SkPtrRecorder.cpp
+++ b/src/core/SkPtrRecorder.cpp
@@ -75,5 +75,3 @@
         }
     }
 }
-
-
diff --git a/src/core/SkQuadClipper.cpp b/src/core/SkQuadClipper.cpp
index 9e04887..a67a23f 100644
--- a/src/core/SkQuadClipper.cpp
+++ b/src/core/SkQuadClipper.cpp
@@ -126,4 +126,3 @@
     }
     return true;
 }
-
diff --git a/src/core/SkRRect.cpp b/src/core/SkRRect.cpp
index 0d137ec..fc1a1cf 100644
--- a/src/core/SkRRect.cpp
+++ b/src/core/SkRRect.cpp
@@ -36,9 +36,7 @@
     fType = kSimple_Type;
     if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
         fType = kOval_Type;
-        // TODO: try asserting they are already W/2 & H/2 already
-        xRad = SkScalarHalf(fRect.width());
-        yRad = SkScalarHalf(fRect.height());
+        // TODO: assert that all the x&y radii are already W/2 & H/2
     }
 
     SkDEBUGCODE(this->validate();)
@@ -228,10 +226,10 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-#if 0
-void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
 
+void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
     SkRect r = fRect;
+
     r.inset(dx, dy);
     if (r.isEmpty()) {
         dst->setEmpty();
@@ -239,12 +237,18 @@
     }
 
     SkVector radii[4];
+    memcpy(radii, fRadii, sizeof(radii));
     for (int i = 0; i < 4; ++i) {
-        radii[i].set(fRadii[i].fX - dx, fRadii[i].fY - dy);
+        if (radii[i].fX) {
+            radii[i].fX -= dx;
+        }
+        if (radii[i].fY) {
+            radii[i].fY -= dy;
+        }
     }
     dst->setRectRadii(r, radii);
 }
-#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 uint32_t SkRRect::writeToMemory(void* buffer) const {
diff --git a/src/core/SkRTree.cpp b/src/core/SkRTree.cpp
index 42c1c99..88fc079 100644
--- a/src/core/SkRTree.cpp
+++ b/src/core/SkRTree.cpp
@@ -261,8 +261,7 @@
 
         // Evaluate each sort
         for (int j = 0; j < 2; ++j) {
-
-            SkQSort(sorts[i][j], children, children + fMaxChildren, &RectLessThan);
+            SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[i][j]));
 
             // Evaluate each split index
             for (int32_t k = 1; k <= fMaxChildren - 2 * fMinChildren + 2; ++k) {
@@ -299,7 +298,7 @@
     // replicate the sort of the winning distribution, (we can skip this if the last
     // sort ended up being best)
     if (!(axis == 1 && sortSide == 1)) {
-        SkQSort(sorts[axis][sortSide], children, children + fMaxChildren, &RectLessThan);
+        SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[axis][sortSide]));
     }
 
     return fMinChildren - 1 + k;
@@ -325,7 +324,7 @@
         return out;
     } else {
         // First we sort the whole list by y coordinates
-        SkQSort<int, Branch>(level, branches->begin(), branches->end() - 1, &RectLessY);
+        SkTQSort(branches->begin(), branches->end() - 1, RectLessY());
 
         int numBranches = branches->count() / fMaxChildren;
         int remainder = branches->count() % fMaxChildren;
@@ -357,8 +356,7 @@
             }
 
             // Now we sort horizontal strips of rectangles by their x coords
-            SkQSort<int, Branch>(level, branches->begin() + begin, branches->begin() + end - 1,
-                                 &RectLessX);
+            SkTQSort(branches->begin() + begin, branches->begin() + end - 1, RectLessX());
 
             for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) {
                 int incrementBy = fMaxChildren;
@@ -473,4 +471,3 @@
     if (joinWith.fRight > out->fRight) { out->fRight = joinWith.fRight; }
     if (joinWith.fBottom > out->fBottom) { out->fBottom = joinWith.fBottom; }
 }
-
diff --git a/src/core/SkRTree.h b/src/core/SkRTree.h
index 1f16648..0a53667 100644
--- a/src/core/SkRTree.h
+++ b/src/core/SkRTree.h
@@ -119,19 +119,28 @@
     typedef int32_t SkIRect::*SortSide;
 
     // Helper for sorting our children arrays by sides of their rects
-    static bool RectLessThan(SortSide const& side, const Branch lhs, const Branch rhs) {
-        return lhs.fBounds.*side < rhs.fBounds.*side;
-    }
+    struct RectLessThan {
+        RectLessThan(SkRTree::SortSide side) : fSide(side) { }
+        bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) const {
+            return lhs.fBounds.*fSide < rhs.fBounds.*fSide;
+        }
+    private:
+        const SkRTree::SortSide fSide;
+    };
 
-    static bool RectLessX(int&, const Branch lhs, const Branch rhs) {
-        return ((lhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1) <
-               ((rhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1);
-    }
+    struct RectLessX {
+        bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) {
+            return ((lhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1) <
+                   ((rhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1);
+        }
+    };
 
-    static bool RectLessY(int&, const Branch lhs, const Branch rhs) {
-        return ((lhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1) <
-               ((rhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1);
-    }
+    struct RectLessY {
+        bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) {
+            return ((lhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1) <
+                   ((rhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1);
+        }
+    };
 
     SkRTree(int minChildren, int maxChildren, SkScalar aspectRatio);
 
@@ -180,4 +189,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkRasterClip.cpp b/src/core/SkRasterClip.cpp
index 4312717..919fde9 100644
--- a/src/core/SkRasterClip.cpp
+++ b/src/core/SkRasterClip.cpp
@@ -291,4 +291,3 @@
         fBlitter = &fAABlitter;
     }
 }
-
diff --git a/src/core/SkRasterizer.cpp b/src/core/SkRasterizer.cpp
index 23a749f..a65d541 100644
--- a/src/core/SkRasterizer.cpp
+++ b/src/core/SkRasterizer.cpp
@@ -48,4 +48,3 @@
     return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode,
                               SkPaint::kFill_Style);
 }
-
diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp
index 1d8a13e..c62f2f3 100644
--- a/src/core/SkRect.cpp
+++ b/src/core/SkRect.cpp
@@ -181,4 +181,3 @@
         if (bottom > fBottom) fBottom = bottom;
     }
 }
-
diff --git a/src/core/SkRefCnt.cpp b/src/core/SkRefCnt.cpp
index 773e0d9..8d09065 100644
--- a/src/core/SkRefCnt.cpp
+++ b/src/core/SkRefCnt.cpp
@@ -11,4 +11,3 @@
 
 SK_DEFINE_INST_COUNT(SkRefCnt)
 SK_DEFINE_INST_COUNT(SkWeakRefCnt)
-
diff --git a/src/core/SkRefDict.cpp b/src/core/SkRefDict.cpp
index 53b099b..50a469d 100644
--- a/src/core/SkRefDict.cpp
+++ b/src/core/SkRefDict.cpp
@@ -86,4 +86,3 @@
     }
     fImpl = NULL;
 }
-
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
index 776f3e8..936c87e 100644
--- a/src/core/SkRegion.cpp
+++ b/src/core/SkRegion.cpp
@@ -89,7 +89,7 @@
 }
 
 void SkRegion::freeRuns() {
-    if (fRunHead->isComplex()) {
+    if (this->isComplex()) {
         SkASSERT(fRunHead->fRefCnt >= 1);
         if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) {
             //SkASSERT(gRgnAllocCounter > 0);
@@ -152,7 +152,7 @@
 
         fBounds = src.fBounds;
         fRunHead = src.fRunHead;
-        if (fRunHead->isComplex()) {
+        if (this->isComplex()) {
             sk_atomic_inc(&fRunHead->fRefCnt);
         }
     }
@@ -277,7 +277,7 @@
 
     //  if we get here, we need to become a complex region
 
-    if (!fRunHead->isComplex() || fRunHead->fRunCount != count) {
+    if (!this->isComplex() || fRunHead->fRunCount != count) {
         this->freeRuns();
         this->allocateRuns(count);
     }
@@ -518,7 +518,7 @@
         return true;
     }
     // now we insist that both are complex (but different ptrs)
-    if (!ah->isComplex() || !bh->isComplex()) {
+    if (!this->isComplex() || !b.isComplex()) {
         return false;
     }
     return  ah->fRunCount == bh->fRunCount &&
@@ -848,7 +848,6 @@
 
     RgnOper oper(SkMin32(a_top, b_top), dst, op);
 
-    bool firstInterval = true;
     int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
 
     while (a_bot < SkRegion::kRunTypeSentinel ||
@@ -895,7 +894,6 @@
             oper.addSpan(top, gSentinel, gSentinel);
         }
         oper.addSpan(bot, run0, run1);
-        firstInterval = false;
 
         if (quickExit && !oper.isEmpty()) {
             return QUICK_EXIT_TRUE_COUNT;
@@ -1021,6 +1019,12 @@
         if (a_rect & b_rect) {
             return setRectCheck(result, bounds);
         }
+        if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
+            return setRegionCheck(result, *rgnb);
+        }
+        if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
+            return setRegionCheck(result, *rgna);
+        }
         break;
 
     case kUnion_Op:
diff --git a/src/core/SkRegionPriv.h b/src/core/SkRegionPriv.h
index 91b3a2e..f299f3a 100644
--- a/src/core/SkRegionPriv.h
+++ b/src/core/SkRegionPriv.h
@@ -84,24 +84,16 @@
         return head;
     }
 
-    bool isComplex() const {
-        return this != SkRegion_gEmptyRunHeadPtr && this != SkRegion_gRectRunHeadPtr;
-    }
-
     SkRegion::RunType* writable_runs() {
-        SkASSERT(this->isComplex());
         SkASSERT(fRefCnt == 1);
         return (SkRegion::RunType*)(this + 1);
     }
 
     const SkRegion::RunType* readonly_runs() const {
-        SkASSERT(this->isComplex());
         return (const SkRegion::RunType*)(this + 1);
     }
 
     RunHead* ensureWritable() {
-        SkASSERT(this->isComplex());
-
         RunHead* writable = this;
         if (fRefCnt > 1) {
             // We need to alloc & copy the current region before we call
diff --git a/src/core/SkRegion_path.cpp b/src/core/SkRegion_path.cpp
index 58812eb..80149a3 100644
--- a/src/core/SkRegion_path.cpp
+++ b/src/core/SkRegion_path.cpp
@@ -488,4 +488,3 @@
 
     return true;
 }
-
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index 50526e9..690ee77 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -638,7 +638,7 @@
 
         if (fPathEffect) {
             SkPath effectPath;
-            if (fPathEffect->filterPath(&effectPath, localPath, &rec)) {
+            if (fPathEffect->filterPath(&effectPath, localPath, &rec, NULL)) {
                 localPath.swap(effectPath);
             }
         }
@@ -687,11 +687,9 @@
 
 
 void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const {
-    dst->reset();
-    dst->setScaleX(fPost2x2[0][0]);
-    dst->setSkewX( fPost2x2[0][1]);
-    dst->setSkewY( fPost2x2[1][0]);
-    dst->setScaleY(fPost2x2[1][1]);
+    dst->setAll(fPost2x2[0][0], fPost2x2[0][1], 0,
+                fPost2x2[1][0], fPost2x2[1][1], 0,
+                0,              0,              SkScalarToPersp(SK_Scalar1));
 }
 
 void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const {
@@ -768,4 +766,3 @@
     }
     return c;
 }
-
diff --git a/src/core/SkScalerContext.h b/src/core/SkScalerContext.h
index 05d5bc0..1ada0ac 100644
--- a/src/core/SkScalerContext.h
+++ b/src/core/SkScalerContext.h
@@ -202,7 +202,8 @@
     }
 #endif
 
-    static inline void MakeRec(const SkPaint&, const SkMatrix*, Rec* rec);
+    static inline void MakeRec(const SkPaint&, const SkDeviceProperties* deviceProperties,
+                               const SkMatrix*, Rec* rec);
     static inline void PostMakeRec(const SkPaint&, Rec*);
 
     static SkScalerContext* Create(const SkDescriptor*);
@@ -293,4 +294,3 @@
 
 
 #endif
-
diff --git a/src/core/SkScan.cpp b/src/core/SkScan.cpp
index 30626f7..44968bd 100644
--- a/src/core/SkScan.cpp
+++ b/src/core/SkScan.cpp
@@ -115,4 +115,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkScanPriv.h b/src/core/SkScanPriv.h
index 82402a7..75ceee0 100644
--- a/src/core/SkScanPriv.h
+++ b/src/core/SkScanPriv.h
@@ -38,4 +38,3 @@
 void sk_blit_below(SkBlitter*, const SkIRect& avoid, const SkRegion& clip);
 
 #endif
-
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index fdd0c8e..0d2152c 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -744,4 +744,3 @@
         SkScan::AntiFillPath(path, tmp, &aaBlitter, true);
     }
 }
-
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
index bf1b81f..a6a0869 100644
--- a/src/core/SkScan_Antihair.cpp
+++ b/src/core/SkScan_Antihair.cpp
@@ -89,6 +89,7 @@
 class SkAntiHairBlitter {
 public:
     SkAntiHairBlitter() : fBlitter(NULL) {}
+    virtual ~SkAntiHairBlitter() {}
 
     SkBlitter* getBlitter() const { return fBlitter; }
 
@@ -343,10 +344,12 @@
 }
 #endif
 
+#ifdef SK_DEBUG
 static bool canConvertFDot6ToFixed(SkFDot6 x) {
     const int maxDot6 = SK_MaxS32 >> (16 - 6);
     return SkAbs32(x) <= maxDot6;
 }
+#endif
 
 /*
  *  We want the fractional part of ordinate, but we want multiples of 64 to
@@ -1059,4 +1062,3 @@
         AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
     }
 }
-
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
index 69095c0..ada0078 100644
--- a/src/core/SkScan_Hairline.cpp
+++ b/src/core/SkScan_Hairline.cpp
@@ -33,10 +33,12 @@
     } while (++y < stopy);
 }
 
+#ifdef SK_DEBUG
 static bool canConvertFDot6ToFixed(SkFDot6 x) {
     const int maxDot6 = SK_MaxS32 >> (16 - 6);
     return SkAbs32(x) <= maxDot6;
 }
+#endif
 
 void SkScan::HairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
                          const SkRegion* clip, SkBlitter* blitter) {
@@ -276,7 +278,6 @@
     }
 
     SkAAClipBlitterWrapper wrap;
-    const SkIRect* clipR = NULL;
     const SkRegion* clip = NULL;
 
     {
@@ -288,7 +289,6 @@
             return;
         }
         if (!rclip.quickContains(ibounds)) {
-            clipR = &rclip.getBounds();
             if (rclip.isBW()) {
                 clip = &rclip.bwRgn();
             } else {
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
index a68f2a7..66e9507 100644
--- a/src/core/SkScan_Path.cpp
+++ b/src/core/SkScan_Path.cpp
@@ -727,4 +727,3 @@
         sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir);
     }
 }
-
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index 706c1b7..d51c2ef 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -173,8 +173,8 @@
     return kNone_GradientType;
 }
 
-bool SkShader::asNewEffect(GrContext*, GrEffectStage*) const {
-    return false;
+GrEffectRef* SkShader::asNewEffect(GrContext*, const SkPaint&) const {
+    return NULL;
 }
 
 SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
@@ -182,6 +182,15 @@
     return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0);
 }
 
+#ifdef SK_DEVELOPER
+void SkShader::toString(SkString* str) const {
+    if (this->hasLocalMatrix()) {
+        str->append(" ");
+        this->getLocalMatrix().toString(str);
+    }
+}
+#endif
+
 //////////////////////////////////////////////////////////////////////////////
 
 #include "SkColorShader.h"
@@ -303,6 +312,23 @@
     return kColor_GradientType;
 }
 
+#ifdef SK_DEVELOPER
+void SkColorShader::toString(SkString* str) const {
+    str->append("SkColorShader: (");
+
+    if (fInheritColor) {
+        str->append("Color: inherited from paint");
+    } else {
+        str->append("Color: ");
+        str->appendHex(fColor);
+    }
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkEmptyShader.h"
@@ -324,3 +350,13 @@
 void SkEmptyShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) {
     SkDEBUGFAIL("should never get called, since setContext() returned false");
 }
+
+#ifdef SK_DEVELOPER
+void SkEmptyShader::toString(SkString* str) const {
+    str->append("SkEmptyShader: (");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h
index c1dbfa2..ae79afe 100644
--- a/src/core/SkSpriteBlitter.h
+++ b/src/core/SkSpriteBlitter.h
@@ -44,4 +44,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp
index f7b1fb4..fb343ea 100644
--- a/src/core/SkStream.cpp
+++ b/src/core/SkStream.cpp
@@ -299,6 +299,16 @@
     fOffset = 0;
 }
 
+SkMemoryStream::SkMemoryStream(SkData* data) {
+    if (NULL == data) {
+        fData = SkData::NewEmpty();
+    } else {
+        fData = data;
+        fData->ref();
+    }
+    fOffset = 0;
+}
+
 SkMemoryStream::~SkMemoryStream() {
     fData->unref();
 }
@@ -321,7 +331,13 @@
 }
 
 SkData* SkMemoryStream::setData(SkData* data) {
-    SkRefCnt_SafeAssign(fData, data);
+    fData->unref();
+    if (NULL == data) {
+        fData = SkData::NewEmpty();
+    } else {
+        fData = data;
+        fData->ref();
+    }
     return data;
 }
 
@@ -757,14 +773,14 @@
 
 void SkDebugWStream::newline()
 {
-#ifdef SK_DEBUG
+#if defined(SK_DEBUG) || defined(SK_DEVELOPER)
     SkDebugf("\n");
 #endif
 }
 
 bool SkDebugWStream::write(const void* buffer, size_t size)
 {
-#ifdef SK_DEBUG
+#if defined(SK_DEBUG) || defined(SK_DEVELOPER)
     char* s = new char[size+1];
     memcpy(s, buffer, size);
     s[size] = 0;
diff --git a/src/core/SkStringUtils.cpp b/src/core/SkStringUtils.cpp
new file mode 100644
index 0000000..0f93b8e
--- /dev/null
+++ b/src/core/SkStringUtils.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkString.h"
+#include "SkStringUtils.h"
+
+void SkAddFlagToString(SkString* string, bool flag, const char* flagStr, bool* needSeparator) {
+    if (flag) {
+        if (*needSeparator) {
+            string->append("|");
+        }
+        string->append(flagStr);
+        *needSeparator = true;
+    }
+}
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
index e45a4d0..cc98d74 100644
--- a/src/core/SkStroke.cpp
+++ b/src/core/SkStroke.cpp
@@ -255,9 +255,19 @@
         this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
         this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
     } else {
-        SkVector    normalB, unitB;
+        SkVector    normalB;
+
+#ifdef SK_IGNORE_QUAD_STROKE_FIX
+        SkVector unitB;
         SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
                                              &normalB, &unitB));
+#else
+        normalB = pts[2] - pts[0];
+        normalB.rotateCCW();
+        SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC);
+        SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
+                                     SkScalarSqrt((SK_Scalar1 + dot)/2))));
+#endif
 
         fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
                         pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
@@ -794,4 +804,3 @@
         dst->addRect(r, reverse_direction(dir));
     }
 }
-
diff --git a/src/core/SkStroke.h b/src/core/SkStroke.h
index 4880516..a6a9f08 100644
--- a/src/core/SkStroke.h
+++ b/src/core/SkStroke.h
@@ -54,4 +54,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkStrokeRec.cpp b/src/core/SkStrokeRec.cpp
index 756872b..cdaf241 100644
--- a/src/core/SkStrokeRec.cpp
+++ b/src/core/SkStrokeRec.cpp
@@ -103,4 +103,3 @@
     stroker.strokePath(src, dst);
     return true;
 }
-
diff --git a/src/core/SkStrokerPriv.cpp b/src/core/SkStrokerPriv.cpp
index 0a0a581..269ebd3 100644
--- a/src/core/SkStrokerPriv.cpp
+++ b/src/core/SkStrokerPriv.cpp
@@ -262,6 +262,3 @@
     SkASSERT((unsigned)join < SkPaint::kJoinCount);
     return gJoiners[join];
 }
-
-
-
diff --git a/src/core/SkStrokerPriv.h b/src/core/SkStrokerPriv.h
index e67d677..1c35f0a 100644
--- a/src/core/SkStrokerPriv.h
+++ b/src/core/SkStrokerPriv.h
@@ -39,4 +39,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkTLS.cpp b/src/core/SkTLS.cpp
index e4f6188..f7bf304 100755
--- a/src/core/SkTLS.cpp
+++ b/src/core/SkTLS.cpp
@@ -121,4 +121,3 @@
         curr = next;
     }
 }
-
diff --git a/src/core/SkTSearch.cpp b/src/core/SkTSearch.cpp
index 801c0d6..64c70cb 100644
--- a/src/core/SkTSearch.cpp
+++ b/src/core/SkTSearch.cpp
@@ -112,4 +112,3 @@
         sk_free(fLC);
     }
 }
-
diff --git a/src/core/SkTSort.h b/src/core/SkTSort.h
index db961d0..fbdb55b 100644
--- a/src/core/SkTSort.h
+++ b/src/core/SkTSort.h
@@ -11,69 +11,134 @@
 #define SkTSort_DEFINED
 
 #include "SkTypes.h"
+#include "SkMath.h"
+#include <stddef.h>
 
-template <typename T>
-void SkTHeapSort_SiftDown(T array[], int root, int bottom) {
-    while (root*2 + 1 <= bottom) {
-        int child = root * 2 + 1;
-        if (child+1 <= bottom && array[child] < array[child+1]) {
-            child += 1;
+/* A comparison functor which performs the comparison 'a < b'. */
+template <typename T> struct SkTCompareLT {
+    bool operator()(const T a, const T b) const { return a < b; }
+};
+
+/* A comparison functor which performs the comparison '*a < *b'. */
+template <typename T> struct SkTPointerCompareLT {
+    bool operator()(const T* a, const T* b) const { return *a < *b; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  Sifts a broken heap. The input array is a heap from root to bottom
+ *  except that the root entry may be out of place.
+ *
+ *  Sinks a hole from array[root] to leaf and then sifts the original array[root] element
+ *  from the leaf level up.
+ *
+ *  This version does extra work, in that it copies child to parent on the way down,
+ *  then copies parent to child on the way back up. When copies are inexpensive,
+ *  this is an optimization as this sift variant should only be used when
+ *  the potentially out of place root entry value is expected to be small.
+ *
+ *  @param root the one based index into array of the out-of-place root of the heap.
+ *  @param bottom the one based index in the array of the last entry in the heap.
+ */
+template <typename T, typename C>
+void SkTHeapSort_SiftUp(T array[], size_t root, size_t bottom, C lessThan) {
+    T x = array[root-1];
+    size_t start = root;
+    size_t j = root << 1;
+    while (j <= bottom) {
+        if (j < bottom && lessThan(array[j-1], array[j])) {
+            ++j;
         }
-        if (array[root] < array[child]) {
-            SkTSwap<T>(array[root], array[child]);
-            root = child;
+        array[root-1] = array[j-1];
+        root = j;
+        j = root << 1;
+    }
+    j = root >> 1;
+    while (j >= start) {
+        if (lessThan(array[j-1], x)) {
+            array[root-1] = array[j-1];
+            root = j;
+            j = root >> 1;
         } else {
             break;
         }
     }
+    array[root-1] = x;
 }
 
-template <typename T> void SkTHeapSort(T array[], int count) {
-    int i;
-    for (i = count/2 - 1; i >= 0; --i) {
-        SkTHeapSort_SiftDown<T>(array, i, count-1);
+/*  Sifts a broken heap. The input array is a heap from root to bottom
+ *  except that the root entry may be out of place.
+ *
+ *  Sifts the array[root] element from the root down.
+ *
+ *  @param root the one based index into array of the out-of-place root of the heap.
+ *  @param bottom the one based index in the array of the last entry in the heap.
+ */
+template <typename T, typename C>
+void SkTHeapSort_SiftDown(T array[], size_t root, size_t bottom, C lessThan) {
+    T x = array[root-1];
+    size_t child = root << 1;
+    while (child <= bottom) {
+        if (child < bottom && lessThan(array[child-1], array[child])) {
+            ++child;
+        }
+        if (lessThan(x, array[child-1])) {
+            array[root-1] = array[child-1];
+            root = child;
+            child = root << 1;
+        } else {
+            break;
+        }
     }
-    for (i = count - 1; i > 0; --i) {
+    array[root-1] = x;
+}
+
+/** Sorts the array of size count using comparator lessThan using a Heap Sort algorithm
+ *
+ *  @param array the array to be sorted.
+ *  @param count the number of elements in the array.
+ *  @param lessThan a functor with bool operator()(T a, T b) which returns true if a comes before b.
+ */
+template <typename T, typename C> void SkTHeapSort(T array[], size_t count, C lessThan) {
+    for (size_t i = count >> 1; i > 0; --i) {
+        SkTHeapSort_SiftDown(array, i, count, lessThan);
+    }
+
+    for (size_t i = count - 1; i > 0; --i) {
         SkTSwap<T>(array[0], array[i]);
-        SkTHeapSort_SiftDown<T>(array, 0, i-1);
+        SkTHeapSort_SiftUp(array, 1, i, lessThan);
+    }
+}
+
+/** Sorts the array of size count using comparator '<' using a Heap Sort algorithm. */
+template <typename T> void SkTHeapSort(T array[], size_t count) {
+    SkTHeapSort(array, count, SkTCompareLT<T>());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Sorts the array of size count using comparator lessThan using an Insertion Sort algorithm. */
+template <typename T, typename C> static void SkTInsertionSort(T* left, T* right, C lessThan) {
+    for (T* next = left + 1; next <= right; ++next) {
+        T insert = *next;
+        T* hole = next;
+        while (left < hole && lessThan(insert, *(hole - 1))) {
+            *hole = *(hole - 1);
+            --hole;
+        }
+        *hole = insert;
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-template <typename T>
-static T** SkTQSort_Partition(T** left, T** right, T** pivot) {
-    T* pivotValue = *pivot;
-    SkTSwap(*pivot, *right);
-    T** newPivot = left;
-    while (left < right) {
-        if (**left < *pivotValue) {
-            SkTSwap(*left, *newPivot);
-            newPivot += 1;
-        }
-        left += 1;
-    }
-    SkTSwap(*newPivot, *right);
-    return newPivot;
-}
-
-template <typename T> void SkTQSort(T** left, T** right) {
-    if (left >= right) {
-        return;
-    }
-    T** pivot = left + ((right - left) >> 1);
-    pivot = SkTQSort_Partition(left, right, pivot);
-    SkTQSort(left, pivot - 1);
-    SkTQSort(pivot + 1, right);
-}
-
-template <typename T>
-static T* SkTQSort_Partition(T* left, T* right, T* pivot) {
+template <typename T, typename C>
+static T* SkTQSort_Partition(T* left, T* right, T* pivot, C lessThan) {
     T pivotValue = *pivot;
     SkTSwap(*pivot, *right);
     T* newPivot = left;
     while (left < right) {
-        if (*left < pivotValue) {
+        if (lessThan(*left, pivotValue)) {
             SkTSwap(*left, *newPivot);
             newPivot += 1;
         }
@@ -83,43 +148,65 @@
     return newPivot;
 }
 
+/*  Intro Sort is a modified Quick Sort.
+ *  It recurses on the smaller region after pivoting and loops on the larger.
+ *  When the region to be sorted is a small constant size it uses Insertion Sort.
+ *  When depth becomes zero, it switches over to Heap Sort.
+ */
+template <typename T, typename C> void SkTIntroSort(int depth, T* left, T* right, C lessThan) {
+    while (left < right) {
+        if (depth == 0) {
+            SkTHeapSort<T>(left, right - left + 1, lessThan);
+            return;
+        }
+        --depth;
+
+        T* pivot = left + ((right - left) >> 1);
+        pivot = SkTQSort_Partition(left, right, pivot, lessThan);
+
+        ptrdiff_t leftSize = pivot - left;
+        ptrdiff_t rightSize = right - pivot;
+        if (leftSize < rightSize) {
+            if (leftSize < 8) {
+                SkTInsertionSort(left, pivot - 1, lessThan);
+            } else {
+                SkTIntroSort(depth, left, pivot - 1, lessThan);
+            }
+            left = pivot + 1;
+        } else {
+            if (rightSize < 8) {
+                SkTInsertionSort(pivot + 1, right, lessThan);
+            } else {
+                SkTIntroSort(depth, pivot + 1, right, lessThan);
+            }
+            right = pivot - 1;
+        }
+    }
+}
+
+/** Sorts the region from left to right using comparator lessThan using a Quick Sort algorithm.
+ *
+ *  @param left the beginning of the region to be sorted.
+ *  @param right the end of the region to be sorted (inclusive).
+ *  @param lessThan a functor with bool operator()(T a, T b) which returns true if a comes before b.
+ */
+template <typename T, typename C> void SkTQSort(T* left, T* right, C lessThan) {
+    if (left >= right) {
+        return;
+    }
+    ptrdiff_t size = right - left;
+    int depth = SkNextLog2(SkToU32(size));
+    SkTIntroSort(depth * 2, left, right, lessThan);
+}
+
+/** Sorts the region from left to right using comparator '<' using a Quick Sort algorithm. */
 template <typename T> void SkTQSort(T* left, T* right) {
-    if (left >= right) {
-        return;
-    }
-    T* pivot = left + ((right - left) >> 1);
-    pivot = SkTQSort_Partition(left, right, pivot);
-    SkTQSort(left, pivot - 1);
-    SkTQSort(pivot + 1, right);
+    SkTQSort(left, right, SkTCompareLT<T>());
 }
 
-template <typename S, typename T>
-static T* SkTQSort_Partition(S& context, T* left, T* right, T* pivot,
-                             bool (*lessThan)(S&, const T, const T)) {
-    T pivotValue = *pivot;
-    SkTSwap(*pivot, *right);
-    T* newPivot = left;
-    while (left < right) {
-        if (lessThan(context, *left, pivotValue)) {
-            SkTSwap(*left, *newPivot);
-            newPivot += 1;
-        }
-        left += 1;
-    }
-    SkTSwap(*newPivot, *right);
-    return newPivot;
-}
-
-template <typename S, typename T>
-void SkQSort(S& context, T* left, T* right,
-             bool (*lessThan)(S& , const T, const T)) {
-    if (left >= right) {
-        return;
-    }
-    T* pivot = left + ((right - left) >> 1);
-    pivot = SkTQSort_Partition(context, left, right, pivot, lessThan);
-    SkQSort(context, left, pivot - 1, lessThan);
-    SkQSort(context, pivot + 1, right, lessThan);
+/** Sorts the region from left to right using comparator '* < *' using a Quick Sort algorithm. */
+template <typename T> void SkTQSort(T** left, T** right) {
+    SkTQSort(left, right, SkTPointerCompareLT<T>());
 }
 
 #endif
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
index 94bb237..c30e5a9 100644
--- a/src/core/SkTypeface.cpp
+++ b/src/core/SkTypeface.cpp
@@ -134,4 +134,3 @@
 #endif
     return upem;
 }
-
diff --git a/src/core/SkTypefaceCache.cpp b/src/core/SkTypefaceCache.cpp
index ee5ced9..bb83d55 100644
--- a/src/core/SkTypefaceCache.cpp
+++ b/src/core/SkTypefaceCache.cpp
@@ -145,4 +145,3 @@
     (void)Get().findByProcAndRef(DumpProc, NULL);
 #endif
 }
-
diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
index fe498ce..1af64c2 100644
--- a/src/core/SkUtils.cpp
+++ b/src/core/SkUtils.cpp
@@ -428,4 +428,3 @@
             round_to_K(mi.uordblks));
 #endif
 }
-
diff --git a/src/core/SkUtilsArm.cpp b/src/core/SkUtilsArm.cpp
index 1c49316..b0eaac0 100644
--- a/src/core/SkUtilsArm.cpp
+++ b/src/core/SkUtilsArm.cpp
@@ -16,6 +16,20 @@
 #include <string.h>
 #include <pthread.h>
 
+// Set USE_ANDROID_NDK_CPU_FEATURES to use the Android NDK's
+// cpu-features helper library to detect NEON at runtime. See
+// http://crbug.com/164154 to see why this is needed in Chromium
+// for Android.
+#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_BUILD_FOR_CHROMIUM)
+#  define USE_ANDROID_NDK_CPU_FEATURES 1
+#else
+#  define USE_ANDROID_NDK_CPU_FEATURES 0
+#endif
+
+#if USE_ANDROID_NDK_CPU_FEATURES
+#  include <cpu-features.h>
+#endif
+
 // Set NEON_DEBUG to 1 to allow debugging of the CPU features probing.
 // For now, we always set it for SK_DEBUG builds.
 #ifdef SK_DEBUG
@@ -62,6 +76,12 @@
     SkDebugf("Running dynamic CPU feature detection\n");
 #endif
 
+#if USE_ANDROID_NDK_CPU_FEATURES
+
+  result = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+
+#else  // USE_ANDROID_NDK_CPU_FEATURES
+
     // There is no user-accessible CPUID instruction on ARM that we can use.
     // Instead, we must parse /proc/cpuinfo and look for the 'neon' feature.
     // For example, here's a typical output (Nexus S running ICS 4.0.3):
@@ -151,6 +171,8 @@
 
     } while (0);
 
+#endif  // USE_ANDROID_NDK_CPU_FEATURES
+
     if (result) {
         SkDebugf("Device supports ARM NEON instructions!\n");
     } else {
diff --git a/src/core/SkUtilsArm.h b/src/core/SkUtilsArm.h
index 9ae648a..b9a2614 100644
--- a/src/core/SkUtilsArm.h
+++ b/src/core/SkUtilsArm.h
@@ -39,11 +39,11 @@
 // probes the CPU at runtime (and caches the result).
 
 #if SK_ARM_NEON_IS_NONE
-static bool sk_cpu_arm_has_neon(void) {
+static inline bool sk_cpu_arm_has_neon(void) {
     return false;
 }
 #elif SK_ARM_NEON_IS_ALWAYS
-static bool sk_cpu_arm_has_neon(void) {
+static inline bool sk_cpu_arm_has_neon(void) {
     return true;
 }
 #else // SK_ARM_NEON_IS_DYNAMIC
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index 4bb83b3..56edd19 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -1,80 +1,20 @@
-
 /*
  * 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 "SkWriter32.h"
 
-struct SkWriter32::Block {
-    Block*  fNext;
-    size_t  fSizeOfBlock;      // total space allocated (after this)
-    size_t  fAllocatedSoFar;    // space used so far
-
-    size_t  available() const { return fSizeOfBlock - fAllocatedSoFar; }
-    char*   base() { return (char*)(this + 1); }
-    const char* base() const { return (const char*)(this + 1); }
-
-    uint32_t* alloc(size_t size) {
-        SkASSERT(SkAlign4(size) == size);
-        SkASSERT(this->available() >= size);
-        void* ptr = this->base() + fAllocatedSoFar;
-        fAllocatedSoFar += size;
-        SkASSERT(fAllocatedSoFar <= fSizeOfBlock);
-        return (uint32_t*)ptr;
-    }
-
-    uint32_t* peek32(size_t offset) {
-        SkASSERT(offset <= fAllocatedSoFar + 4);
-        void* ptr = this->base() + offset;
-        return (uint32_t*)ptr;
-    }
-
-    void rewind() {
-        fNext = NULL;
-        fAllocatedSoFar = 0;
-        // keep fSizeOfBlock as is
-    }
-
-    static Block* Create(size_t size) {
-        SkASSERT(SkAlign4(size) == size);
-        Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
-        block->fNext = NULL;
-        block->fSizeOfBlock = size;
-        block->fAllocatedSoFar = 0;
-        return block;
-    }
-
-    static Block* CreateFromStorage(void* storage, size_t size) {
-        SkASSERT(SkIsAlign4((intptr_t)storage));
-        Block* block = (Block*)storage;
-        block->fNext = NULL;
-        block->fSizeOfBlock = size - sizeof(Block);
-        block->fAllocatedSoFar = 0;
-        return block;
-    }
-
-};
-
-#define MIN_BLOCKSIZE   (sizeof(SkWriter32::Block) + sizeof(intptr_t))
-
-///////////////////////////////////////////////////////////////////////////////
-
 SkWriter32::SkWriter32(size_t minSize, void* storage, size_t storageSize) {
     fMinSize = minSize;
     fSize = 0;
-    fSingleBlock = NULL;
-    fSingleBlockSize = 0;
     fWrittenBeforeLastBlock = 0;
+    fHead = fTail = NULL;
 
-    storageSize &= ~3;  // trunc down to multiple of 4
-    if (storageSize >= MIN_BLOCKSIZE) {
-        fHead = fTail = Block::CreateFromStorage(storage, storageSize);
-        fHeadIsExternalStorage = true;
-    } else {
-        fHead = fTail = NULL;
-        fHeadIsExternalStorage = false;
+    if (storageSize) {
+        this->reset(storage, storageSize);
     }
 }
 
@@ -85,7 +25,7 @@
 void SkWriter32::reset() {
     Block* block = fHead;
 
-    if (fHeadIsExternalStorage) {
+    if (this->isHeadExternallyAllocated()) {
         SkASSERT(block);
         // don't 'free' the first block, since it is owned by the caller
         block = block->fNext;
@@ -98,40 +38,29 @@
 
     fSize = 0;
     fWrittenBeforeLastBlock = 0;
-    fSingleBlock = NULL;
-    if (fHeadIsExternalStorage) {
-        SkASSERT(fHead);
-        fHead->rewind();
-        fTail = fHead;
-    } else {
-        fHead = fTail = NULL;
+    fHead = fTail = NULL;
+}
+
+void SkWriter32::reset(void* storage, size_t storageSize) {
+    this->reset();
+
+    storageSize &= ~3;  // trunc down to multiple of 4
+    if (storageSize > 0 && SkIsAlign4((intptr_t)storage)) {
+        fHead = fTail = fExternalBlock.initFromStorage(storage, storageSize);
     }
 }
 
-void SkWriter32::reset(void* block, size_t size) {
-    this->reset();
-    SkASSERT(0 == ((fSingleBlock - (char*)0) & 3));   // need 4-byte alignment
-    fSingleBlock = (char*)block;
-    fSingleBlockSize = (size & ~3);
-}
-
-uint32_t* SkWriter32::reserve(size_t size) {
+SkWriter32::Block* SkWriter32::doReserve(size_t size) {
     SkASSERT(SkAlign4(size) == size);
 
-    if (fSingleBlock) {
-        uint32_t* ptr = (uint32_t*)(fSingleBlock + fSize);
-        fSize += size;
-        SkASSERT(fSize <= fSingleBlockSize);
-        return ptr;
-    }
-
     Block* block = fTail;
+    SkASSERT(NULL == block || block->available() < size);
 
     if (NULL == block) {
         SkASSERT(NULL == fHead);
         fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
         SkASSERT(0 == fWrittenBeforeLastBlock);
-    } else if (block->available() < size) {
+    } else {
         SkASSERT(fSize > 0);
         fWrittenBeforeLastBlock = fSize;
 
@@ -139,10 +68,7 @@
         block->fNext = fTail;
         block = fTail;
     }
-
-    fSize += size;
-
-    return block->alloc(size);
+    return block;
 }
 
 uint32_t* SkWriter32::peek32(size_t offset) {
@@ -151,10 +77,6 @@
     SkASSERT(SkAlign4(offset) == offset);
     SkASSERT(offset <= fSize);
 
-    if (fSingleBlock) {
-        return (uint32_t*)(fSingleBlock + offset);
-    }
-
     // try the fast case, where offset is within fTail
     if (offset >= fWrittenBeforeLastBlock) {
         return fTail->peek32(offset - fWrittenBeforeLastBlock);
@@ -176,7 +98,7 @@
         return;
     }
     if (0 == offset) {
-        this->reset(NULL, 0);
+        this->reset();
         return;
     }
 
@@ -186,10 +108,6 @@
     SkASSERT(offset <= fSize);
     fSize = offset;
 
-    if (fSingleBlock) {
-        return;
-    }
-
     // Try the fast case, where offset is within fTail
     if (offset >= fWrittenBeforeLastBlock) {
         fTail->fAllocatedSoFar = offset - fWrittenBeforeLastBlock;
@@ -227,11 +145,6 @@
 }
 
 void SkWriter32::flatten(void* dst) const {
-    if (fSingleBlock) {
-        memcpy(dst, fSingleBlock, fSize);
-        return;
-    }
-
     const Block* block = fHead;
     SkDEBUGCODE(size_t total = 0;)
 
@@ -270,17 +183,6 @@
 #include "SkStream.h"
 
 size_t SkWriter32::readFromStream(SkStream* stream, size_t length) {
-    if (fSingleBlock) {
-        SkASSERT(fSingleBlockSize >= fSize);
-        size_t remaining = fSingleBlockSize - fSize;
-        if (length > remaining) {
-            length = remaining;
-        }
-        stream->read(fSingleBlock + fSize, length);
-        fSize += length;
-        return length;
-    }
-
     char scratch[1024];
     const size_t MAX = sizeof(scratch);
     size_t remaining = length;
@@ -301,10 +203,6 @@
 }
 
 bool SkWriter32::writeToStream(SkWStream* stream) {
-    if (fSingleBlock) {
-        return stream->write(fSingleBlock, fSize);
-    }
-
     const Block* block = fHead;
     while (block) {
         if (!stream->write(block->base(), block->fAllocatedSoFar)) {
@@ -318,12 +216,6 @@
 #ifdef SK_DEBUG
 void SkWriter32::validate() const {
     SkASSERT(SkIsAlign4(fSize));
-    SkASSERT(SkIsAlign4(fSingleBlockSize));
-
-    if (fSingleBlock) {
-        SkASSERT(fSize <= fSingleBlockSize);
-        return;
-    }
 
     size_t accum = 0;
     const Block* block = fHead;
@@ -402,5 +294,3 @@
     // add 1 since we also write a terminating 0
     return SkAlign4(lenBytes + len + 1);
 }
-
-
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 7a25c6e..520ab87 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -11,6 +11,7 @@
 #include "SkColorPriv.h"
 #include "SkFlattenableBuffers.h"
 #include "SkMathPriv.h"
+#include "SkString.h"
 
 SK_DEFINE_INST_COUNT(SkXfermode)
 
@@ -181,8 +182,8 @@
     return SkPackARGB32(a, r, g, b);
 }
 
-// kMultiply_Mode
-static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
+// kModulate_Mode
+static SkPMColor modulate_modeproc(SkPMColor src, SkPMColor dst) {
     int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
     int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
     int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
@@ -435,7 +436,7 @@
     { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
 
     { plus_modeproc,    SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff },
-    { multiply_modeproc,SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
+    { modulate_modeproc,SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
     { screen_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { overlay_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { darken_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
@@ -696,6 +697,12 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkProcXfermode::toString(SkString* str) const {
+    str->appendf("SkProcXfermode: %p", fProc);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -730,6 +737,7 @@
         return true;
     }
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcCoeffXfermode)
 
 protected:
@@ -753,10 +761,43 @@
     Mode    fMode;
     Coeff   fSrcCoeff, fDstCoeff;
 
-
     typedef SkProcXfermode INHERITED;
 };
 
+#ifdef SK_DEVELOPER
+void SkProcCoeffXfermode::toString(SkString* str) const {
+    str->append("SkProcCoeffXfermode: ");
+
+    const char *gModeStrings[kLastMode+1] = {
+        "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn",
+        "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Plus",
+        "Modulate", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge",
+        "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion"
+    };
+
+    str->append("mode: ");
+    str->append(gModeStrings[fMode]);
+
+    static const char* gCoeffStrings[kCoeffCount] = {
+        "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
+    };
+
+    str->append(" src: ");
+    if (CANNOT_USE_COEFF == fSrcCoeff) {
+        str->append("can't use");
+    } else {
+        str->append(gCoeffStrings[fSrcCoeff]);
+    }
+
+    str->append(" dst: ");
+    if (CANNOT_USE_COEFF == fDstCoeff) {
+        str->append("can't use");
+    } else {
+        str->append(gCoeffStrings[fDstCoeff]);
+    }
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkClearXfermode : public SkProcCoeffXfermode {
@@ -766,12 +807,14 @@
     virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
     virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkClearXfermode)
 
 private:
     SkClearXfermode(SkFlattenableReadBuffer& buffer)
         : SkProcCoeffXfermode(buffer) {}
 
+    typedef SkProcCoeffXfermode INHERITED;
 };
 
 void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
@@ -811,6 +854,12 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkClearXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkSrcXfermode : public SkProcCoeffXfermode {
@@ -820,12 +869,14 @@
     virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
     virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSrcXfermode)
 
 private:
     SkSrcXfermode(SkFlattenableReadBuffer& buffer)
         : SkProcCoeffXfermode(buffer) {}
 
+    typedef SkProcCoeffXfermode INHERITED;
 };
 
 void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
@@ -870,6 +921,11 @@
         }
     }
 }
+#ifdef SK_DEVELOPER
+void SkSrcXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -879,6 +935,7 @@
 
     virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstInXfermode)
 
 private:
@@ -907,6 +964,12 @@
     } while (--count != 0);
 }
 
+#ifdef SK_DEVELOPER
+void SkDstInXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkDstOutXfermode : public SkProcCoeffXfermode {
@@ -915,6 +978,7 @@
 
     virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstOutXfermode)
 
 private:
@@ -944,6 +1008,12 @@
     } while (--count != 0);
 }
 
+#ifdef SK_DEVELOPER
+void SkDstOutXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkXfermode* SkXfermode::Create(Mode mode) {
@@ -1164,7 +1234,7 @@
     { NULL,                 NULL,                   NULL            }, // XOR
 
     { NULL,                 NULL,                   NULL            }, // plus
-    { NULL,                 NULL,                   NULL            }, // multiply
+    { NULL,                 NULL,                   NULL            }, // modulate
     { NULL,                 NULL,                   NULL            }, // screen
     { NULL,                 NULL,                   NULL            }, // overlay
     { darken_modeproc16_0,  darken_modeproc16_255,  NULL            }, // darken
diff --git a/src/device/xps/SkXPSDevice.cpp b/src/device/xps/SkXPSDevice.cpp
index e1d5eed..2f01123 100644
--- a/src/device/xps/SkXPSDevice.cpp
+++ b/src/device/xps/SkXPSDevice.cpp
@@ -2061,7 +2061,7 @@
     newTypefaceUse.fontData = fontData;
     newTypefaceUse.xpsFont = xpsFontResource.release();
 
-    SkAutoGlyphCache agc = SkAutoGlyphCache(paint, &SkMatrix::I());
+    SkAutoGlyphCache agc = SkAutoGlyphCache(paint, NULL, &SkMatrix::I());
     SkGlyphCache* glyphCache = agc.getCache();
     unsigned int glyphCount = glyphCache->getGlyphCount();
     newTypefaceUse.glyphsUsed = new SkBitSet(glyphCount);
@@ -2422,4 +2422,3 @@
 bool SkXPSDevice::allowImageFilter(SkImageFilter*) {
     return false;
 }
-
diff --git a/src/effects/Sk1DPathEffect.cpp b/src/effects/Sk1DPathEffect.cpp
index 3ef050c..10a68b9 100644
--- a/src/effects/Sk1DPathEffect.cpp
+++ b/src/effects/Sk1DPathEffect.cpp
@@ -12,7 +12,7 @@
 #include "SkPathMeasure.h"
 
 bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                SkStrokeRec*) const {
+                                SkStrokeRec*, const SkRect*) const {
     SkPathMeasure   meas(src, false);
     do {
         SkScalar    length = meas.getLength();
@@ -69,10 +69,10 @@
 }
 
 bool SkPath1DPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                    SkStrokeRec* rec) const {
+                            SkStrokeRec* rec, const SkRect* cullRect) const {
     if (fAdvance > 0) {
         rec->setFillStyle();
-        return this->INHERITED::filterPath(dst, src, rec);
+        return this->INHERITED::filterPath(dst, src, rec, cullRect);
     }
     return false;
 }
diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
index a4ebe74..dc15f07 100644
--- a/src/effects/Sk2DPathEffect.cpp
+++ b/src/effects/Sk2DPathEffect.cpp
@@ -17,7 +17,7 @@
 }
 
 bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                SkStrokeRec*) const {
+                                SkStrokeRec*, const SkRect*) const {
     if (!fMatrixIsInvertible) {
         return false;
     }
@@ -80,8 +80,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkLine2DPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                    SkStrokeRec* rec) const {
-    if (this->INHERITED::filterPath(dst, src, rec)) {
+                            SkStrokeRec* rec, const SkRect* cullRect) const {
+    if (this->INHERITED::filterPath(dst, src, rec, cullRect)) {
         rec->setStrokeStyle(fWidth);
         return true;
     }
diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp
index 54a28ce..7492cce 100644
--- a/src/effects/SkArithmeticMode.cpp
+++ b/src/effects/SkArithmeticMode.cpp
@@ -1,5 +1,13 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "SkArithmeticMode.h"
 #include "SkColorPriv.h"
+#include "SkString.h"
 #include "SkUnPreMultiply.h"
 
 class SkArithmeticMode_scalar : public SkXfermode {
@@ -14,10 +22,13 @@
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
                         const SkAlpha aa[]) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_UNFLATTENABLE_OBJECT()
 
 private:
     SkScalar fK[4];
+
+    typedef SkXfermode INHERITED;
 };
 
 static int pinToByte(int value) {
@@ -119,6 +130,17 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkArithmeticMode_scalar::toString(SkString* str) const {
+    str->append("SkArithmeticMode_scalar: ");
+    for (int i = 0; i < 4; ++i) {
+        str->appendScalar(fK[i]);
+        if (i < 3) {
+            str->append(" ");
+        }
+    }
+}
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -169,4 +191,3 @@
     }
     return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4));
 }
-
diff --git a/src/effects/SkAvoidXfermode.cpp b/src/effects/SkAvoidXfermode.cpp
index f39ee74..206f7e9 100644
--- a/src/effects/SkAvoidXfermode.cpp
+++ b/src/effects/SkAvoidXfermode.cpp
@@ -8,6 +8,7 @@
 #include "SkAvoidXfermode.h"
 #include "SkColorPriv.h"
 #include "SkFlattenableBuffers.h"
+#include "SkString.h"
 
 SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode) {
     if (tolerance > 255) {
@@ -217,3 +218,15 @@
                              const SkAlpha aa[]) const {
     // override in subclass
 }
+
+#ifdef SK_DEVELOPER
+void SkAvoidXfermode::toString(SkString* str) const {
+    str->append("SkAvoidXfermode: opColor: ");
+    str->appendHex(fOpColor);
+    str->appendf("distMul: %d ", fDistMul);
+
+    static const char* gModeStrings[] = { "Avoid", "Target" };
+
+    str->appendf("mode: %s", gModeStrings[fMode]);
+}
+#endif
diff --git a/src/effects/SkBicubicImageFilter.cpp b/src/effects/SkBicubicImageFilter.cpp
new file mode 100644
index 0000000..0670c4d
--- /dev/null
+++ b/src/effects/SkBicubicImageFilter.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2013 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 "SkBicubicImageFilter.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMatrix.h"
+#include "SkRect.h"
+#include "SkUnPreMultiply.h"
+
+#if SK_SUPPORT_GPU
+#include "gl/GrGLEffectMatrix.h"
+#include "effects/GrSingleTextureEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "SkImageFilterUtils.h"
+#endif
+
+SkBicubicImageFilter::SkBicubicImageFilter(const SkSize& scale, const SkScalar coefficients[16], SkImageFilter* input)
+  : INHERITED(input),
+    fScale(scale) {
+    memcpy(fCoefficients, coefficients, sizeof(fCoefficients));
+}
+
+#define DS(x) SkDoubleToScalar(x)
+
+SkBicubicImageFilter* SkBicubicImageFilter::CreateMitchell(const SkSize& scale,
+                                                           SkImageFilter* input) {
+    static const SkScalar coefficients[16] = {
+        DS( 1.0 / 18.0), DS(-9.0 / 18.0), DS( 15.0 / 18.0), DS( -7.0 / 18.0),
+        DS(16.0 / 18.0), DS( 0.0 / 18.0), DS(-36.0 / 18.0), DS( 21.0 / 18.0),
+        DS( 1.0 / 18.0), DS( 9.0 / 18.0), DS( 27.0 / 18.0), DS(-21.0 / 18.0),
+        DS( 0.0 / 18.0), DS( 0.0 / 18.0), DS( -6.0 / 18.0), DS(  7.0 / 18.0),
+    };
+    return SkNEW_ARGS(SkBicubicImageFilter, (scale, coefficients, input));
+}
+
+SkBicubicImageFilter::SkBicubicImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    SkDEBUGCODE(uint32_t readSize =) buffer.readScalarArray(fCoefficients);
+    SkASSERT(readSize == 16);
+    fScale.fWidth = buffer.readScalar();
+    fScale.fHeight = buffer.readScalar();
+}
+
+void SkBicubicImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalarArray(fCoefficients, 16);
+    buffer.writeScalar(fScale.fWidth);
+    buffer.writeScalar(fScale.fHeight);
+}
+
+SkBicubicImageFilter::~SkBicubicImageFilter() {
+}
+
+inline SkPMColor cubicBlend(const SkScalar c[16], SkScalar t, SkPMColor c0, SkPMColor c1, SkPMColor c2, SkPMColor c3) {
+    SkScalar t2 = t * t, t3 = t2 * t;
+    SkScalar cc[4];
+    // FIXME:  For the fractx case, this should be refactored out of this function.
+    cc[0] = c[0]  + SkScalarMul(c[1], t) + SkScalarMul(c[2], t2) + SkScalarMul(c[3], t3);
+    cc[1] = c[4]  + SkScalarMul(c[5], t) + SkScalarMul(c[6], t2) + SkScalarMul(c[7], t3);
+    cc[2] = c[8]  + SkScalarMul(c[9], t) + SkScalarMul(c[10], t2) + SkScalarMul(c[11], t3);
+    cc[3] = c[12] + SkScalarMul(c[13], t) + SkScalarMul(c[14], t2) + SkScalarMul(c[15], t3);
+    SkScalar a = SkScalarMul(cc[0], SkGetPackedA32(c0)) + SkScalarMul(cc[1], SkGetPackedA32(c1)) + SkScalarMul(cc[2], SkGetPackedA32(c2)) + SkScalarMul(cc[3], SkGetPackedA32(c3));
+    SkScalar r = SkScalarMul(cc[0], SkGetPackedR32(c0)) + SkScalarMul(cc[1], SkGetPackedR32(c1)) + SkScalarMul(cc[2], SkGetPackedR32(c2)) + SkScalarMul(cc[3], SkGetPackedR32(c3));
+    SkScalar g = SkScalarMul(cc[0], SkGetPackedG32(c0)) + SkScalarMul(cc[1], SkGetPackedG32(c1)) + SkScalarMul(cc[2], SkGetPackedG32(c2)) + SkScalarMul(cc[3], SkGetPackedG32(c3));
+    SkScalar b = SkScalarMul(cc[0], SkGetPackedB32(c0)) + SkScalarMul(cc[1], SkGetPackedB32(c1)) + SkScalarMul(cc[2], SkGetPackedB32(c2)) + SkScalarMul(cc[3], SkGetPackedB32(c3));
+    return SkPackARGB32(SkScalarRoundToInt(SkScalarClampMax(a, 255)), SkScalarRoundToInt(SkScalarClampMax(r, 255)), SkScalarRoundToInt(SkScalarClampMax(g, 255)), SkScalarRoundToInt(SkScalarClampMax(b, 255)));
+}
+
+bool SkBicubicImageFilter::onFilterImage(Proxy* proxy,
+                                         const SkBitmap& source,
+                                         const SkMatrix& matrix,
+                                         SkBitmap* result,
+                                         SkIPoint* loc) {
+    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
+    if (src.config() != SkBitmap::kARGB_8888_Config) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(src);
+    if (!src.getPixels()) {
+        return false;
+    }
+
+    SkRect dstRect = SkRect::MakeWH(SkScalarMul(SkIntToScalar(src.width()), fScale.fWidth),
+                                    SkScalarMul(SkIntToScalar(src.height()), fScale.fHeight));
+    SkIRect dstIRect;
+    dstRect.roundOut(&dstIRect);
+    result->setConfig(src.config(), dstIRect.width(), dstIRect.height());
+    result->allocPixels();
+    if (!result->getPixels()) {
+        return false;
+    }
+
+    SkRect srcRect;
+    src.getBounds(&srcRect);
+    SkMatrix inverse;
+    inverse.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
+    inverse.postTranslate(SkFloatToScalar(-0.5f), SkFloatToScalar(-0.5f));
+
+    for (int y = dstIRect.fTop; y < dstIRect.fBottom; ++y) {
+        SkPMColor* dptr = result->getAddr32(dstIRect.fLeft, y);
+        for (int x = dstIRect.fLeft; x < dstIRect.fRight; ++x) {
+            SkPoint srcPt, dstPt = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
+            inverse.mapPoints(&srcPt, &dstPt, 1);
+            SkScalar fractx = srcPt.fX - SkScalarFloorToScalar(srcPt.fX);
+            SkScalar fracty = srcPt.fY - SkScalarFloorToScalar(srcPt.fY);
+            int sx = SkScalarFloorToInt(srcPt.fX);
+            int sy = SkScalarFloorToInt(srcPt.fY);
+            int x0 = SkClampMax(sx - 1, src.width() - 1);
+            int x1 = SkClampMax(sx    , src.width() - 1);
+            int x2 = SkClampMax(sx + 1, src.width() - 1);
+            int x3 = SkClampMax(sx + 2, src.width() - 1);
+            int y0 = SkClampMax(sy - 1, src.height() - 1);
+            int y1 = SkClampMax(sy    , src.height() - 1);
+            int y2 = SkClampMax(sy + 1, src.height() - 1);
+            int y3 = SkClampMax(sy + 2, src.height() - 1);
+            SkPMColor s00 = *src.getAddr32(x0, y0);
+            SkPMColor s10 = *src.getAddr32(x1, y0);
+            SkPMColor s20 = *src.getAddr32(x2, y0);
+            SkPMColor s30 = *src.getAddr32(x3, y0);
+            SkPMColor s0 = cubicBlend(fCoefficients, fractx, s00, s10, s20, s30);
+            SkPMColor s01 = *src.getAddr32(x0, y1);
+            SkPMColor s11 = *src.getAddr32(x1, y1);
+            SkPMColor s21 = *src.getAddr32(x2, y1);
+            SkPMColor s31 = *src.getAddr32(x3, y1);
+            SkPMColor s1 = cubicBlend(fCoefficients, fractx, s01, s11, s21, s31);
+            SkPMColor s02 = *src.getAddr32(x0, y2);
+            SkPMColor s12 = *src.getAddr32(x1, y2);
+            SkPMColor s22 = *src.getAddr32(x2, y2);
+            SkPMColor s32 = *src.getAddr32(x3, y2);
+            SkPMColor s2 = cubicBlend(fCoefficients, fractx, s02, s12, s22, s32);
+            SkPMColor s03 = *src.getAddr32(x0, y3);
+            SkPMColor s13 = *src.getAddr32(x1, y3);
+            SkPMColor s23 = *src.getAddr32(x2, y3);
+            SkPMColor s33 = *src.getAddr32(x3, y3);
+            SkPMColor s3 = cubicBlend(fCoefficients, fractx, s03, s13, s23, s33);
+            *dptr++ = cubicBlend(fCoefficients, fracty, s0, s1, s2, s3);
+        }
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+class GrGLBicubicEffect;
+
+class GrBicubicEffect : public GrSingleTextureEffect {
+public:
+    virtual ~GrBicubicEffect();
+
+    static const char* Name() { return "Bicubic"; }
+    const float* coefficients() const { return fCoefficients; }
+
+    typedef GrGLBicubicEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16]) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients)));
+        return CreateEffectRef(effect);
+    }
+
+private:
+    GrBicubicEffect(GrTexture*, const SkScalar coefficients[16]);
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+    float    fCoefficients[16];
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+class GrGLBicubicEffect : public GrGLEffect {
+public:
+    GrGLBicubicEffect(const GrBackendEffectFactory& factory,
+                      const GrEffectRef& effect);
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+private:
+    typedef GrGLUniformManager::UniformHandle        UniformHandle;
+
+    UniformHandle       fCoefficientsUni;
+    UniformHandle       fImageIncrementUni;
+
+    GrGLEffectMatrix    fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLBicubicEffect::GrGLBicubicEffect(const GrBackendEffectFactory& factory,
+                                     const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fCoefficientsUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle) {
+}
+
+void GrGLBicubicEffect::emitCode(GrGLShaderBuilder* builder,
+                                 const GrEffectStage&,
+                                 EffectKey key,
+                                 const char* vertexCoords,
+                                 const char* outputColor,
+                                 const char* inputColor,
+                                 const TextureSamplerArray& samplers) {
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+    fCoefficientsUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                           kMat44f_GrSLType, "Coefficients");
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "ImageIncrement");
+    SkString* code = &builder->fFSCode;
+
+    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+    const char* coeff = builder->getUniformCStr(fCoefficientsUni);
+
+    SkString cubicBlendName;
+
+    static const GrGLShaderVar gCubicBlendArgs[] = {
+        GrGLShaderVar("coefficients",  kMat44f_GrSLType),
+        GrGLShaderVar("t",             kFloat_GrSLType),
+        GrGLShaderVar("c0",            kVec4f_GrSLType),
+        GrGLShaderVar("c1",            kVec4f_GrSLType),
+        GrGLShaderVar("c2",            kVec4f_GrSLType),
+        GrGLShaderVar("c3",            kVec4f_GrSLType),
+    };
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kVec4f_GrSLType,
+                          "cubicBlend",
+                          SK_ARRAY_COUNT(gCubicBlendArgs),
+                          gCubicBlendArgs,
+                          "\tvec4 ts = vec4(1.0, t, t * t, t * t * t);\n"
+                          "\tvec4 c = coefficients * ts;\n"
+                          "\treturn c.x * c0 + c.y * c1 + c.z * c2 + c.w * c3;\n",
+                          &cubicBlendName);
+    code->appendf("\tvec2 coord = %s - %s * vec2(0.5, 0.5);\n", coords, imgInc);
+    code->appendf("\tvec2 f = fract(coord / %s);\n", imgInc);
+    for (int y = 0; y < 4; ++y) {
+        for (int x = 0; x < 4; ++x) {
+            SkString coord;
+            coord.printf("coord + %s * vec2(%d, %d)", imgInc, x - 1, y - 1);
+            code->appendf("\tvec4 s%d%d = ", x, y);
+            builder->appendTextureLookup(&builder->fFSCode, samplers[0], coord.c_str());
+            code->appendf(";\n");
+        }
+        code->appendf("\tvec4 s%d = %s(%s, f.x, s0%d, s1%d, s2%d, s3%d);\n", y, cubicBlendName.c_str(), coeff, y, y, y, y);
+    }
+    code->appendf("\t%s = %s(%s, f.y, s0, s1, s2, s3);\n", outputColor, cubicBlendName.c_str(), coeff);
+}
+
+GrGLEffect::EffectKey GrGLBicubicEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
+    const GrBicubicEffect& m = GetEffectFromStage<GrBicubicEffect>(s);
+    EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
+                                                   s.getCoordChangeMatrix(),
+                                                   m.texture(0));
+    return matrixKey;
+}
+
+void GrGLBicubicEffect::setData(const GrGLUniformManager& uman,
+                                const GrEffectStage& stage) {
+    const GrBicubicEffect& effect = GetEffectFromStage<GrBicubicEffect>(stage);
+    GrTexture& texture = *effect.texture(0);
+    float imageIncrement[2];
+    imageIncrement[0] = 1.0f / texture.width();
+    imageIncrement[1] = 1.0f / texture.height();
+    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+    uman.setMatrix4f(fCoefficientsUni, effect.coefficients());
+    fEffectMatrix.setData(uman,
+                          effect.getMatrix(),
+                          stage.getCoordChangeMatrix(),
+                          effect.texture(0));
+}
+
+GrBicubicEffect::GrBicubicEffect(GrTexture* texture,
+                                 const SkScalar coefficients[16])
+  : INHERITED(texture, MakeDivByTextureWHMatrix(texture)) {
+    for (int y = 0; y < 4; y++) {
+        for (int x = 0; x < 4; x++) {
+            // Convert from row-major scalars to column-major floats.
+            fCoefficients[x * 4 + y] = SkScalarToFloat(coefficients[y * 4 + x]);
+        }
+    }
+}
+
+GrBicubicEffect::~GrBicubicEffect() {
+}
+
+const GrBackendEffectFactory& GrBicubicEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrBicubicEffect>::getInstance();
+}
+
+bool GrBicubicEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrBicubicEffect& s = CastEffect<GrBicubicEffect>(sBase);
+    return this->texture(0) == s.texture(0) &&
+           !memcmp(fCoefficients, s.coefficients(), 16);
+}
+
+void GrBicubicEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // FIXME:  Perhaps we can do better.
+    *validFlags = 0;
+    return;
+}
+
+GR_DEFINE_EFFECT_TEST(GrBicubicEffect);
+
+GrEffectRef* GrBicubicEffect::TestCreate(SkRandom* random,
+                                         GrContext* context,
+                                         GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    SkScalar coefficients[16];
+    for (int i = 0; i < 16; i++) {
+        coefficients[i] = random->nextSScalar1();
+    }
+    return GrBicubicEffect::Create(textures[texIdx], coefficients);
+}
+
+bool SkBicubicImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap srcBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &srcBM)) {
+        return false;
+    }
+    GrTexture* srcTexture = (GrTexture*) srcBM.getTexture();
+    GrContext* context = srcTexture->getContext();
+
+    SkRect dstRect = SkRect::MakeWH(srcBM.width() * fScale.fWidth,
+                                    srcBM.height() * fScale.fHeight);
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = SkScalarCeilToInt(dstRect.width());
+    desc.fHeight = SkScalarCeilToInt(dstRect.height());
+    desc.fConfig = kSkia8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    SkAutoTUnref<GrTexture> dst(ast.detach());
+    if (!dst) {
+        return false;
+    }
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(GrBicubicEffect::Create(srcTexture, fCoefficients))->unref();
+    SkRect srcRect;
+    srcBM.getBounds(&srcRect);
+    context->drawRectToRect(paint, dstRect, srcRect);
+    return SkImageFilterUtils::WrapTexture(dst, desc.fWidth, desc.fHeight, result);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index 1394a0a..3f6d3c5 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -10,12 +10,11 @@
 #include "SkColorPriv.h"
 #include "SkFlattenableBuffers.h"
 #if SK_SUPPORT_GPU
-#include "SkGr.h"
-#include "SkGrPixelRef.h"
+#include "GrContext.h"
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLEffectMatrix.h"
-#include "effects/GrSingleTextureEffect.h"
 #include "GrTBackendEffectFactory.h"
+#include "SkImageFilterUtils.h"
 #endif
 
 namespace {
@@ -26,7 +25,7 @@
       case SkBlendImageFilter::kNormal_Mode:
         return SkXfermode::kSrcOver_Mode;
       case SkBlendImageFilter::kMultiply_Mode:
-        return SkXfermode::kMultiply_Mode;
+        return SkXfermode::kModulate_Mode;
       case SkBlendImageFilter::kScreen_Mode:
         return SkXfermode::kScreen_Mode;
       case SkBlendImageFilter::kDarken_Mode:
@@ -116,7 +115,7 @@
 class GrGLBlendEffect : public GrGLEffect {
 public:
     GrGLBlendEffect(const GrBackendEffectFactory& factory,
-                    const GrEffect& effect);
+                    const GrEffectRef& effect);
     virtual ~GrGLBlendEffect();
 
     virtual void emitCode(GrGLShaderBuilder*,
@@ -132,121 +131,122 @@
     virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
 
 private:
+    SkBlendImageFilter::Mode    fMode;
+    GrGLEffectMatrix            fForegroundEffectMatrix;
+    GrGLEffectMatrix            fBackgroundEffectMatrix;
+
     typedef GrGLEffect INHERITED;
-    SkBlendImageFilter::Mode fMode;
-    GrGLEffectMatrix fEffectMatrix;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class GrBlendEffect : public GrSingleTextureEffect {
+class GrBlendEffect : public GrEffect {
 public:
-    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, const SkMatrix&);
+    static GrEffectRef* Create(SkBlendImageFilter::Mode mode,
+                               GrTexture* foreground,
+                               GrTexture* background) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrBlendEffect, (mode, foreground, background)));
+        return CreateEffectRef(effect);
+    }
+
     virtual ~GrBlendEffect();
 
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
     const GrBackendEffectFactory& getFactory() const;
     SkBlendImageFilter::Mode mode() const { return fMode; }
 
     typedef GrGLBlendEffect GLEffect;
     static const char* Name() { return "Blend"; }
 
+    void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
 private:
-    typedef GrSingleTextureEffect INHERITED;
-    SkBlendImageFilter::Mode fMode;
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, GrTexture* background);
+    GrTextureAccess             fForegroundAccess;
+    GrTextureAccess             fBackgroundAccess;
+    SkBlendImageFilter::Mode    fMode;
+
+    typedef GrEffect INHERITED;
 };
 
-// FIXME:  This should be refactored with SkSingleInputImageFilter's version.
-static GrTexture* getInputResultAsTexture(SkImageFilter::Proxy* proxy,
-                                          SkImageFilter* input,
-                                          GrTexture* src,
-                                          const SkRect& rect) {
-    GrTexture* resultTex;
-    if (!input) {
-        resultTex = src;
-    } else if (input->canFilterImageGPU()) {
-        // onFilterImageGPU() already refs the result, so just return it here.
-        return input->onFilterImageGPU(proxy, src, rect);
-    } else {
-        SkBitmap srcBitmap, result;
-        srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, src->width(), src->height());
-        srcBitmap.setPixelRef(new SkGrPixelRef(src))->unref();
-        SkIPoint offset;
-        if (input->filterImage(proxy, srcBitmap, SkMatrix(), &result, &offset)) {
-            if (result.getTexture()) {
-                resultTex = (GrTexture*) result.getTexture();
-            } else {
-                resultTex = GrLockCachedBitmapTexture(src->getContext(), result, NULL);
-                SkSafeRef(resultTex);
-                GrUnlockCachedBitmapTexture(resultTex);
-                return resultTex;
-            }
-        } else {
-            resultTex = src;
-        }
+bool SkBlendImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap backgroundBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getBackgroundInput(), proxy, src, &backgroundBM)) {
+        return false;
     }
-    SkSafeRef(resultTex);
-    return resultTex;
-}
-
-GrTexture* SkBlendImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
-    SkAutoTUnref<GrTexture> background(getInputResultAsTexture(proxy, getBackgroundInput(), src, rect));
-    SkAutoTUnref<GrTexture> foreground(getInputResultAsTexture(proxy, getForegroundInput(), src, rect));
-    GrContext* context = src->getContext();
+    GrTexture* background = (GrTexture*) backgroundBM.getTexture();
+    SkBitmap foregroundBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getForegroundInput(), proxy, src, &foregroundBM)) {
+        return false;
+    }
+    GrTexture* foreground = (GrTexture*) foregroundBM.getTexture();
+    GrContext* context = foreground->getContext();
 
     GrTextureDesc desc;
     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
-    desc.fWidth = SkScalarCeilToInt(rect.width());
-    desc.fHeight = SkScalarCeilToInt(rect.height());
-    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fWidth = src.width();
+    desc.fHeight = src.height();
+    desc.fConfig = kSkia8888_GrPixelConfig;
 
     GrAutoScratchTexture ast(context, desc);
-    GrTexture* dst = ast.detach();
-
-    GrContext::AutoMatrix am;
-    am.setIdentity(context);
+    SkAutoTUnref<GrTexture> dst(ast.detach());
 
     GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
-    GrContext::AutoClip ac(context, rect);
 
-    SkMatrix backgroundTexMatrix, foregroundTexMatrix;
-    backgroundTexMatrix.setIDiv(background->width(), background->height());
-    foregroundTexMatrix.setIDiv(foreground->width(), foreground->height());
     GrPaint paint;
     paint.colorStage(0)->setEffect(
-        SkNEW_ARGS(GrSingleTextureEffect, (background.get(), backgroundTexMatrix)))->unref();
-    paint.colorStage(1)->setEffect(
-        SkNEW_ARGS(GrBlendEffect, (fMode, foreground.get(), foregroundTexMatrix)))->unref();
-    context->drawRect(paint, rect);
-    return dst;
+        GrBlendEffect::Create(fMode, foreground, background))->unref();
+    SkRect srcRect;
+    src.getBounds(&srcRect);
+    context->drawRect(paint, srcRect);
+    return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode,
                              GrTexture* foreground,
-                             const SkMatrix& matrix)
-    : INHERITED(foreground, matrix), fMode(mode) {
+                             GrTexture* background)
+    : fForegroundAccess(foreground)
+    , fBackgroundAccess(background)
+    , fMode(mode) {
+    this->addTextureAccess(&fForegroundAccess);
+    this->addTextureAccess(&fBackgroundAccess);
 }
 
 GrBlendEffect::~GrBlendEffect() {
 }
 
-bool GrBlendEffect::isEqual(const GrEffect& sBase) const {
-    const GrBlendEffect& s = static_cast<const GrBlendEffect&>(sBase);
-    return INHERITED::isEqual(sBase) && fMode == s.fMode;
+bool GrBlendEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrBlendEffect& s = CastEffect<GrBlendEffect>(sBase);
+    return fForegroundAccess.getTexture() == s.fForegroundAccess.getTexture() &&
+           fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture() &&
+           fMode == s.fMode;
 }
 
 const GrBackendEffectFactory& GrBlendEffect::getFactory() const {
     return GrTBackendEffectFactory<GrBlendEffect>::getInstance();
 }
 
+void GrBlendEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // The output alpha is always 1 - (1 - FGa) * (1 - BGa). So if either FGa or BGa is known to
+    // be one then the output alpha is one. (This effect ignores its input. We should have a way to
+    // communicate this.)
+    if (GrPixelConfigIsOpaque(fForegroundAccess.getTexture()->config()) ||
+        GrPixelConfigIsOpaque(fBackgroundAccess.getTexture()->config())) {
+        *validFlags = kA_ValidComponentFlag;
+        *color = GrColorPackRGBA(0, 0, 0, 0xff);
+    } else {
+        *validFlags = 0;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
-GrGLBlendEffect::GrGLBlendEffect(const GrBackendEffectFactory& factory,
-                                 const GrEffect& effect)
+GrGLBlendEffect::GrGLBlendEffect(const GrBackendEffectFactory& factory, const GrEffectRef& effect)
     : INHERITED(factory),
-      fMode(static_cast<const GrBlendEffect&>(effect).mode()) {
+      fMode(CastEffect<GrBlendEffect>(effect).mode()) {
 }
 
 GrGLBlendEffect::~GrGLBlendEffect() {
@@ -259,15 +259,25 @@
                                const char* outputColor,
                                const char* inputColor,
                                const TextureSamplerArray& samplers) {
-    const char* coords;
-    GrSLType coordsType =  fEffectMatrix.emitCode(builder, key, vertexCoords, &coords);
+    const char* fgCoords;
+    const char* bgCoords;
+    GrSLType fgCoordsType =  fForegroundEffectMatrix.emitCode(
+        builder, key, vertexCoords, &fgCoords, NULL, "FG");
+    GrSLType bgCoordsType =  fBackgroundEffectMatrix.emitCode(
+        builder, key, vertexCoords, &bgCoords, NULL, "BG");
 
     SkString* code = &builder->fFSCode;
-    const char* bgColor = inputColor;
+    const char* bgColor = "bgColor";
     const char* fgColor = "fgColor";
+
     code->appendf("\t\tvec4 %s = ", fgColor);
-    builder->appendTextureLookup(code, samplers[0], coords, coordsType);
+    builder->appendTextureLookup(code, samplers[0], fgCoords, fgCoordsType);
     code->append(";\n");
+
+    code->appendf("\t\tvec4 %s = ", bgColor);
+    builder->appendTextureLookup(code, samplers[1], bgCoords, bgCoordsType);
+    code->append(";\n");
+
     code->appendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.b);\n", outputColor, bgColor, fgColor);
     switch (fMode) {
       case SkBlendImageFilter::kNormal_Mode:
@@ -289,15 +299,36 @@
 }
 
 void GrGLBlendEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
-    const GrBlendEffect& blend = static_cast<const GrBlendEffect&>(*stage.getEffect());
-    fEffectMatrix.setData(uman, blend.getMatrix(), stage.getCoordChangeMatrix(), blend.texture(0));
+    const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage);
+    GrTexture* fgTex = blend.texture(0);
+    GrTexture* bgTex = blend.texture(1);
+    fForegroundEffectMatrix.setData(uman,
+                                    GrEffect::MakeDivByTextureWHMatrix(fgTex),
+                                    stage.getCoordChangeMatrix(),
+                                    fgTex);
+    fBackgroundEffectMatrix.setData(uman,
+                                    GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                    stage.getCoordChangeMatrix(),
+                                    bgTex);
+
 }
 
 GrGLEffect::EffectKey GrGLBlendEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
-    const GrBlendEffect& blend = static_cast<const GrBlendEffect&>(*stage.getEffect());
-    EffectKey key =
-        GrGLEffectMatrix::GenKey(blend.getMatrix(), stage.getCoordChangeMatrix(), blend.texture(0));
-    key |= (blend.mode() << GrGLEffectMatrix::kKeyBits);
-    return key;
+    const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage);
+
+    GrTexture* fgTex = blend.texture(0);
+    GrTexture* bgTex = blend.texture(1);
+
+    EffectKey fgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(fgTex),
+                                               stage.getCoordChangeMatrix(),
+                                               fgTex);
+
+    EffectKey bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                               stage.getCoordChangeMatrix(),
+                                               bgTex);
+    bgKey <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey modeKey = blend.mode() << (2 * GrGLEffectMatrix::kKeyBits);
+
+    return  modeKey | bgKey | fgKey;
 }
 #endif
diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp
index e6e2ffd..b5a8754 100644
--- a/src/effects/SkBlurDrawLooper.cpp
+++ b/src/effects/SkBlurDrawLooper.cpp
@@ -8,10 +8,12 @@
 #include "SkBlurDrawLooper.h"
 #include "SkBlurMaskFilter.h"
 #include "SkCanvas.h"
-#include "SkFlattenableBuffers.h"
-#include "SkPaint.h"
-#include "SkMaskFilter.h"
 #include "SkColorFilter.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStringUtils.h"
 
 SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy,
                                    SkColor color, uint32_t flags)
@@ -115,3 +117,35 @@
             return false;
     }
 }
+
+#ifdef SK_DEVELOPER
+void SkBlurDrawLooper::toString(SkString* str) const {
+    str->append("SkBlurDrawLooper: ");
+
+    str->append("dx: ");
+    str->appendScalar(fDx);
+
+    str->append(" dy: ");
+    str->appendScalar(fDy);
+
+    str->append(" color: ");
+    str->appendHex(fBlurColor);
+
+    str->append(" flags: (");
+    if (kNone_BlurFlag == fBlurFlags) {
+        str->append("None");
+    } else {
+        bool needsSeparator = false;
+        SkAddFlagToString(str, SkToBool(kIgnoreTransform_BlurFlag & fBlurFlags), "IgnoreTransform",
+                          &needsSeparator);
+        SkAddFlagToString(str, SkToBool(kOverrideColor_BlurFlag & fBlurFlags), "OverrideColor",
+                          &needsSeparator);
+        SkAddFlagToString(str, SkToBool(kHighQuality_BlurFlag & fBlurFlags), "HighQuality",
+                          &needsSeparator);
+    }
+    str->append(")");
+
+    // TODO: add optional "fBlurFilter->toString(str);" when SkMaskFilter::toString is added
+    // alternatively we could cache the radius in SkBlurDrawLooper and just add it here
+}
+#endif
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index fb76269..78359d2 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -11,6 +11,7 @@
 #include "SkFlattenableBuffers.h"
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
+#include "SkImageFilterUtils.h"
 #endif
 
 SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
@@ -187,13 +188,20 @@
     return true;
 }
 
-GrTexture* SkBlurImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
+bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
 #if SK_SUPPORT_GPU
-    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(proxy, src, rect));
-    return src->getContext()->gaussianBlur(input.get(), false, rect,
-                                           fSigma.width(), fSigma.height());
+    SkBitmap input;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &input)) {
+        return false;
+    }
+    GrTexture* source = (GrTexture*) input.getTexture();
+    SkRect rect;
+    src.getBounds(&rect);
+    SkAutoTUnref<GrTexture> tex(source->getContext()->gaussianBlur(source, false, rect,
+        fSigma.width(), fSigma.height()));
+    return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result);
 #else
     SkDEBUGFAIL("Should not call in GPU-less build");
-    return NULL;
+    return false;
 #endif
 }
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index 658b0fd..99bf2d5 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -12,6 +12,14 @@
 #include "SkTemplates.h"
 #include "SkEndian.h"
 
+// scale factor for the blur radius to match the behavior of the all existing blur
+// code (both on the CPU and the GPU).  This magic constant is  1/sqrt(3).
+
+// TODO: get rid of this fudge factor and move any required fudging up into
+// the calling library
+
+#define kBlurRadiusFudgeFactor SkFloatToScalar( .57735f )
+
 #define UNROLL_SEPARABLE_LOOPS
 
 /**
@@ -844,7 +852,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// we use a local funciton to wrap the class static method to work around
+// we use a local function to wrap the class static method to work around
 // a bug in gcc98
 void SkMask_FreeImage(uint8_t* image);
 void SkMask_FreeImage(uint8_t* image) {
@@ -866,7 +874,7 @@
 
     // highQuality: use three box blur passes as a cheap way to approximate a Gaussian blur
     int passCount = (kHigh_Quality == quality) ? 3 : 1;
-    SkScalar passRadius = SkScalarDiv(radius, SkScalarSqrt(SkIntToScalar(passCount)));
+    SkScalar passRadius = (kHigh_Quality == quality) ? SkScalarMul( radius, kBlurRadiusFudgeFactor): radius;
 
     int rx = SkScalarCeil(passRadius);
     int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
@@ -1021,3 +1029,154 @@
 {
     return SkBlurMask::Blur(dst, src, radius, style, quality, margin, false);
 }
+
+/* Convolving a box with itself three times results in a piecewise
+   quadratic function:
+
+   0                              x <= -1.5
+   9/8 + 3/2 x + 1/2 x^2   -1.5 < x <= 1.5
+   3/4 - x^2                -.5 < x <= .5
+   9/8 - 3/2 x + 1/2 x^2    0.5 < x <= 1.5
+   0                        1.5 < x
+
+   To get the profile curve of the blurred step function at the rectangle
+   edge, we evaluate the indefinite integral, which is piecewise cubic:
+
+   0                                        x <= -1.5
+   5/8 + 9/8 x + 3/4 x^2 + 1/6 x^3   -1.5 < x <= -0.5
+   1/2 + 3/4 x - 1/3 x^3              -.5 < x <= .5
+   3/8 + 9/8 x - 3/4 x^2 + 1/6 x^3     .5 < x <= 1.5
+   1                                  1.5 < x
+*/
+
+static float gaussian_integral( float x ) {
+    if ( x > 1.5f ) {
+        return 0.0f;
+    }
+    if ( x < -1.5f ) {
+        return 1.0f;
+    }
+
+    float x2 = x*x;
+    float x3 = x2*x;
+
+    if ( x > 0.5f ) {
+        return 0.5625f - ( x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
+    }
+    if ( x > -0.5f ) {
+        return 0.5f - (0.75f * x - x3 / 3.0f);
+    }
+    return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
+}
+
+/*
+    compute_profile allocates and fills in an array of floating
+    point values between 0 and 255 for the profile signature of
+    a blurred half-plane with the given blur radius.  Since we're
+    going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
+    all the time, we actually fill in the profile pre-inverted
+    (already done 255-x).
+
+    The function returns the size of the array allocated for the
+    profile.  It's the responsibility of the caller to delete the
+    memory returned in profile_out.
+*/
+
+static int compute_profile( SkScalar radius, unsigned int **profile_out ) {
+    int size = SkScalarFloorToInt(radius * 3 + 1);
+    int center = size >> 1;
+
+    unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
+
+    float invr = 1.0f/radius;
+
+    profile[0] = 255;
+    for (int x = 1 ; x < size ; x++) {
+        float scaled_x = ( center - x ) * invr;
+        float gi = gaussian_integral( scaled_x );
+        profile[x] = 255 - (uint8_t) ( 255.f * gi );
+    }
+
+    *profile_out = profile;
+    return size;
+}
+
+// TODO MAYBE: Maintain a profile cache to avoid recomputing this for
+// commonly used radii.  Consider baking some of the most common blur radii
+// directly in as static data?
+
+// Implementation adapted from Michael Herf's approach:
+// http://stereopsis.com/shadowrect/
+
+bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
+                          SkScalar provided_radius, Style style, Quality quality,
+                          SkIPoint *margin) {
+    int profile_size;
+    unsigned int *profile;
+
+
+    float radius = SkScalarToFloat( SkScalarMul( provided_radius, kBlurRadiusFudgeFactor ) );
+
+    profile_size = compute_profile( radius, &profile );
+    SkAutoTDeleteArray<unsigned int> ada(profile);
+
+    int pad = (int) (radius * 1.5f + 1);
+    if (margin) {
+        margin->set( pad, pad );
+    }
+    dst->fBounds = SkIRect::MakeWH(SkScalarFloorToInt(src.width()), SkScalarFloorToInt(src.height()));
+    dst->fBounds.outset(pad, pad);
+
+    dst->fRowBytes = dst->fBounds.width();
+    dst->fFormat = SkMask::kA8_Format;
+    dst->fImage = NULL;
+
+    size_t dstSize = dst->computeImageSize();
+    if (0 == dstSize) {
+        return false;   // too big to allocate, abort
+    }
+
+    int             sw = SkScalarFloorToInt(src.width());
+    int             sh = SkScalarFloorToInt(src.height());
+
+    uint8_t*        dp = SkMask::AllocImage(dstSize);
+
+    dst->fImage = dp;
+
+    int dst_height = dst->fBounds.height();
+    int dst_width = dst->fBounds.width();
+
+    // nearest odd number less than the profile size represents the center
+    // of the (2x scaled) profile
+    int center = ( profile_size & ~1 ) - 1;
+
+    int w = sw - center;
+    int h = sh - center;
+
+    uint8_t *outptr = dp;
+
+    for (int y = 0 ; y < dst_height ; y++)
+    {
+        // time to fill in a scanline of the blurry rectangle.
+        // to avoid floating point math, everything is multiplied by
+        // 2 where needed.  This keeps things nice and integer-oriented.
+
+        int dy = abs((y << 1) - dst_height) - h; // how far are we from the original edge?
+        int oy = dy >> 1;
+        if (oy < 0) oy = 0;
+
+        unsigned int profile_y = profile[oy];
+
+        for (int x = 0 ; x < (dst_width << 1) ; x += 2) {
+            int dx = abs( x - dst_width ) - w;
+            int ox = dx >> 1;
+            if (ox < 0) ox = 0;
+
+            unsigned int maskval = SkMulDiv255Round(profile[ox], profile_y);
+
+            *(outptr++) = maskval;
+        }
+    }
+
+    return true;
+}
diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
index cfd7e67..853ba52 100644
--- a/src/effects/SkBlurMask.h
+++ b/src/effects/SkBlurMask.h
@@ -28,6 +28,10 @@
         kHigh_Quality   //!< three pass box blur (similar to gaussian)
     };
 
+    static bool BlurRect(SkMask *dst, const SkRect &src,
+                         SkScalar radius, Style style, Quality quality,
+                         SkIPoint *margin = NULL);
+
     static bool Blur(SkMask* dst, const SkMask& src,
                      SkScalar radius, Style style, Quality quality,
                      SkIPoint* margin = NULL);
@@ -41,6 +45,3 @@
 };
 
 #endif
-
-
-
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
index 2036d37..a14babc 100644
--- a/src/effects/SkColorFilters.cpp
+++ b/src/effects/SkColorFilters.cpp
@@ -111,7 +111,7 @@
 public:
     Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {}
 
-    virtual uint32_t getFlags() {
+    virtual uint32_t getFlags() const SK_OVERRIDE {
         if (SkGetPackedA32(this->getPMColor()) == 0xFF) {
             return kAlphaUnchanged_Flag | kHasFilter16_Flag;
         } else {
@@ -120,12 +120,12 @@
     }
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
+                            SkPMColor result[]) const SK_OVERRIDE {
         sk_memset32(result, this->getPMColor(), count);
     }
 
     virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) {
+                              uint16_t result[]) const SK_OVERRIDE {
         SkASSERT(this->getFlags() & kHasFilter16_Flag);
         sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
     }
@@ -144,10 +144,10 @@
 public:
     SrcOver_SkModeColorFilter(SkColor color)
             : INHERITED(color, SkXfermode::kSrcOver_Mode) {
-        fColor32Proc = NULL;
+        fColor32Proc = SkBlitRow::ColorProcFactory();
     }
 
-    virtual uint32_t getFlags() {
+    virtual uint32_t getFlags() const SK_OVERRIDE {
         if (SkGetPackedA32(this->getPMColor()) == 0xFF) {
             return kAlphaUnchanged_Flag | kHasFilter16_Flag;
         } else {
@@ -156,15 +156,12 @@
     }
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
-        if (NULL == fColor32Proc) {
-            fColor32Proc = SkBlitRow::ColorProcFactory();
-        }
+                            SkPMColor result[]) const SK_OVERRIDE {
         fColor32Proc(result, shader, count, this->getPMColor());
     }
 
     virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) {
+                              uint16_t result[]) const SK_OVERRIDE {
         SkASSERT(this->getFlags() & kHasFilter16_Flag);
         sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
     }
@@ -173,7 +170,9 @@
 
 protected:
     SrcOver_SkModeColorFilter(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer), fColor32Proc(NULL) {}
+        : INHERITED(buffer) {
+            fColor32Proc = SkBlitRow::ColorProcFactory();
+        }
 
 private:
 
@@ -361,7 +360,7 @@
         SkASSERT(SkColorGetR(mul) == SkColorGetB(mul));
     }
 
-    virtual uint32_t getFlags() const {
+    virtual uint32_t getFlags() const SK_OVERRIDE {
         return this->INHERITED::getFlags() | (kAlphaUnchanged_Flag | kHasFilter16_Flag);
     }
 
@@ -491,4 +490,3 @@
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingColorFilter_NoPin)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSimpleColorFilter)
 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
diff --git a/src/effects/SkColorMatrix.cpp b/src/effects/SkColorMatrix.cpp
index 98e2865..d6cb940 100644
--- a/src/effects/SkColorMatrix.cpp
+++ b/src/effects/SkColorMatrix.cpp
@@ -159,4 +159,3 @@
     setrow(fMat + 10, SK_Scalar1, kU2B, 0);
     fMat[18] = SK_Scalar1;
 }
-
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index e460325..1582464 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -325,17 +325,60 @@
 
 class ColorMatrixEffect : public GrEffect {
 public:
-    static const char* Name() { return "Color Matrix"; }
+    static GrEffectRef* Create(const SkColorMatrix& matrix) {
+        AutoEffectUnref effect(SkNEW_ARGS(ColorMatrixEffect, (matrix)));
+        return CreateEffectRef(effect);
+    }
 
-    ColorMatrixEffect(const SkColorMatrix& matrix) : GrEffect(0), fMatrix(matrix) {}
+    static const char* Name() { return "Color Matrix"; }
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
     }
 
-    virtual bool isEqual(const GrEffect& s) const {
-        const ColorMatrixEffect& cme = static_cast<const ColorMatrixEffect&>(s);
-        return cme.fMatrix == fMatrix;
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
+        // type flags it might be worth checking the other components.
+
+        // The matrix is defined such the 4th row determines the output alpha. The first four
+        // columns of that row multiply the input r, g, b, and a, respectively, and the last column
+        // is the "translation".
+        static const ValidComponentFlags kRGBAFlags[] = {
+            kR_ValidComponentFlag,
+            kG_ValidComponentFlag,
+            kB_ValidComponentFlag,
+            kA_ValidComponentFlag
+        };
+        static const int kShifts[] = {
+            GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
+        };
+        enum {
+            kAlphaRowStartIdx = 15,
+            kAlphaRowTranslateIdx = 19,
+        };
+
+        SkScalar outputA = 0;
+        for (int i = 0; i < 4; ++i) {
+            // If any relevant component of the color to be passed through the matrix is non-const
+            // then we can't know the final result.
+            if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
+                if (!(*validFlags & kRGBAFlags[i])) {
+                    *validFlags = 0;
+                    return;
+                } else {
+                    uint32_t component = (*color >> kShifts[i]) & 0xFF;
+                    outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
+                }
+            }
+        }
+        outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
+        *validFlags = kA_ValidComponentFlag;
+        // We pin the color to [0,1]. This would happen to the *final* color output from the frag
+        // shader but currently the effect does not pin its own output. So in the case of over/
+        // underflow this may deviate from the actual result. Maybe the effect should pin its
+        // result if the matrix could over/underflow for any component?
+        *color = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
     }
 
     GR_DECLARE_EFFECT_TEST;
@@ -346,7 +389,7 @@
         static EffectKey GenKey(const GrEffectStage&, const GrGLCaps&) { return 0; }
 
         GLEffect(const GrBackendEffectFactory& factory,
-                 const GrEffect& effect)
+                 const GrEffectRef& effect)
         : INHERITED(factory)
         , fMatrixHandle(GrGLUniformManager::kInvalidUniformHandle)
         , fVectorHandle(GrGLUniformManager::kInvalidUniformHandle) {}
@@ -382,8 +425,7 @@
 
         virtual void setData(const GrGLUniformManager& uniManager,
                              const GrEffectStage& stage) SK_OVERRIDE {
-            const ColorMatrixEffect& cme =
-                static_cast<const ColorMatrixEffect&>(*stage.getEffect());
+            const ColorMatrixEffect& cme = GetEffectFromStage<ColorMatrixEffect>(stage);
             const float* m = cme.fMatrix.fMat;
             // The GL matrix is transposed from SkColorMatrix.
             GrGLfloat mt[]  = {
@@ -406,6 +448,13 @@
     };
 
 private:
+    ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {}
+
+    virtual bool onIsEqual(const GrEffect& s) const {
+        const ColorMatrixEffect& cme = CastEffect<ColorMatrixEffect>(s);
+        return cme.fMatrix == fMatrix;
+    }
+
     SkColorMatrix fMatrix;
 
     typedef GrGLEffect INHERITED;
@@ -413,18 +462,18 @@
 
 GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
 
-GrEffect* ColorMatrixEffect::TestCreate(SkRandom* random,
-                                        GrContext*,
-                                        GrTexture* dummyTextures[2]) {
+GrEffectRef* ColorMatrixEffect::TestCreate(SkRandom* random,
+                                           GrContext*,
+                                           GrTexture* dummyTextures[2]) {
     SkColorMatrix colorMatrix;
     for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
         colorMatrix.fMat[i] = random->nextSScalar1();
     }
-    return SkNEW_ARGS(ColorMatrixEffect, (colorMatrix));
+    return ColorMatrixEffect::Create(colorMatrix);
 }
 
-GrEffect* SkColorMatrixFilter::asNewEffect(GrContext*) const {
-    return SkNEW_ARGS(ColorMatrixEffect, (fMatrix));
+GrEffectRef* SkColorMatrixFilter::asNewEffect(GrContext*) const {
+    return ColorMatrixEffect::Create(fMatrix);
 }
 
 #endif
diff --git a/src/effects/SkCornerPathEffect.cpp b/src/effects/SkCornerPathEffect.cpp
index b63045d..1cf797a 100644
--- a/src/effects/SkCornerPathEffect.cpp
+++ b/src/effects/SkCornerPathEffect.cpp
@@ -31,7 +31,7 @@
 }
 
 bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                    SkStrokeRec*) const {
+                                    SkStrokeRec*, const SkRect*) const {
     if (0 == fRadius) {
         return false;
     }
diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp
index 58d746b..1032270 100644
--- a/src/effects/SkDashPathEffect.cpp
+++ b/src/effects/SkDashPathEffect.cpp
@@ -88,10 +88,81 @@
     sk_free(fIntervals);
 }
 
+static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
+    SkScalar radius = SkScalarHalf(rec.getWidth());
+    if (0 == radius) {
+        radius = SK_Scalar1;    // hairlines
+    }
+    if (SkPaint::kMiter_Join == rec.getJoin()) {
+        radius = SkScalarMul(radius, rec.getMiter());
+    }
+    rect->outset(radius, radius);
+}
+
+// Only handles lines for now. If returns true, dstPath is the new (smaller)
+// path. If returns false, then dstPath parameter is ignored.
+static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec,
+                      const SkRect* cullRect, SkScalar intervalLength,
+                      SkPath* dstPath) {
+    if (NULL == cullRect) {
+        return false;
+    }
+
+    SkPoint pts[2];
+    if (!srcPath.isLine(pts)) {
+        return false;
+    }
+
+    SkRect bounds = *cullRect;
+    outset_for_stroke(&bounds, rec);
+
+    SkScalar dx = pts[1].x() - pts[0].x();
+    SkScalar dy = pts[1].y() - pts[0].y();
+
+    // just do horizontal lines for now (lazy)
+    if (dy) {
+        return false;
+    }
+
+    SkScalar minX = pts[0].fX;
+    SkScalar maxX = pts[1].fX;
+
+    if (maxX < bounds.fLeft || minX > bounds.fRight) {
+        return false;
+    }
+
+    if (dx < 0) {
+        SkTSwap(minX, maxX);
+    }
+
+    // Now we actually perform the chop, removing the excess to the left and
+    // right of the bounds (keeping our new line "in phase" with the dash,
+    // hence the (mod intervalLength).
+
+    if (minX < bounds.fLeft) {
+        minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX,
+                                          intervalLength);
+    }
+    if (maxX > bounds.fRight) {
+        maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight,
+                                           intervalLength);
+    }
+
+    SkASSERT(maxX >= minX);
+    if (dx < 0) {
+        SkTSwap(minX, maxX);
+    }
+    pts[0].fX = minX;
+    pts[1].fX = maxX;
+
+    dstPath->moveTo(pts[0]);
+    dstPath->lineTo(pts[1]);
+    return true;
+}
+
 class SpecialLineRec {
 public:
     bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
-              SkScalar pathLength,
               int intervalCount, SkScalar intervalLength) {
         if (rec->isHairlineStyle() || !src.isLine(fPts)) {
             return false;
@@ -102,6 +173,8 @@
             return false;
         }
 
+        SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]);
+
         fTangent = fPts[1] - fPts[0];
         if (fTangent.isZero()) {
             return false;
@@ -156,19 +229,30 @@
 };
 
 bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                  SkStrokeRec* rec) const {
+                              SkStrokeRec* rec, const SkRect* cullRect) const {
+
+#ifdef SK_IGNORE_LARGE_DASH_OPT
+    cullRect = NULL;
+#endif
+
     // we do nothing if the src wants to be filled, or if our dashlength is 0
     if (rec->isFillStyle() || fInitialDashLength < 0) {
         return false;
     }
 
-    SkPathMeasure   meas(src, false);
     const SkScalar* intervals = fIntervals;
     SkScalar        dashCount = 0;
 
+    SkPath cullPathStorage;
+    const SkPath* srcPtr = &src;
+    if (cull_path(src, *rec, cullRect, fIntervalLength, &cullPathStorage)) {
+        srcPtr = &cullPathStorage;
+    }
+
     SpecialLineRec lineRec;
-    const bool specialLine = lineRec.init(src, dst, rec, meas.getLength(),
-                                          fCount >> 1, fIntervalLength);
+    bool specialLine = lineRec.init(*srcPtr, dst, rec, fCount >> 1, fIntervalLength);
+
+    SkPathMeasure   meas(*srcPtr, false);
 
     do {
         bool        skipFirstSegment = meas.isClosed();
@@ -256,7 +340,8 @@
 bool SkDashPathEffect::asPoints(PointData* results,
                                 const SkPath& src,
                                 const SkStrokeRec& rec,
-                                const SkMatrix& matrix) const {
+                                const SkMatrix& matrix,
+                                const SkRect* cullRect) const {
     // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out
     if (fInitialDashLength < 0 || 0 >= rec.getWidth()) {
         return false;
@@ -320,6 +405,7 @@
 
     if (NULL != results) {
         results->fFlags = 0;
+        SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength);
 
         if (SkPaint::kRound_Cap == rec.getCap()) {
             results->fFlags |= PointData::kCircles_PointFlag;
@@ -327,23 +413,21 @@
 
         results->fNumPoints = 0;
         SkScalar len2 = length;
-        bool partialFirst = false;
-        if (fInitialDashLength > 0 || 0 == fInitialDashIndex) {
-            SkASSERT(len2 >= fInitialDashLength);
+        if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
+            SkASSERT(len2 >= clampedInitialDashLength);
             if (0 == fInitialDashIndex) {
-                if (fInitialDashLength > 0) {
-                    partialFirst = true;
-                    if (fInitialDashLength >= fIntervals[0]) {
+                if (clampedInitialDashLength > 0) {
+                    if (clampedInitialDashLength >= fIntervals[0]) {
                         ++results->fNumPoints;  // partial first dash
                     }
-                    len2 -= fInitialDashLength;
+                    len2 -= clampedInitialDashLength;
                 }
                 len2 -= fIntervals[1];  // also skip first space
                 if (len2 < 0) {
                     len2 = 0;
                 }
             } else {
-                len2 -= fInitialDashLength; // skip initial partial empty
+                len2 -= clampedInitialDashLength; // skip initial partial empty
             }
         }
         int numMidPoints = SkScalarFloorToInt(SkScalarDiv(len2, fIntervalLength));
@@ -364,24 +448,24 @@
         SkScalar    distance = 0;
         int         curPt = 0;
 
-        if (fInitialDashLength > 0 || 0 == fInitialDashIndex) {
-            SkASSERT(fInitialDashLength <= length);
+        if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
+            SkASSERT(clampedInitialDashLength <= length);
 
             if (0 == fInitialDashIndex) {
-                if (fInitialDashLength > 0) {
+                if (clampedInitialDashLength > 0) {
                     // partial first block
                     SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
-                    SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(fInitialDashLength));
-                    SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(fInitialDashLength));
+                    SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(clampedInitialDashLength));
+                    SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(clampedInitialDashLength));
                     SkScalar halfWidth, halfHeight;
                     if (isXAxis) {
-                        halfWidth = SkScalarHalf(fInitialDashLength);
+                        halfWidth = SkScalarHalf(clampedInitialDashLength);
                         halfHeight = SkScalarHalf(rec.getWidth());
                     } else {
                         halfWidth = SkScalarHalf(rec.getWidth());
-                        halfHeight = SkScalarHalf(fInitialDashLength);
+                        halfHeight = SkScalarHalf(clampedInitialDashLength);
                     }
-                    if (fInitialDashLength < fIntervals[0]) {
+                    if (clampedInitialDashLength < fIntervals[0]) {
                         // This one will not be like the others
                         results->fFirst.addRect(x - halfWidth, y - halfHeight,
                                                 x + halfWidth, y + halfHeight);
@@ -391,12 +475,12 @@
                         ++curPt;
                     }
 
-                    distance += fInitialDashLength;
+                    distance += clampedInitialDashLength;
                 }
 
                 distance += fIntervals[1];  // skip over the next blank block too
             } else {
-                distance += fInitialDashLength;
+                distance += clampedInitialDashLength;
             }
         }
 
diff --git a/src/effects/SkDiscretePathEffect.cpp b/src/effects/SkDiscretePathEffect.cpp
index a936be9..2c95208 100644
--- a/src/effects/SkDiscretePathEffect.cpp
+++ b/src/effects/SkDiscretePathEffect.cpp
@@ -26,7 +26,7 @@
 }
 
 bool SkDiscretePathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                      SkStrokeRec* rec) const {
+                                      SkStrokeRec* rec, const SkRect*) const {
     bool doFill = rec->isFillStyle();
 
     SkPathMeasure   meas(src, doFill);
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
new file mode 100644
index 0000000..f652bb2
--- /dev/null
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDisplacementMapEffect.h"
+#include "SkFlattenableBuffers.h"
+#include "SkUnPreMultiply.h"
+#include "SkColorPriv.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "GrTBackendEffectFactory.h"
+#include "SkImageFilterUtils.h"
+#endif
+
+namespace {
+
+template<SkDisplacementMapEffect::ChannelSelectorType type>
+uint32_t getValue(SkColor, const SkUnPreMultiply::Scale*) {
+    SkASSERT(!"Unknown channel selector");
+    return 0;
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kR_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedR32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kG_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedG32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kB_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedB32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kA_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale*) {
+    return SkGetPackedA32(l);
+}
+
+template<SkDisplacementMapEffect::ChannelSelectorType typeX,
+         SkDisplacementMapEffect::ChannelSelectorType typeY>
+void computeDisplacement(SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    static const SkScalar Inv8bit = SkScalarDiv(SK_Scalar1, SkFloatToScalar(255.0f));
+    static const SkScalar Half8bit = SkFloatToScalar(255.0f * 0.5f);
+    const int dstW = displ->width();
+    const int dstH = displ->height();
+    const int srcW = src->width();
+    const int srcH = src->height();
+    const SkScalar scaleX = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstW)), Inv8bit);
+    const SkScalar scaleY = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstH)), Inv8bit);
+    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
+    for (int y = 0; y < dstH; ++y) {
+        const SkPMColor* displPtr = displ->getAddr32(0, y);
+        SkPMColor* dstPtr = dst->getAddr32(0, y);
+        for (int x = 0; x < dstW; ++x, ++displPtr, ++dstPtr) {
+            const SkScalar displX =
+                SkScalarMul(scaleX, SkIntToScalar(getValue<typeX>(*displPtr, table))-Half8bit);
+            const SkScalar displY =
+                SkScalarMul(scaleY, SkIntToScalar(getValue<typeY>(*displPtr, table))-Half8bit);
+            const int coordX = x + SkScalarRoundToInt(displX);
+            const int coordY = y + SkScalarRoundToInt(displY);
+            *dstPtr = ((coordX < 0) || (coordX >= srcW) || (coordY < 0) || (coordY >= srcH)) ?
+                      0 : *(src->getAddr32(coordX, coordY));
+        }
+    }
+}
+
+template<SkDisplacementMapEffect::ChannelSelectorType typeX>
+void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    switch (yChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kR_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kG_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kB_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kA_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown Y channel selector");
+    }
+}
+
+void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                         SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    switch (xChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kR_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kG_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kB_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kA_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown X channel selector");
+    }
+}
+
+} // end namespace
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDisplacementMapEffect::SkDisplacementMapEffect(ChannelSelectorType xChannelSelector,
+                                                 ChannelSelectorType yChannelSelector,
+                                                 SkScalar scale,
+                                                 SkImageFilter* displacement,
+                                                 SkImageFilter* color)
+  : INHERITED(displacement, color)
+  , fXChannelSelector(xChannelSelector)
+  , fYChannelSelector(yChannelSelector)
+  , fScale(scale)
+{
+}
+
+SkDisplacementMapEffect::~SkDisplacementMapEffect() {
+}
+
+SkDisplacementMapEffect::SkDisplacementMapEffect(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer)
+{
+    fXChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
+    fYChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
+    fScale            = buffer.readScalar();
+}
+
+void SkDisplacementMapEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt((int) fXChannelSelector);
+    buffer.writeInt((int) fYChannelSelector);
+    buffer.writeScalar(fScale);
+}
+
+bool SkDisplacementMapEffect::onFilterImage(Proxy* proxy,
+                                            const SkBitmap& src,
+                                            const SkMatrix& ctm,
+                                            SkBitmap* dst,
+                                            SkIPoint* offset) {
+    SkBitmap displ, color = src;
+    SkImageFilter* colorInput = getColorInput();
+    SkImageFilter* displacementInput = getDisplacementInput();
+    SkASSERT(NULL != displacementInput);
+    if ((colorInput && !colorInput->filterImage(proxy, src, ctm, &color, offset)) ||
+        !displacementInput->filterImage(proxy, src, ctm, &displ, offset)) {
+        return false;
+    }
+    if ((displ.config() != SkBitmap::kARGB_8888_Config) ||
+        (color.config() != SkBitmap::kARGB_8888_Config)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp_displacement(displ), alp_color(color);
+    if (!displ.getPixels() || !color.getPixels()) {
+        return false;
+    }
+    dst->setConfig(displ.config(), displ.width(), displ.height());
+    dst->allocPixels();
+    if (!dst->getPixels()) {
+        return false;
+    }
+
+    computeDisplacement(fXChannelSelector, fYChannelSelector, fScale, dst, &displ, &color);
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+class GrGLDisplacementMapEffect : public GrGLEffect {
+public:
+    GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory,
+                              const GrEffectRef& effect);
+    virtual ~GrGLDisplacementMapEffect();
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
+
+private:
+    SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
+    SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
+    GrGLEffectMatrix fDisplacementEffectMatrix;
+    GrGLEffectMatrix fColorEffectMatrix;
+    GrGLUniformManager::UniformHandle fScaleUni;
+    GrGLUniformManager::UniformHandle fYSignColor;
+    GrGLUniformManager::UniformHandle fYSignDispl;
+
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrDisplacementMapEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                               SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                               SkScalar scale, GrTexture* displacement, GrTexture* color) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrDisplacementMapEffect, (xChannelSelector,
+                                                                    yChannelSelector,
+                                                                    scale,
+                                                                    displacement,
+                                                                    color)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrDisplacementMapEffect();
+
+    const GrBackendEffectFactory& getFactory() const;
+    SkDisplacementMapEffect::ChannelSelectorType xChannelSelector() const
+        { return fXChannelSelector; }
+    SkDisplacementMapEffect::ChannelSelectorType yChannelSelector() const
+        { return fYChannelSelector; }
+    SkScalar scale() const { return fScale; }
+
+    typedef GrGLDisplacementMapEffect GLEffect;
+    static const char* Name() { return "DisplacementMap"; }
+
+    void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                            SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                            SkScalar scale, GrTexture* displacement, GrTexture* color);
+
+    GR_DECLARE_EFFECT_TEST;
+
+    GrTextureAccess             fDisplacementAccess;
+    GrTextureAccess             fColorAccess;
+    SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
+    SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
+    SkScalar fScale;
+
+    typedef GrEffect INHERITED;
+};
+
+bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap colorBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getColorInput(), proxy, src, &colorBM)) {
+        return false;
+    }
+    GrTexture* color = (GrTexture*) colorBM.getTexture();
+    SkBitmap displacementBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getDisplacementInput(), proxy, src, &displacementBM)) {
+        return false;
+    }
+    GrTexture* displacement = (GrTexture*) displacementBM.getTexture();
+    GrContext* context = color->getContext();
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = src.width();
+    desc.fHeight = src.height();
+    desc.fConfig = kSkia8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    SkAutoTUnref<GrTexture> dst(ast.detach());
+
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(
+        GrDisplacementMapEffect::Create(fXChannelSelector,
+                                        fYChannelSelector,
+                                        fScale,
+                                        displacement,
+                                        color))->unref();
+    SkRect srcRect;
+    src.getBounds(&srcRect);
+    context->drawRect(paint, srcRect);
+    return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDisplacementMapEffect::GrDisplacementMapEffect(
+                             SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                             SkScalar scale,
+                             GrTexture* displacement,
+                             GrTexture* color)
+    : fDisplacementAccess(displacement)
+    , fColorAccess(color)
+    , fXChannelSelector(xChannelSelector)
+    , fYChannelSelector(yChannelSelector)
+    , fScale(scale) {
+    this->addTextureAccess(&fDisplacementAccess);
+    this->addTextureAccess(&fColorAccess);
+}
+
+GrDisplacementMapEffect::~GrDisplacementMapEffect() {
+}
+
+bool GrDisplacementMapEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrDisplacementMapEffect& s = CastEffect<GrDisplacementMapEffect>(sBase);
+    return fDisplacementAccess.getTexture() == s.fDisplacementAccess.getTexture() &&
+           fColorAccess.getTexture() == s.fColorAccess.getTexture() &&
+           fXChannelSelector == s.fXChannelSelector &&
+           fYChannelSelector == s.fYChannelSelector &&
+           fScale == s.fScale;
+}
+
+const GrBackendEffectFactory& GrDisplacementMapEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrDisplacementMapEffect>::getInstance();
+}
+
+void GrDisplacementMapEffect::getConstantColorComponents(GrColor* color,
+                                                         uint32_t* validFlags) const {
+    // Any displacement offset bringing a pixel out of bounds will output a color of (0,0,0,0),
+    // so the only way we'd get a constant alpha is if the input color image has a constant alpha
+    // and no displacement offset push any texture coordinates out of bounds OR if the constant
+    // alpha is 0. Since this isn't trivial to compute at this point, let's assume the output is
+    // not of constant color when a displacement effect is applied.
+    *validFlags = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrDisplacementMapEffect);
+
+GrEffectRef* GrDisplacementMapEffect::TestCreate(SkRandom* random,
+                                                 GrContext* context,
+                                                 GrTexture* textures[]) {
+    int texIdxDispl = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                           GrEffectUnitTest::kAlphaTextureIdx;
+    int texIdxColor = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                           GrEffectUnitTest::kAlphaTextureIdx;
+    static const int kMaxComponent = 4;
+    SkDisplacementMapEffect::ChannelSelectorType xChannelSelector =
+        static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
+        random->nextRangeU(1, kMaxComponent));
+    SkDisplacementMapEffect::ChannelSelectorType yChannelSelector =
+        static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
+        random->nextRangeU(1, kMaxComponent));
+    SkScalar scale = random->nextUScalar1();
+
+    return GrDisplacementMapEffect::Create(xChannelSelector, yChannelSelector, scale,
+                                           textures[texIdxDispl], textures[texIdxColor]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLDisplacementMapEffect::GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory,
+                                                     const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fXChannelSelector(CastEffect<GrDisplacementMapEffect>(effect).xChannelSelector())
+    , fYChannelSelector(CastEffect<GrDisplacementMapEffect>(effect).yChannelSelector()) {
+}
+
+GrGLDisplacementMapEffect::~GrGLDisplacementMapEffect() {
+}
+
+void GrGLDisplacementMapEffect::emitCode(GrGLShaderBuilder* builder,
+                               const GrEffectStage&,
+                               EffectKey key,
+                               const char* vertexCoords,
+                               const char* outputColor,
+                               const char* inputColor,
+                               const TextureSamplerArray& samplers) {
+    fScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                    kVec2f_GrSLType, "Scale");
+    const char* scaleUni = builder->getUniformCStr(fScaleUni);
+
+    const char* dCoordsIn;
+    GrSLType dCoordsType = fDisplacementEffectMatrix.emitCode(
+                                builder, key, vertexCoords, &dCoordsIn, NULL, "DISPL");
+    const char* cCoordsIn;
+    GrSLType cCoordsType = fColorEffectMatrix.emitCode(
+                                builder, key, vertexCoords, &cCoordsIn, NULL, "COLOR");
+
+    SkString* code = &builder->fFSCode;
+    const char* dColor = "dColor";
+    const char* cCoords = "cCoords";
+    const char* nearZero = "1e-6"; // Since 6.10352e−5 is the smallest half float, use
+                                   // a number smaller than that to approximate 0, but
+                                   // leave room for 32-bit float GPU rounding errors.
+
+    code->appendf("\t\tvec4 %s = ", dColor);
+    builder->appendTextureLookup(code, samplers[0], dCoordsIn, dCoordsType);
+    code->append(";\n");
+
+    // Unpremultiply the displacement
+    code->appendf("\t\t%s.rgb = (%s.a < %s) ? vec3(0.0) : clamp(%s.rgb / %s.a, 0.0, 1.0);",
+                  dColor, dColor, nearZero, dColor, dColor);
+
+    code->appendf("\t\tvec2 %s = %s + %s*(%s.",
+                  cCoords, cCoordsIn, scaleUni, dColor);
+
+    switch (fXChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        code->append("r");
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        code->append("g");
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        code->append("b");
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        code->append("a");
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown X channel selector");
+    }
+
+    switch (fYChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        code->append("r");
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        code->append("g");
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        code->append("b");
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        code->append("a");
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown Y channel selector");
+    }
+    code->append("-vec2(0.5));\t\t");
+
+    // FIXME : This can be achieved with a "clamp to border" texture repeat mode and
+    //         a 0 border color instead of computing if cCoords is out of bounds here.
+    code->appendf(
+        "%s = any(greaterThan(vec4(vec2(0.0), %s), vec4(%s, vec2(1.0)))) ? vec4(0.0) : ",
+        outputColor, cCoords, cCoords);
+    builder->appendTextureLookup(code, samplers[1], cCoords, cCoordsType);
+    code->append(";\n");
+}
+
+void GrGLDisplacementMapEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrDisplacementMapEffect& displacementMap = GetEffectFromStage<GrDisplacementMapEffect>(stage);
+    GrTexture* displTex = displacementMap.texture(0);
+    GrTexture* colorTex = displacementMap.texture(1);
+    fDisplacementEffectMatrix.setData(uman,
+                                     GrEffect::MakeDivByTextureWHMatrix(displTex),
+                                     stage.getCoordChangeMatrix(),
+                                     displTex);
+    fColorEffectMatrix.setData(uman,
+                               GrEffect::MakeDivByTextureWHMatrix(colorTex),
+                               stage.getCoordChangeMatrix(),
+                               colorTex);
+
+    uman.set2f(fScaleUni, SkScalarToFloat(displacementMap.scale()),
+                colorTex->origin() == kTopLeft_GrSurfaceOrigin ?
+                SkScalarToFloat(displacementMap.scale()) :
+                SkScalarToFloat(-displacementMap.scale()));
+}
+
+GrGLEffect::EffectKey GrGLDisplacementMapEffect::GenKey(const GrEffectStage& stage,
+                                                        const GrGLCaps&) {
+    const GrDisplacementMapEffect& displacementMap =
+        GetEffectFromStage<GrDisplacementMapEffect>(stage);
+
+    GrTexture* displTex = displacementMap.texture(0);
+    GrTexture* colorTex = displacementMap.texture(1);
+
+    EffectKey displKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(displTex),
+                                                  stage.getCoordChangeMatrix(),
+                                                  displTex);
+
+    EffectKey colorKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(colorTex),
+                                                  stage.getCoordChangeMatrix(),
+                                                  colorTex);
+
+    colorKey <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey xKey = displacementMap.xChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits);
+    EffectKey yKey = displacementMap.yChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits +
+                                                            SkDisplacementMapEffect::kKeyBits);
+
+    return xKey | yKey | displKey | colorKey;
+}
+#endif
diff --git a/src/effects/SkEmbossMask.cpp b/src/effects/SkEmbossMask.cpp
index 300494b..32e9b23 100644
--- a/src/effects/SkEmbossMask.cpp
+++ b/src/effects/SkEmbossMask.cpp
@@ -161,5 +161,3 @@
         prev_row = rowBytes;
     }
 }
-
-
diff --git a/src/effects/SkEmbossMask.h b/src/effects/SkEmbossMask.h
index 15e2474..7053be2 100644
--- a/src/effects/SkEmbossMask.h
+++ b/src/effects/SkEmbossMask.h
@@ -18,4 +18,3 @@
 };
 
 #endif
-
diff --git a/src/effects/SkImageFilterUtils.cpp b/src/effects/SkImageFilterUtils.cpp
new file mode 100644
index 0000000..2dfb33d
--- /dev/null
+++ b/src/effects/SkImageFilterUtils.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013 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 "SkMatrix.h"
+
+#if SK_SUPPORT_GPU
+#include "GrTexture.h"
+#include "SkImageFilterUtils.h"
+#include "SkBitmap.h"
+#include "SkGrPixelRef.h"
+#include "SkGr.h"
+
+bool SkImageFilterUtils::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) {
+    SkASSERT(texture->config() == kSkia8888_GrPixelConfig);
+    result->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
+    return true;
+}
+
+bool SkImageFilterUtils::GetInputResultGPU(SkImageFilter* filter, SkImageFilter::Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    if (!filter) {
+        *result = src;
+        return true;
+    } else if (filter->canFilterImageGPU()) {
+        return filter->filterImageGPU(proxy, src, result);
+    } else {
+        SkIPoint offset;
+        if (filter->filterImage(proxy, src, SkMatrix(), result, &offset)) {
+            if (!result->getTexture()) {
+                GrContext* context = ((GrTexture *) src.getTexture())->getContext();
+                GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context,
+                    *result, NULL);
+                result->setPixelRef(new SkGrPixelRef(resultTex))->unref();
+                GrUnlockAndUnrefCachedBitmapTexture(resultTex);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
+#endif
diff --git a/src/effects/SkKernel33MaskFilter.cpp b/src/effects/SkKernel33MaskFilter.cpp
index 0f198cd..9c42b39 100644
--- a/src/effects/SkKernel33MaskFilter.cpp
+++ b/src/effects/SkKernel33MaskFilter.cpp
@@ -113,8 +113,7 @@
 
 SkKernel33MaskFilter::SkKernel33MaskFilter(SkFlattenableReadBuffer& rb)
         : SkKernel33ProcMaskFilter(rb) {
-    const uint32_t count = rb.readIntArray(&fKernel[0][0]);
+    SkDEBUGCODE(const uint32_t count = )rb.readIntArray(&fKernel[0][0]);
     SkASSERT(9 == count);
     fShift = rb.readInt();
 }
-
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index c27908f..0ffc08e 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -10,6 +10,8 @@
 #include "SkFlattenableBuffers.h"
 #include "SkLayerDrawLooper.h"
 #include "SkPaint.h"
+#include "SkString.h"
+#include "SkStringUtils.h"
 #include "SkUnPreMultiply.h"
 
 SK_DEFINE_INST_COUNT(SkLayerDrawLooper)
@@ -245,3 +247,99 @@
     }
 #endif
 }
+
+#ifdef SK_DEVELOPER
+void SkLayerDrawLooper::toString(SkString* str) const {
+    str->appendf("SkLayerDrawLooper (%d): ", fCount);
+
+    Rec* rec = fRecs;
+    for (int i = 0; i < fCount; i++) {
+        str->appendf("%d: ", i);
+
+        str->append("flagsMask: (");
+        if (0 == rec->fInfo.fFlagsMask) {
+            str->append("None");
+        } else {
+            bool needSeparator = false;
+            SkAddFlagToString(str, SkToBool(SkPaint::kAntiAlias_Flag & rec->fInfo.fFlagsMask),
+                              "AntiAlias", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kFilterBitmap_Flag & rec->fInfo.fFlagsMask),
+                              "FilterBitmap", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kDither_Flag & rec->fInfo.fFlagsMask),
+                              "Dither", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kUnderlineText_Flag & rec->fInfo.fFlagsMask),
+                              "UnderlineText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kStrikeThruText_Flag & rec->fInfo.fFlagsMask),
+                              "StrikeThruText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kFakeBoldText_Flag & rec->fInfo.fFlagsMask),
+                              "FakeBoldText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kLinearText_Flag & rec->fInfo.fFlagsMask),
+                              "LinearText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kSubpixelText_Flag & rec->fInfo.fFlagsMask),
+                              "SubpixelText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kDevKernText_Flag & rec->fInfo.fFlagsMask),
+                              "DevKernText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kLCDRenderText_Flag & rec->fInfo.fFlagsMask),
+                              "LCDRenderText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kEmbeddedBitmapText_Flag & rec->fInfo.fFlagsMask),
+                              "EmbeddedBitmapText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kAutoHinting_Flag & rec->fInfo.fFlagsMask),
+                              "Autohinted", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kVerticalText_Flag & rec->fInfo.fFlagsMask),
+                              "VerticalText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kGenA8FromLCD_Flag & rec->fInfo.fFlagsMask),
+                              "GenA8FromLCD", &needSeparator);
+        }
+        str->append(") ");
+
+        str->append("paintBits: (");
+        if (0 == rec->fInfo.fPaintBits) {
+            str->append("None");
+        } else if (kEntirePaint_Bits == rec->fInfo.fPaintBits) {
+            str->append("EntirePaint");
+        } else {
+            bool needSeparator = false;
+            SkAddFlagToString(str, SkToBool(kStyle_Bit & rec->fInfo.fPaintBits), "Style",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kTextSkewX_Bit & rec->fInfo.fPaintBits), "TextSkewX",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kPathEffect_Bit & rec->fInfo.fPaintBits), "PathEffect",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kMaskFilter_Bit & rec->fInfo.fPaintBits), "MaskFilter",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kShader_Bit & rec->fInfo.fPaintBits), "Shader",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kColorFilter_Bit & rec->fInfo.fPaintBits), "ColorFilter",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kXfermode_Bit & rec->fInfo.fPaintBits), "Xfermode",
+                              &needSeparator);
+        }
+        str->append(") ");
+
+        static const char* gModeStrings[SkXfermode::kLastMode+1] = {
+            "kClear", "kSrc", "kDst", "kSrcOver", "kDstOver", "kSrcIn", "kDstIn",
+            "kSrcOut", "kDstOut", "kSrcATop", "kDstATop", "kXor", "kPlus",
+            "kMultiply", "kScreen", "kOverlay", "kDarken", "kLighten", "kColorDodge",
+            "kColorBurn", "kHardLight", "kSoftLight", "kDifference", "kExclusion"
+        };
+
+        str->appendf("mode: %s ", gModeStrings[rec->fInfo.fColorMode]);
+
+        str->append("offset: (");
+        str->appendScalar(rec->fInfo.fOffset.fX);
+        str->append(", ");
+        str->appendScalar(rec->fInfo.fOffset.fY);
+        str->append(") ");
+
+        str->append("postTranslate: ");
+        if (rec->fInfo.fPostTranslate) {
+            str->append("true ");
+        } else {
+            str->append("false ");
+        }
+
+        // TODO: add "rec->fPaint.toString(str);" when SkPaint::toString is added
+        rec = rec->fNext;
+    }
+}
+#endif
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 48f16b3..498c8e0 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -264,7 +264,7 @@
                                  SkScalar kd, SkImageFilter* input);
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter)
 
-    virtual bool asNewEffect(GrEffect** effect, GrTexture*) const SK_OVERRIDE;
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture*) const SK_OVERRIDE;
     SkScalar kd() const { return fKD; }
 
 protected:
@@ -284,7 +284,7 @@
     SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, SkImageFilter* input);
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter)
 
-    virtual bool asNewEffect(GrEffect** effect, GrTexture*) const SK_OVERRIDE;
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture*) const SK_OVERRIDE;
     SkScalar ks() const { return fKS; }
     SkScalar shininess() const { return fShininess; }
 
@@ -307,10 +307,18 @@
     GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale);
     virtual ~GrLightingEffect();
 
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
-
     const SkLight* light() const { return fLight; }
     SkScalar surfaceScale() const { return fSurfaceScale; }
+
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // lighting shaders are complicated. We just throw up our hands.
+        *validFlags = 0;
+    }
+
+protected:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
 private:
     typedef GrSingleTextureEffect INHERITED;
     const SkLight* fLight;
@@ -319,19 +327,32 @@
 
 class GrDiffuseLightingEffect : public GrLightingEffect {
 public:
-    GrDiffuseLightingEffect(GrTexture* texture,
-                            const SkLight* light,
-                            SkScalar surfaceScale,
-                            SkScalar kd);
+    static GrEffectRef* Create(GrTexture* texture,
+                               const SkLight* light,
+                               SkScalar surfaceScale,
+                               SkScalar kd) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrDiffuseLightingEffect, (texture,
+                                                                    light,
+                                                                    surfaceScale,
+                                                                    kd)));
+        return CreateEffectRef(effect);
+    }
 
     static const char* Name() { return "DiffuseLighting"; }
 
     typedef GrGLDiffuseLightingEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
     SkScalar kd() const { return fKD; }
+
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrDiffuseLightingEffect(GrTexture* texture,
+                            const SkLight* light,
+                            SkScalar surfaceScale,
+                            SkScalar kd);
+
     GR_DECLARE_EFFECT_TEST;
     typedef GrLightingEffect INHERITED;
     SkScalar fKD;
@@ -339,22 +360,35 @@
 
 class GrSpecularLightingEffect : public GrLightingEffect {
 public:
+    static GrEffectRef* Create(GrTexture* texture,
+                               const SkLight* light,
+                               SkScalar surfaceScale,
+                               SkScalar ks,
+                               SkScalar shininess) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSpecularLightingEffect, (texture,
+                                                                     light,
+                                                                     surfaceScale,
+                                                                     ks,
+                                                                     shininess)));
+        return CreateEffectRef(effect);
+    }
+    static const char* Name() { return "SpecularLighting"; }
+
+    typedef GrGLSpecularLightingEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    SkScalar ks() const { return fKS; }
+    SkScalar shininess() const { return fShininess; }
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GrSpecularLightingEffect(GrTexture* texture,
                              const SkLight* light,
                              SkScalar surfaceScale,
                              SkScalar ks,
                              SkScalar shininess);
 
-    static const char* Name() { return "SpecularLighting"; }
-
-    typedef GrGLSpecularLightingEffect GLEffect;
-
-    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
-    SkScalar ks() const { return fKS; }
-    SkScalar shininess() const { return fShininess; }
-
-private:
     GR_DECLARE_EFFECT_TEST;
     typedef GrLightingEffect INHERITED;
     SkScalar fKS;
@@ -825,12 +859,12 @@
     return true;
 }
 
-bool SkDiffuseLightingImageFilter::asNewEffect(GrEffect** effect,
+bool SkDiffuseLightingImageFilter::asNewEffect(GrEffectRef** effect,
                                                GrTexture* texture) const {
 #if SK_SUPPORT_GPU
     if (effect) {
         SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
-        *effect = SkNEW_ARGS(GrDiffuseLightingEffect, (texture, light(), scale, kd()));
+        *effect = GrDiffuseLightingEffect::Create(texture, light(), scale, kd());
     }
     return true;
 #else
@@ -894,12 +928,12 @@
     return true;
 }
 
-bool SkSpecularLightingImageFilter::asNewEffect(GrEffect** effect,
+bool SkSpecularLightingImageFilter::asNewEffect(GrEffectRef** effect,
                                                 GrTexture* texture) const {
 #if SK_SUPPORT_GPU
     if (effect) {
         SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
-        *effect = SkNEW_ARGS(GrSpecularLightingEffect, (texture, light(), scale, ks(), shininess()));
+        *effect = GrSpecularLightingEffect::Create(texture, light(), scale, ks(), shininess());
     }
     return true;
 #else
@@ -946,7 +980,7 @@
 class GrGLLightingEffect  : public GrGLEffect {
 public:
     GrGLLightingEffect(const GrBackendEffectFactory& factory,
-                       const GrEffect& effect);
+                       const GrEffectRef& effect);
     virtual ~GrGLLightingEffect();
 
     virtual void emitCode(GrGLShaderBuilder*,
@@ -981,7 +1015,7 @@
 class GrGLDiffuseLightingEffect  : public GrGLLightingEffect {
 public:
     GrGLDiffuseLightingEffect(const GrBackendEffectFactory& factory,
-                              const GrEffect& effect);
+                              const GrEffectRef& effect);
     virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) SK_OVERRIDE;
     virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
 
@@ -996,7 +1030,7 @@
 class GrGLSpecularLightingEffect  : public GrGLLightingEffect {
 public:
     GrGLSpecularLightingEffect(const GrBackendEffectFactory& factory,
-                               const GrEffect& effect);
+                               const GrEffectRef& effect);
     virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) SK_OVERRIDE;
     virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
 
@@ -1020,10 +1054,9 @@
     fLight->unref();
 }
 
-bool GrLightingEffect::isEqual(const GrEffect& sBase) const {
-    const GrLightingEffect& s =
-        static_cast<const GrLightingEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+bool GrLightingEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrLightingEffect& s = CastEffect<GrLightingEffect>(sBase);
+    return this->texture(0) == s.texture(0) &&
            fLight->isEqual(*s.fLight) &&
            fSurfaceScale == s.fSurfaceScale;
 }
@@ -1038,34 +1071,33 @@
     return GrTBackendEffectFactory<GrDiffuseLightingEffect>::getInstance();
 }
 
-bool GrDiffuseLightingEffect::isEqual(const GrEffect& sBase) const {
-    const GrDiffuseLightingEffect& s =
-        static_cast<const GrDiffuseLightingEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+bool GrDiffuseLightingEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrDiffuseLightingEffect& s = CastEffect<GrDiffuseLightingEffect>(sBase);
+    return INHERITED::onIsEqual(sBase) &&
             this->kd() == s.kd();
 }
 
 GR_DEFINE_EFFECT_TEST(GrDiffuseLightingEffect);
 
-GrEffect* GrDiffuseLightingEffect::TestCreate(SkRandom* random,
-                                              GrContext* context,
-                                              GrTexture* textures[]) {
+GrEffectRef* GrDiffuseLightingEffect::TestCreate(SkRandom* random,
+                                                 GrContext* context,
+                                                 GrTexture* textures[]) {
     SkScalar surfaceScale = random->nextSScalar1();
     SkScalar kd = random->nextUScalar1();
     SkAutoTUnref<SkLight> light(create_random_light(random));
-    return SkNEW_ARGS(GrDiffuseLightingEffect, (textures[GrEffectUnitTest::kAlphaTextureIdx],
-                                                light, surfaceScale, kd));
+    return GrDiffuseLightingEffect::Create(textures[GrEffectUnitTest::kAlphaTextureIdx],
+                                           light, surfaceScale, kd);
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 
 GrGLLightingEffect::GrGLLightingEffect(const GrBackendEffectFactory& factory,
-                                       const GrEffect& effect)
+                                       const GrEffectRef& effect)
     : INHERITED(factory)
     , fImageIncrementUni(kInvalidUniformHandle)
     , fSurfaceScaleUni(kInvalidUniformHandle) {
-    const GrLightingEffect& m = static_cast<const GrLightingEffect&>(effect);
+    const GrLightingEffect& m = CastEffect<GrLightingEffect>(effect);
     fLight = m.light()->createGLLight();
 }
 
@@ -1174,8 +1206,8 @@
 
 GrGLEffect::EffectKey GrGLLightingEffect::GenKey(const GrEffectStage& s,
                                                  const GrGLCaps& caps) {
-    const GrLightingEffect& effect = static_cast<const GrLightingEffect&>(*s.getEffect());
-    EffectKey key = static_cast<const GrLightingEffect&>(*s.getEffect()).light()->type();
+    const GrLightingEffect& effect = GetEffectFromStage<GrLightingEffect>(s);
+    EffectKey key = effect.light()->type();
     key <<= GrGLEffectMatrix::kKeyBits;
     EffectKey matrixKey = GrGLEffectMatrix::GenKey(effect.getMatrix(),
                                                    s.getCoordChangeMatrix(),
@@ -1184,9 +1216,9 @@
 }
 
 void GrGLLightingEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
-    const GrLightingEffect& effect =static_cast<const GrLightingEffect&>(*stage.getEffect());
+    const GrLightingEffect& effect = GetEffectFromStage<GrLightingEffect>(stage);
     GrTexture* texture = effect.texture(0);
-    float ySign = texture->origin() == GrSurface::kTopLeft_Origin ? -1.0f : 1.0f;
+    float ySign = texture->origin() == kTopLeft_GrSurfaceOrigin ? -1.0f : 1.0f;
     uman.set2f(fImageIncrementUni, 1.0f / texture->width(), ySign / texture->height());
     uman.set1f(fSurfaceScaleUni, effect.surfaceScale());
     fLight->setData(uman, effect.light());
@@ -1201,7 +1233,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 GrGLDiffuseLightingEffect::GrGLDiffuseLightingEffect(const GrBackendEffectFactory& factory,
-                                                     const GrEffect& effect)
+                                                     const GrEffectRef& effect)
     : INHERITED(factory, effect)
     , fKDUni(kInvalidUniformHandle) {
 }
@@ -1233,8 +1265,7 @@
 void GrGLDiffuseLightingEffect::setData(const GrGLUniformManager& uman,
                                         const GrEffectStage& stage) {
     INHERITED::setData(uman, stage);
-    const GrDiffuseLightingEffect& effect =
-        static_cast<const GrDiffuseLightingEffect&>(*stage.getEffect());
+    const GrDiffuseLightingEffect& effect = GetEffectFromStage<GrDiffuseLightingEffect>(stage);
     uman.set1f(fKDUni, effect.kd());
 }
 
@@ -1250,31 +1281,30 @@
     return GrTBackendEffectFactory<GrSpecularLightingEffect>::getInstance();
 }
 
-bool GrSpecularLightingEffect::isEqual(const GrEffect& sBase) const {
-    const GrSpecularLightingEffect& s =
-        static_cast<const GrSpecularLightingEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+bool GrSpecularLightingEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrSpecularLightingEffect& s = CastEffect<GrSpecularLightingEffect>(sBase);
+    return INHERITED::onIsEqual(sBase) &&
            this->ks() == s.ks() &&
            this->shininess() == s.shininess();
 }
 
 GR_DEFINE_EFFECT_TEST(GrSpecularLightingEffect);
 
-GrEffect* GrSpecularLightingEffect::TestCreate(SkRandom* random,
-                                               GrContext* context,
-                                               GrTexture* textures[]) {
+GrEffectRef* GrSpecularLightingEffect::TestCreate(SkRandom* random,
+                                                  GrContext* context,
+                                                  GrTexture* textures[]) {
     SkScalar surfaceScale = random->nextSScalar1();
     SkScalar ks = random->nextUScalar1();
     SkScalar shininess = random->nextUScalar1();
     SkAutoTUnref<SkLight> light(create_random_light(random));
-    return SkNEW_ARGS(GrSpecularLightingEffect, (textures[GrEffectUnitTest::kAlphaTextureIdx],
-                                                 light, surfaceScale, ks, shininess));
+    return GrSpecularLightingEffect::Create(textures[GrEffectUnitTest::kAlphaTextureIdx],
+                                            light, surfaceScale, ks, shininess);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 GrGLSpecularLightingEffect::GrGLSpecularLightingEffect(const GrBackendEffectFactory& factory,
-                                            const GrEffect& effect)
+                                                       const GrEffectRef& effect)
     : GrGLLightingEffect(factory, effect)
     , fKSUni(kInvalidUniformHandle)
     , fShininessUni(kInvalidUniformHandle) {
@@ -1311,8 +1341,7 @@
 void GrGLSpecularLightingEffect::setData(const GrGLUniformManager& uman,
                                          const GrEffectStage& stage) {
     INHERITED::setData(uman, stage);
-    const GrSpecularLightingEffect& effect =
-        static_cast<const GrSpecularLightingEffect&>(*stage.getEffect());
+    const GrSpecularLightingEffect& effect = GetEffectFromStage<GrSpecularLightingEffect>(stage);
     uman.set1f(fKSUni, effect.ks());
     uman.set1f(fShininessUni, effect.shininess());
 }
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index 8cb1030..e0ab4d0 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -26,6 +26,40 @@
 class GrMagnifierEffect : public GrSingleTextureEffect {
 
 public:
+    static GrEffectRef* Create(GrTexture* texture,
+                               float xOffset,
+                               float yOffset,
+                               float xZoom,
+                               float yZoom,
+                               float xInset,
+                               float yInset) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrMagnifierEffect, (texture,
+                                                              xOffset,
+                                                              yOffset,
+                                                              xZoom,
+                                                              yZoom,
+                                                              xInset,
+                                                              yInset)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrMagnifierEffect() {};
+
+    static const char* Name() { return "Magnifier"; }
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    float x_offset() const { return fXOffset; }
+    float y_offset() const { return fYOffset; }
+    float x_zoom() const { return fXZoom; }
+    float y_zoom() const { return fYZoom; }
+    float x_inset() const { return fXInset; }
+    float y_inset() const { return fYInset; }
+
+    typedef GrGLMagnifierEffect GLEffect;
+
+private:
     GrMagnifierEffect(GrTexture* texture,
                       float xOffset,
                       float yOffset,
@@ -41,23 +75,8 @@
         , fXInset(xInset)
         , fYInset(yInset) {}
 
-    virtual ~GrMagnifierEffect() {};
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
 
-    static const char* Name() { return "Magnifier"; }
-
-    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
-
-    float x_offset() const { return fXOffset; }
-    float y_offset() const { return fYOffset; }
-    float x_zoom() const { return fXZoom; }
-    float y_zoom() const { return fYZoom; }
-    float x_inset() const { return fXInset; }
-    float y_inset() const { return fYInset; }
-
-    typedef GrGLMagnifierEffect GLEffect;
-
-private:
     GR_DECLARE_EFFECT_TEST;
 
     float fXOffset;
@@ -75,8 +94,7 @@
 
 class GrGLMagnifierEffect : public GrGLEffect {
 public:
-    GrGLMagnifierEffect(const GrBackendEffectFactory& factory,
-                        const GrEffect& effect);
+    GrGLMagnifierEffect(const GrBackendEffectFactory& factory, const GrEffectRef& effect);
 
     virtual void emitCode(GrGLShaderBuilder*,
                           const GrEffectStage&,
@@ -101,8 +119,7 @@
     typedef GrGLEffect INHERITED;
 };
 
-GrGLMagnifierEffect::GrGLMagnifierEffect(const GrBackendEffectFactory& factory,
-                                         const GrEffect& effect)
+GrGLMagnifierEffect::GrGLMagnifierEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
     : INHERITED(factory)
     , fOffsetVar(GrGLUniformManager::kInvalidUniformHandle)
     , fZoomVar(GrGLUniformManager::kInvalidUniformHandle)
@@ -165,8 +182,7 @@
 
 void GrGLMagnifierEffect::setData(const GrGLUniformManager& uman,
                                   const GrEffectStage& stage) {
-    const GrMagnifierEffect& zoom = static_cast<const GrMagnifierEffect&>(*stage.getEffect());
-
+    const GrMagnifierEffect& zoom = GetEffectFromStage<GrMagnifierEffect>(stage);
     uman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset());
     uman.set2f(fZoomVar, zoom.x_zoom(), zoom.y_zoom());
     uman.set2f(fInsetVar, zoom.x_inset(), zoom.y_inset());
@@ -174,7 +190,7 @@
 }
 
 GrGLEffect::EffectKey GrGLMagnifierEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
-    const GrMagnifierEffect& zoom = static_cast<const GrMagnifierEffect&>(*stage.getEffect());
+    const GrMagnifierEffect& zoom = GetEffectFromStage<GrMagnifierEffect>(stage);
     return GrGLEffectMatrix::GenKey(zoom.getMatrix(),
                                     stage.getCoordChangeMatrix(),
                                     zoom.texture(0));
@@ -184,9 +200,9 @@
 
 GR_DEFINE_EFFECT_TEST(GrMagnifierEffect);
 
-GrEffect* GrMagnifierEffect::TestCreate(SkRandom* random,
-                                        GrContext* context,
-                                        GrTexture** textures) {
+GrEffectRef* GrMagnifierEffect::TestCreate(SkRandom* random,
+                                           GrContext* context,
+                                           GrTexture** textures) {
     const int kMaxWidth = 200;
     const int kMaxHeight = 200;
     const int kMaxInset = 20;
@@ -201,7 +217,7 @@
                 SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
                                  SkIntToScalar(width), SkIntToScalar(height)),
                 inset));
-    GrEffect* effect;
+    GrEffectRef* effect;
     filter->asNewEffect(&effect, textures[0]);
     GrAssert(NULL != effect);
     return effect;
@@ -213,10 +229,10 @@
     return GrTBackendEffectFactory<GrMagnifierEffect>::getInstance();
 }
 
-bool GrMagnifierEffect::isEqual(const GrEffect& sBase) const {
-     const GrMagnifierEffect& s =
-        static_cast<const GrMagnifierEffect&>(sBase);
-    return (this->fXOffset == s.fXOffset &&
+bool GrMagnifierEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrMagnifierEffect& s = CastEffect<GrMagnifierEffect>(sBase);
+    return (this->texture(0) == s.texture(0) &&
+            this->fXOffset == s.fXOffset &&
             this->fYOffset == s.fYOffset &&
             this->fXZoom == s.fXZoom &&
             this->fYZoom == s.fYZoom &&
@@ -224,6 +240,10 @@
             this->fYInset == s.fYInset);
 }
 
+void GrMagnifierEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -243,17 +263,16 @@
     SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0);
 }
 
-bool SkMagnifierImageFilter::asNewEffect(GrEffect** effect,
-                                         GrTexture* texture) const {
+bool SkMagnifierImageFilter::asNewEffect(GrEffectRef** effect, GrTexture* texture) const {
 #if SK_SUPPORT_GPU
     if (effect) {
-      *effect = SkNEW_ARGS(GrMagnifierEffect, (texture,
-                                               fSrcRect.x() / texture->width(),
-                                               fSrcRect.y() / texture->height(),
-                                               texture->width() / fSrcRect.width(),
-                                               texture->height() / fSrcRect.height(),
-                                               fInset / texture->width(),
-                                               fInset / texture->height()));
+      *effect = GrMagnifierEffect::Create(texture,
+                                          fSrcRect.x() / texture->width(),
+                                          fSrcRect.y() / texture->height(),
+                                          texture->width() / fSrcRect.width(),
+                                          texture->height() / fSrcRect.height(),
+                                          fInset / texture->width(),
+                                          fInset / texture->height());
     }
     return true;
 #else
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
index b5188b3..5a97ec4 100644
--- a/src/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -43,7 +43,7 @@
     fKernelSize.fHeight = buffer.readInt();
     uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
     fKernel = SkNEW_ARRAY(SkScalar, size);
-    uint32_t readSize = buffer.readScalarArray(fKernel);
+    SkDEBUGCODE(uint32_t readSize = )buffer.readScalarArray(fKernel);
     SkASSERT(readSize == size);
     fGain = buffer.readScalar();
     fBias = buffer.readScalar();
@@ -247,16 +247,32 @@
 class GrMatrixConvolutionEffect : public GrSingleTextureEffect {
 public:
     typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
-    GrMatrixConvolutionEffect(GrTexture*,
-                              const SkISize& kernelSize,
-                              const SkScalar* kernel,
-                              SkScalar gain,
-                              SkScalar bias,
-                              const SkIPoint& target,
-                              TileMode tileMode,
-                              bool convolveAlpha);
+    static GrEffectRef* Create(GrTexture* texture,
+                               const SkISize& kernelSize,
+                               const SkScalar* kernel,
+                               SkScalar gain,
+                               SkScalar bias,
+                               const SkIPoint& target,
+                               TileMode tileMode,
+                               bool convolveAlpha) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
+                                                                      kernelSize,
+                                                                      kernel,
+                                                                      gain,
+                                                                      bias,
+                                                                      target,
+                                                                      tileMode,
+                                                                      convolveAlpha)));
+        return CreateEffectRef(effect);
+    }
     virtual ~GrMatrixConvolutionEffect();
 
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // TODO: Try to do better?
+        *validFlags = 0;
+    }
+
     static const char* Name() { return "MatrixConvolution"; }
     const SkISize& kernelSize() const { return fKernelSize; }
     const float* target() const { return fTarget; }
@@ -268,10 +284,22 @@
 
     typedef GrGLMatrixConvolutionEffect GLEffect;
 
+
+
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
 
 private:
+    GrMatrixConvolutionEffect(GrTexture*,
+                              const SkISize& kernelSize,
+                              const SkScalar* kernel,
+                              SkScalar gain,
+                              SkScalar bias,
+                              const SkIPoint& target,
+                              TileMode tileMode,
+                              bool convolveAlpha);
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     SkISize  fKernelSize;
     float   *fKernel;
     float    fGain;
@@ -288,7 +316,7 @@
 class GrGLMatrixConvolutionEffect : public GrGLEffect {
 public:
     GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
-                                const GrEffect& effect);
+                                const GrEffectRef& effect);
     virtual void emitCode(GrGLShaderBuilder*,
                           const GrEffectStage&,
                           EffectKey,
@@ -320,14 +348,14 @@
 };
 
 GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
-                                                         const GrEffect& effect)
+                                                         const GrEffectRef& effect)
     : INHERITED(factory)
     , fKernelUni(GrGLUniformManager::kInvalidUniformHandle)
     , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle)
     , fTargetUni(GrGLUniformManager::kInvalidUniformHandle)
     , fGainUni(GrGLUniformManager::kInvalidUniformHandle)
     , fBiasUni(GrGLUniformManager::kInvalidUniformHandle) {
-    const GrMatrixConvolutionEffect& m = static_cast<const GrMatrixConvolutionEffect&>(effect);
+    const GrMatrixConvolutionEffect& m = CastEffect<GrMatrixConvolutionEffect>(effect);
     fKernelSize = m.kernelSize();
     fTileMode = m.tileMode();
     fConvolveAlpha = m.convolveAlpha();
@@ -426,8 +454,7 @@
 };
 
 GrGLEffect::EffectKey GrGLMatrixConvolutionEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
-    const GrMatrixConvolutionEffect& m =
-        static_cast<const GrMatrixConvolutionEffect&>(*s.getEffect());
+    const GrMatrixConvolutionEffect& m = GetEffectFromStage<GrMatrixConvolutionEffect>(s);
     EffectKey key = encodeXY(m.kernelSize().width(), m.kernelSize().height());
     key |= m.tileMode() << 7;
     key |= m.convolveAlpha() ? 1 << 9 : 0;
@@ -440,15 +467,15 @@
 
 void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
                                           const GrEffectStage& stage) {
-    const GrMatrixConvolutionEffect& effect =
-        static_cast<const GrMatrixConvolutionEffect&>(*stage.getEffect());
+    const GrMatrixConvolutionEffect& effect = GetEffectFromStage<GrMatrixConvolutionEffect>(stage);
     GrTexture& texture = *effect.texture(0);
     // the code we generated was for a specific kernel size
     GrAssert(effect.kernelSize() == fKernelSize);
     GrAssert(effect.tileMode() == fTileMode);
     float imageIncrement[2];
+    float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
     imageIncrement[0] = 1.0f / texture.width();
-    imageIncrement[1] = 1.0f / texture.height();
+    imageIncrement[1] = ySign / texture.height();
     uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
     uman.set2fv(fTargetUni, 0, 1, effect.target());
     uman.set1fv(fKernelUni, 0, fKernelSize.width() * fKernelSize.height(), effect.kernel());
@@ -490,10 +517,9 @@
     return GrTBackendEffectFactory<GrMatrixConvolutionEffect>::getInstance();
 }
 
-bool GrMatrixConvolutionEffect::isEqual(const GrEffect& sBase) const {
-    const GrMatrixConvolutionEffect& s =
-        static_cast<const GrMatrixConvolutionEffect&>(sBase);
-    return INHERITED::isEqual(sBase) &&
+bool GrMatrixConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrMatrixConvolutionEffect& s = CastEffect<GrMatrixConvolutionEffect>(sBase);
+    return this->texture(0) == s.texture(0) &&
            fKernelSize == s.kernelSize() &&
            !memcmp(fKernel, s.kernel(), fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
            fGain == s.gain() &&
@@ -509,9 +535,9 @@
 // Allows for a 5x5 kernel (or 25x1, for that matter).
 #define MAX_KERNEL_SIZE 25
 
-GrEffect* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
-                                                GrContext* context,
-                                                GrTexture* textures[]) {
+GrEffectRef* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
+                                                   GrContext* context,
+                                                   GrTexture* textures[]) {
     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                       GrEffectUnitTest::kAlphaTextureIdx;
     int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
@@ -527,29 +553,29 @@
                                      random->nextRangeU(0, kernelSize.height()));
     TileMode tileMode = static_cast<TileMode>(random->nextRangeU(0, 2));
     bool convolveAlpha = random->nextBool();
-    return SkNEW_ARGS(GrMatrixConvolutionEffect, (textures[texIdx],
-                                                  kernelSize,
-                                                  kernel,
-                                                  gain,
-                                                  bias,
-                                                  target,
-                                                  tileMode,
-                                                  convolveAlpha));
+    return GrMatrixConvolutionEffect::Create(textures[texIdx],
+                                             kernelSize,
+                                             kernel,
+                                             gain,
+                                             bias,
+                                             target,
+                                             tileMode,
+                                             convolveAlpha);
 
 }
 
-bool SkMatrixConvolutionImageFilter::asNewEffect(GrEffect** effect,
+bool SkMatrixConvolutionImageFilter::asNewEffect(GrEffectRef** effect,
                                                  GrTexture* texture) const {
     bool ok = fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
     if (ok && effect) {
-        *effect = SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
-                                                         fKernelSize,
-                                                         fKernel,
-                                                         fGain,
-                                                         fBias,
-                                                         fTarget,
-                                                         fTileMode,
-                                                         fConvolveAlpha));
+        *effect = GrMatrixConvolutionEffect::Create(texture,
+                                                    fKernelSize,
+                                                    fKernel,
+                                                    fGain,
+                                                    fBias,
+                                                     fTarget,
+                                                     fTileMode,
+                                                     fConvolveAlpha);
     }
     return ok;
 }
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index a497346..09d7926 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -17,6 +17,7 @@
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLEffectMatrix.h"
 #include "effects/Gr1DKernelEffect.h"
+#include "SkImageFilterUtils.h"
 #endif
 
 SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
@@ -243,7 +244,11 @@
         kDilate_MorphologyType,
     };
 
-    GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
+    static GrEffectRef* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)));
+        return CreateEffectRef(effect);
+    }
+
     virtual ~GrMorphologyEffect();
 
     MorphologyType type() const { return fType; }
@@ -253,13 +258,17 @@
     typedef GrGLMorphologyEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 protected:
 
     MorphologyType fType;
 
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
+
     GR_DECLARE_EFFECT_TEST;
 
     typedef Gr1DKernelEffect INHERITED;
@@ -267,10 +276,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class GrGLMorphologyEffect  : public GrGLEffect {
+class GrGLMorphologyEffect : public GrGLEffect {
 public:
-    GrGLMorphologyEffect (const GrBackendEffectFactory& factory,
-                          const GrEffect& effect);
+    GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrEffectRef&);
 
     virtual void emitCode(GrGLShaderBuilder*,
                           const GrEffectStage&,
@@ -296,10 +304,10 @@
 };
 
 GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory,
-                                           const GrEffect& effect)
+                                           const GrEffectRef& effect)
     : INHERITED(factory)
     , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle) {
-    const GrMorphologyEffect& m = static_cast<const GrMorphologyEffect&>(effect);
+    const GrMorphologyEffect& m = CastEffect<GrMorphologyEffect>(effect);
     fRadius = m.radius();
     fType = m.type();
 }
@@ -346,7 +354,7 @@
 }
 
 GrGLEffect::EffectKey GrGLMorphologyEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
-    const GrMorphologyEffect& m = static_cast<const GrMorphologyEffect&>(*s.getEffect());
+    const GrMorphologyEffect& m = GetEffectFromStage<GrMorphologyEffect>(s);
     EffectKey key = static_cast<EffectKey>(m.radius());
     key |= (m.type() << 8);
     key <<= GrGLEffectMatrix::kKeyBits;
@@ -357,7 +365,7 @@
 }
 
 void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
-    const Gr1DKernelEffect& kern = static_cast<const Gr1DKernelEffect&>(*stage.getEffect());
+    const Gr1DKernelEffect& kern = GetEffectFromStage<Gr1DKernelEffect>(stage);
     GrTexture& texture = *kern.texture(0);
     // the code we generated was for a specific kernel radius
     GrAssert(kern.radius() == fRadius);
@@ -393,22 +401,27 @@
     return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance();
 }
 
-bool GrMorphologyEffect::isEqual(const GrEffect& sBase) const {
-    const GrMorphologyEffect& s =
-        static_cast<const GrMorphologyEffect&>(sBase);
-    return (INHERITED::isEqual(sBase) &&
+bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrMorphologyEffect& s = CastEffect<GrMorphologyEffect>(sBase);
+    return (this->texture(0) == s.texture(0) &&
             this->radius() == s.radius() &&
             this->direction() == s.direction() &&
             this->type() == s.type());
 }
 
+void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // This is valid because the color components of the result of the kernel all come
+    // exactly from existing values in the source texture.
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_EFFECT_TEST(GrMorphologyEffect);
 
-GrEffect* GrMorphologyEffect::TestCreate(SkRandom* random,
-                                         GrContext* context,
-                                         GrTexture* textures[]) {
+GrEffectRef* GrMorphologyEffect::TestCreate(SkRandom* random,
+                                            GrContext* context,
+                                            GrTexture* textures[]) {
     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                       GrEffectUnitTest::kAlphaTextureIdx;
     Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
@@ -417,27 +430,27 @@
     MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
                                                GrMorphologyEffect::kDilate_MorphologyType;
 
-    return SkNEW_ARGS(GrMorphologyEffect, (textures[texIdx], dir, radius, type));
+    return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
 }
 
 namespace {
 
 void apply_morphology_pass(GrContext* context,
                            GrTexture* texture,
-                           const SkRect& rect,
+                           const SkIRect& rect,
                            int radius,
                            GrMorphologyEffect::MorphologyType morphType,
                            Gr1DKernelEffect::Direction direction) {
     GrPaint paint;
-    paint.colorStage(0)->setEffect(SkNEW_ARGS(GrMorphologyEffect, (texture,
-                                                                   direction,
-                                                                   radius,
-                                                                   morphType)))->unref();
-    context->drawRect(paint, rect);
+    paint.colorStage(0)->setEffect(GrMorphologyEffect::Create(texture,
+                                                              direction,
+                                                              radius,
+                                                              morphType))->unref();
+    context->drawRect(paint, SkRect::MakeFromIRect(rect));
 }
 
 GrTexture* apply_morphology(GrTexture* srcTexture,
-                            const GrRect& rect,
+                            const SkIRect& rect,
                             GrMorphologyEffect::MorphologyType morphType,
                             SkISize radius) {
     GrContext* context = srcTexture->getContext();
@@ -451,20 +464,17 @@
 
     GrTextureDesc desc;
     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
-    desc.fWidth = SkScalarCeilToInt(rect.width());
-    desc.fHeight = SkScalarCeilToInt(rect.height());
-    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fWidth = rect.width();
+    desc.fHeight = rect.height();
+    desc.fConfig = kSkia8888_GrPixelConfig;
 
     if (radius.fWidth > 0) {
         GrAutoScratchTexture ast(context, desc);
         GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
         apply_morphology_pass(context, srcTexture, rect, radius.fWidth,
                               morphType, Gr1DKernelEffect::kX_Direction);
-        SkIRect clearRect = SkIRect::MakeXYWH(
-                    SkScalarFloorToInt(rect.fLeft),
-                    SkScalarFloorToInt(rect.fBottom),
-                    SkScalarFloorToInt(rect.width()),
-                    radius.fHeight);
+        SkIRect clearRect = SkIRect::MakeXYWH(rect.fLeft, rect.fBottom,
+                                              rect.width(), radius.fHeight);
         context->clear(&clearRect, 0x0);
         srcTexture->unref();
         srcTexture = ast.detach();
@@ -482,14 +492,30 @@
 
 };
 
-GrTexture* SkDilateImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
-    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(proxy, src, rect));
-    return apply_morphology(input, rect, GrMorphologyEffect::kDilate_MorphologyType, radius());
+bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap inputBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) {
+        return false;
+    }
+    GrTexture* input = (GrTexture*) inputBM.getTexture();
+    SkIRect bounds;
+    src.getBounds(&bounds);
+    SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
+        GrMorphologyEffect::kDilate_MorphologyType, radius()));
+    return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
 }
 
-GrTexture* SkErodeImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
-    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(proxy, src, rect));
-    return apply_morphology(input, rect, GrMorphologyEffect::kErode_MorphologyType, radius());
+bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap inputBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) {
+        return false;
+    }
+    GrTexture* input = (GrTexture*) inputBM.getTexture();
+    SkIRect bounds;
+    src.getBounds(&bounds);
+    SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
+        GrMorphologyEffect::kErode_MorphologyType, radius()));
+    return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
 }
 
 #endif
diff --git a/src/effects/SkPaintFlagsDrawFilter.cpp b/src/effects/SkPaintFlagsDrawFilter.cpp
index 92d6f64..dc1c007 100644
--- a/src/effects/SkPaintFlagsDrawFilter.cpp
+++ b/src/effects/SkPaintFlagsDrawFilter.cpp
@@ -18,4 +18,3 @@
     paint->setFlags((paint->getFlags() & ~fClearFlags) | fSetFlags);
     return true;
 }
-
diff --git a/src/effects/SkPixelXorXfermode.cpp b/src/effects/SkPixelXorXfermode.cpp
index cf454da..b9c96dc 100644
--- a/src/effects/SkPixelXorXfermode.cpp
+++ b/src/effects/SkPixelXorXfermode.cpp
@@ -10,10 +10,11 @@
 #include "SkPixelXorXfermode.h"
 #include "SkColorPriv.h"
 #include "SkFlattenableBuffers.h"
+#include "SkString.h"
 
 // we always return an opaque color, 'cause I don't know what to do with
 // the alpha-component and still return a valid premultiplied color.
-SkPMColor SkPixelXorXfermode::xferColor(SkPMColor src, SkPMColor dst) {
+SkPMColor SkPixelXorXfermode::xferColor(SkPMColor src, SkPMColor dst) const {
     SkPMColor res = src ^ dst ^ fOpColor;
     res |= (SK_A32_MASK << SK_A32_SHIFT);   // force it to be opaque
     return res;
@@ -28,3 +29,10 @@
         : INHERITED(rb) {
     fOpColor = rb.readColor();
 }
+
+#ifdef SK_DEVELOPER
+void SkPixelXorXfermode::toString(SkString* str) const {
+    str->append("SkPixelXorXfermode: ");
+    str->appendHex(fOpColor);
+}
+#endif
diff --git a/src/effects/SkPorterDuff.cpp b/src/effects/SkPorterDuff.cpp
index 8acb345..816ddae 100644
--- a/src/effects/SkPorterDuff.cpp
+++ b/src/effects/SkPorterDuff.cpp
@@ -36,7 +36,7 @@
     MAKE_PAIR(Xor),
     MAKE_PAIR(Darken),
     MAKE_PAIR(Lighten),
-    MAKE_PAIR(Multiply),
+    MAKE_PAIR(Modulate),
     MAKE_PAIR(Screen),
     { SkPorterDuff::kAdd_Mode, SkXfermode::kPlus_Mode },
 #ifdef SK_BUILD_FOR_ANDROID
@@ -85,4 +85,3 @@
 SkXfermodeProc16 SkPorterDuff::GetXfermodeProc16(Mode mode, SkColor srcColor) {
     return SkXfermode::GetProc16(gPairs[mode].fXF, srcColor);
 }
-
diff --git a/src/effects/SkRectShape.cpp b/src/effects/SkRectShape.cpp
deleted file mode 100644
index 52c3b37..0000000
--- a/src/effects/SkRectShape.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-
-/*
- * 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 "SkRectShape.h"
-#include "SkCanvas.h"
-#include "SkFlattenableBuffers.h"
-
-SkPaintShape::SkPaintShape() {
-    fPaint.setAntiAlias(true);
-}
-
-SkRectShape::SkRectShape() {
-    fBounds.setEmpty();
-    fRadii.set(0, 0);
-}
-
-void SkRectShape::setRect(const SkRect& bounds) {
-    fBounds = bounds;
-    fRadii.set(0, 0);
-}
-
-void SkRectShape::setOval(const SkRect& bounds) {
-    fBounds = bounds;
-    fRadii.set(-SK_Scalar1, -SK_Scalar1);
-}
-
-void SkRectShape::setCircle(SkScalar cx, SkScalar cy, SkScalar radius) {
-    fBounds.set(cx - radius, cy - radius, cx + radius, cy + radius);
-    fRadii.set(-SK_Scalar1, -SK_Scalar1);
-}
-
-void SkRectShape::setRRect(const SkRect& bounds, SkScalar rx, SkScalar ry) {
-    if (rx < 0) {
-        rx = 0;
-    }
-    if (ry < 0) {
-        ry = 0;
-    }
-
-    fBounds = bounds;
-    fRadii.set(rx, ry);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkRectShape::onDraw(SkCanvas* canvas) {
-    const SkPaint& paint = this->paint();
-
-    if (fRadii.fWidth < 0) {
-        canvas->drawOval(fBounds, paint);
-    } else if (fRadii.isZero()) {
-        canvas->drawRect(fBounds, paint);
-    } else {
-        canvas->drawRoundRect(fBounds, fRadii.fWidth, fRadii.fHeight, paint);
-    }
-}
-
-void SkRectShape::flatten(SkFlattenableWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-
-    buffer.writeRect(fBounds);
-    buffer.writeScalar(fRadii.fWidth);
-    buffer.writeScalar(fRadii.fHeight);
-}
-
-SkRectShape::SkRectShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    buffer.readRect(&fBounds);
-    fRadii.fWidth = buffer.readScalar();
-    fRadii.fHeight = buffer.readScalar();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkPaintShape::flatten(SkFlattenableWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    buffer.writePaint(fPaint);
-}
-
-SkPaintShape::SkPaintShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    buffer.readPaint(&fPaint);
-}
diff --git a/src/effects/SkSingleInputImageFilter.cpp b/src/effects/SkSingleInputImageFilter.cpp
index 7615038..9e45c35 100644
--- a/src/effects/SkSingleInputImageFilter.cpp
+++ b/src/effects/SkSingleInputImageFilter.cpp
@@ -9,11 +9,6 @@
 #include "SkBitmap.h"
 #include "SkFlattenableBuffers.h"
 #include "SkMatrix.h"
-#if SK_SUPPORT_GPU
-#include "GrTexture.h"
-#include "SkGr.h"
-#include "SkGrPixelRef.h"
-#endif
 
 SkSingleInputImageFilter::SkSingleInputImageFilter(SkImageFilter* input) : INHERITED(input) {
 }
@@ -35,38 +30,3 @@
                                                   SkIPoint* offset) {
     return this->INHERITED::getInputResult(0, proxy, src, ctm, offset);
 }
-
-#if SK_SUPPORT_GPU
-// FIXME:  generalize and move to base class
-GrTexture* SkSingleInputImageFilter::getInputResultAsTexture(Proxy* proxy,
-                                                             GrTexture* src,
-                                                             const SkRect& rect) {
-    GrTexture* resultTex = NULL;
-    SkImageFilter* input = getInput(0);
-    if (!input) {
-        resultTex = src;
-    } else if (input->canFilterImageGPU()) {
-        // onFilterImageGPU() already refs the result, so just return it here.
-        return input->onFilterImageGPU(proxy, src, rect);
-    } else {
-        SkBitmap srcBitmap, result;
-        srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, src->width(), src->height());
-        srcBitmap.setPixelRef(new SkGrPixelRef(src))->unref();
-        SkIPoint offset;
-        if (input->filterImage(proxy, srcBitmap, SkMatrix(), &result, &offset)) {
-            if (result.getTexture()) {
-                resultTex = (GrTexture*) result.getTexture();
-            } else {
-                resultTex = GrLockCachedBitmapTexture(src->getContext(), result, NULL);
-                SkSafeRef(resultTex);
-                GrUnlockCachedBitmapTexture(resultTex);
-                return resultTex;
-            }
-        } else {
-            resultTex = src;
-        }
-    }
-    SkSafeRef(resultTex);
-    return resultTex;
-}
-#endif
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index ad34fbb..5c00794 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -41,7 +41,7 @@
     virtual bool asComponentTable(SkBitmap* table) const SK_OVERRIDE;
 
 #if SK_SUPPORT_GPU
-    virtual GrEffect* asNewEffect(GrContext* context) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context) const SK_OVERRIDE;
 #endif
 
     virtual void filterSpan(const SkPMColor src[], int count,
@@ -49,6 +49,13 @@
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTable_ColorFilter)
 
+    enum {
+        kA_Flag = 1 << 0,
+        kR_Flag = 1 << 1,
+        kG_Flag = 1 << 2,
+        kB_Flag = 1 << 3,
+    };
+
 protected:
     SkTable_ColorFilter(SkFlattenableReadBuffer& buffer);
     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
@@ -56,12 +63,6 @@
 private:
     mutable const SkBitmap* fBitmap; // lazily allocated
 
-    enum {
-        kA_Flag = 1 << 0,
-        kR_Flag = 1 << 1,
-        kG_Flag = 1 << 2,
-        kB_Flag = 1 << 3,
-    };
     uint8_t fStorage[256 * 4];
     unsigned fFlags;
 
@@ -181,10 +182,10 @@
     SkASSERT(size <= sizeof(storage));
     buffer.readByteArray(storage);
 
-    size_t raw = SkPackBits::Unpack8(storage, size, fStorage);
+    SkDEBUGCODE(size_t raw = ) SkPackBits::Unpack8(storage, size, fStorage);
 
     SkASSERT(raw <= sizeof(fStorage));
-    size_t count = gCountNibBits[fFlags & 0xF];
+    SkDEBUGCODE(size_t count = gCountNibBits[fFlags & 0xF]);
     SkASSERT(raw == count * 256);
 }
 
@@ -225,30 +226,37 @@
 
 class ColorTableEffect : public GrEffect {
 public:
+    static GrEffectRef* Create(GrTexture* texture, unsigned flags) {
+        AutoEffectUnref effect(SkNEW_ARGS(ColorTableEffect, (texture, flags)));
+        return CreateEffectRef(effect);
+    }
 
-    explicit ColorTableEffect(GrTexture* texture);
     virtual ~ColorTableEffect();
 
     static const char* Name() { return "ColorTable"; }
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
 
-    virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     typedef GLColorTableEffect GLEffect;
 
 private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    explicit ColorTableEffect(GrTexture* texture, unsigned flags);
+
     GR_DECLARE_EFFECT_TEST;
 
     GrTextureAccess fTextureAccess;
+    unsigned        fFlags; // currently not used in shader code, just to assist
+                            // getConstantColorComponents().
 
     typedef GrEffect INHERITED;
 };
 
 class GLColorTableEffect : public GrGLEffect {
 public:
-    GLColorTableEffect(const GrBackendEffectFactory& factory,
-                         const GrEffect& effect);
+    GLColorTableEffect(const GrBackendEffectFactory&, const GrEffectRef&);
 
     virtual void emitCode(GrGLShaderBuilder*,
                           const GrEffectStage&,
@@ -267,8 +275,7 @@
     typedef GrGLEffect INHERITED;
 };
 
-GLColorTableEffect::GLColorTableEffect(
-    const GrBackendEffectFactory& factory, const GrEffect& effect)
+GLColorTableEffect::GLColorTableEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
     : INHERITED(factory) {
  }
 
@@ -323,9 +330,10 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-ColorTableEffect::ColorTableEffect(GrTexture* texture)
-    : INHERITED(1)
-    , fTextureAccess(texture, "a") {
+ColorTableEffect::ColorTableEffect(GrTexture* texture, unsigned flags)
+    : fTextureAccess(texture, "a")
+    , fFlags(flags) {
+    this->addTextureAccess(&fTextureAccess);
 }
 
 ColorTableEffect::~ColorTableEffect() {
@@ -335,36 +343,51 @@
     return GrTBackendEffectFactory<ColorTableEffect>::getInstance();
 }
 
-bool ColorTableEffect::isEqual(const GrEffect& sBase) const {
-    return INHERITED::isEqual(sBase);
+bool ColorTableEffect::onIsEqual(const GrEffect& sBase) const {
+    return this->texture(0) == sBase.texture(0);
 }
 
-const GrTextureAccess& ColorTableEffect::textureAccess(int index) const {
-    GrAssert(0 == index);
-    return fTextureAccess;
+void ColorTableEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // If we kept the table in the effect then we could actually run known inputs through the
+    // table.
+    if (fFlags & SkTable_ColorFilter::kR_Flag) {
+        *validFlags &= ~kR_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kG_Flag) {
+        *validFlags &= ~kG_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kB_Flag) {
+        *validFlags &= ~kB_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kA_Flag) {
+        *validFlags &= ~kA_ValidComponentFlag;
+    }
 }
 
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_EFFECT_TEST(ColorTableEffect);
 
-GrEffect* ColorTableEffect::TestCreate(SkRandom* random,
-                                       GrContext* context,
-                                       GrTexture* textures[]) {
-    return SkNEW_ARGS(ColorTableEffect, (textures[GrEffectUnitTest::kAlphaTextureIdx]));
+GrEffectRef* ColorTableEffect::TestCreate(SkRandom* random,
+                                          GrContext* context,
+                                          GrTexture* textures[]) {
+    static unsigned kAllFlags = SkTable_ColorFilter::kR_Flag | SkTable_ColorFilter::kG_Flag |
+                                SkTable_ColorFilter::kB_Flag | SkTable_ColorFilter::kA_Flag;
+    return ColorTableEffect::Create(textures[GrEffectUnitTest::kAlphaTextureIdx], kAllFlags);
 }
 
-GrEffect* SkTable_ColorFilter::asNewEffect(GrContext* context) const {
+GrEffectRef* SkTable_ColorFilter::asNewEffect(GrContext* context) const {
     SkBitmap bitmap;
     this->asComponentTable(&bitmap);
     // passing NULL because this effect does no tiling or filtering.
-    GrTexture* texture = GrLockCachedBitmapTexture(context, bitmap, NULL);
-    GrEffect* effect = SkNEW_ARGS(ColorTableEffect, (texture));
+    GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, bitmap, NULL);
+    GrEffectRef* effect = ColorTableEffect::Create(texture, fFlags);
 
     // Unlock immediately, this is not great, but we don't have a way of
     // knowing when else to unlock it currently. TODO: Remove this when
     // unref becomes the unlock replacement for all types of textures.
-    GrUnlockCachedBitmapTexture(texture);
+    GrUnlockAndUnrefCachedBitmapTexture(texture);
     return effect;
 }
 
diff --git a/src/effects/SkTableMaskFilter.cpp b/src/effects/SkTableMaskFilter.cpp
index 73b7fa6..ada5d01 100644
--- a/src/effects/SkTableMaskFilter.cpp
+++ b/src/effects/SkTableMaskFilter.cpp
@@ -126,4 +126,3 @@
     SkDebugf("\n\n");
 #endif
 }
-
diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
index 28b075f..7b92fde 100644
--- a/src/effects/SkTransparentShader.cpp
+++ b/src/effects/SkTransparentShader.cpp
@@ -9,6 +9,7 @@
 
 #include "SkTransparentShader.h"
 #include "SkColorPriv.h"
+#include "SkString.h"
 
 bool SkTransparentShader::setContext(const SkBitmap& device,
                                      const SkPaint& paint,
@@ -125,3 +126,13 @@
         memcpy(span, src, count << 1);
     }
 }
+
+#ifdef SK_DEVELOPER
+void SkTransparentShader::toString(SkString* str) const {
+    str->append("SkTransparentShader: (");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkBitmapCache.cpp b/src/effects/gradients/SkBitmapCache.cpp
index 91f67ec..95175e4 100644
--- a/src/effects/gradients/SkBitmapCache.cpp
+++ b/src/effects/gradients/SkBitmapCache.cpp
@@ -151,4 +151,3 @@
 }
 
 #endif
-
diff --git a/src/effects/gradients/SkBitmapCache.h b/src/effects/gradients/SkBitmapCache.h
index ea9cf91..d2af5e1 100644
--- a/src/effects/gradients/SkBitmapCache.h
+++ b/src/effects/gradients/SkBitmapCache.h
@@ -47,4 +47,3 @@
 };
 
 #endif
-
diff --git a/src/effects/gradients/SkClampRange.cpp b/src/effects/gradients/SkClampRange.cpp
index c4bf8b1..3e2ca8e 100644
--- a/src/effects/gradients/SkClampRange.cpp
+++ b/src/effects/gradients/SkClampRange.cpp
@@ -163,4 +163,3 @@
         fCount0 += extraCount;
     }
 }
-
diff --git a/src/effects/gradients/SkClampRange.h b/src/effects/gradients/SkClampRange.h
index 68a27e9..376dc93 100644
--- a/src/effects/gradients/SkClampRange.h
+++ b/src/effects/gradients/SkClampRange.h
@@ -36,4 +36,3 @@
 };
 
 #endif
-
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 5b2a60e..de9ae9e 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -364,14 +364,6 @@
     return 0;
 }
 
-/** We duplicate the last value in each half of the cache so that
-    interpolation doesn't have to special-case being at the last point.
-*/
-static void complete_16bit_cache(uint16_t* cache, int stride) {
-    cache[stride - 1] = cache[stride - 2];
-    cache[2 * stride - 1] = cache[2 * stride - 2];
-}
-
 const uint16_t* SkGradientShaderBase::getCache16() const {
     if (fCache16 == NULL) {
         // double the count for dither entries
@@ -384,7 +376,7 @@
         fCache16 = fCache16Storage;
         if (fColorCount == 2) {
             Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
-                            kGradient16Length);
+                            kCache16Count);
         } else {
             Rec* rec = fRecs;
             int prevIndex = 0;
@@ -396,8 +388,6 @@
                     Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
                 prevIndex = nextIndex;
             }
-            // one extra space left over at the end for complete_16bit_cache()
-            SkASSERT(prevIndex == kGradient16Length - 1);
         }
 
         if (fMapper) {
@@ -405,7 +395,7 @@
             uint16_t* linear = fCache16;         // just computed linear data
             uint16_t* mapped = fCache16Storage;  // storage for mapped data
             SkUnitMapper* map = fMapper;
-            for (int i = 0; i < kGradient16Length; i++) {
+            for (int i = 0; i < kCache16Count; i++) {
                 int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
                 mapped[i] = linear[index];
                 mapped[i + kCache16Count] = linear[index + kCache16Count];
@@ -413,19 +403,10 @@
             sk_free(fCache16);
             fCache16 = fCache16Storage;
         }
-        complete_16bit_cache(fCache16, kCache16Count);
     }
     return fCache16;
 }
 
-/** We duplicate the last value in each half of the cache so that
-    interpolation doesn't have to special-case being at the last point.
-*/
-static void complete_32bit_cache(SkPMColor* cache, int stride) {
-    cache[stride - 1] = cache[stride - 2];
-    cache[2 * stride - 1] = cache[2 * stride - 2];
-}
-
 const SkPMColor* SkGradientShaderBase::getCache32() const {
     if (fCache32 == NULL) {
         // double the count for dither entries
@@ -439,13 +420,13 @@
         fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
         if (fColorCount == 2) {
             Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
-                            kGradient32Length, fCacheAlpha);
+                            kCache32Count, fCacheAlpha);
         } else {
             Rec* rec = fRecs;
             int prevIndex = 0;
             for (int i = 1; i < fColorCount; i++) {
                 int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
-                SkASSERT(nextIndex < kGradient32Length);
+                SkASSERT(nextIndex < kCache32Count);
 
                 if (nextIndex > prevIndex)
                     Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
@@ -453,7 +434,6 @@
                                     nextIndex - prevIndex + 1, fCacheAlpha);
                 prevIndex = nextIndex;
             }
-            SkASSERT(prevIndex == kGradient32Length - 1);
         }
 
         if (fMapper) {
@@ -462,7 +442,7 @@
             SkPMColor* linear = fCache32;           // just computed linear data
             SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
             SkUnitMapper* map = fMapper;
-            for (int i = 0; i < kGradient32Length; i++) {
+            for (int i = 0; i < kCache32Count; i++) {
                 int index = map->mapUnit16((i << 8) | i) >> 8;
                 mapped[i] = linear[index];
                 mapped[i + kCache32Count] = linear[index + kCache32Count];
@@ -471,7 +451,6 @@
             fCache32PixelRef = newPR;
             fCache32 = (SkPMColor*)newPR->getAddr();
         }
-        complete_32bit_cache(fCache32, kCache32Count);
     }
     return fCache32;
 }
@@ -493,7 +472,7 @@
     if (fMapper) {
         // force our cahce32pixelref to be built
         (void)this->getCache32();
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
         bitmap->setPixelRef(fCache32PixelRef);
         return;
     }
@@ -533,9 +512,7 @@
     if (!gCache->find(storage.get(), size, bitmap)) {
         // force our cahce32pixelref to be built
         (void)this->getCache32();
-        // Only expose the linear section of the cache; don't let the caller
-        // know about the padding at the end to make interpolation faster.
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
         bitmap->setPixelRef(fCache32PixelRef);
 
         gCache->add(storage.get(), size, *bitmap);
@@ -546,16 +523,16 @@
     if (info) {
         if (info->fColorCount >= fColorCount) {
             if (info->fColors) {
-                memcpy(info->fColors, fOrigColors,
-                       fColorCount * sizeof(SkColor));
+                memcpy(info->fColors, fOrigColors, fColorCount * sizeof(SkColor));
             }
             if (info->fColorOffsets) {
                 if (fColorCount == 2) {
                     info->fColorOffsets[0] = 0;
                     info->fColorOffsets[1] = SK_Scalar1;
                 } else if (fColorCount > 2) {
-                    for (int i = 0; i < fColorCount; i++)
+                    for (int i = 0; i < fColorCount; ++i) {
                         info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
+                    }
                 }
             }
         }
@@ -564,6 +541,42 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkGradientShaderBase::toString(SkString* str) const {
+
+    str->appendf("%d colors: ", fColorCount);
+
+    for (int i = 0; i < fColorCount; ++i) {
+        str->appendHex(fOrigColors[i]);
+        if (i < fColorCount-1) {
+            str->append(", ");
+        }
+    }
+
+    if (fColorCount > 2) {
+        str->append(" points: (");
+        for (int i = 0; i < fColorCount; ++i) {
+            str->appendScalar(SkFixedToScalar(fRecs[i].fPos));
+            if (i < fColorCount-1) {
+                str->append(", ");
+            }
+        }
+        str->append(")");
+    }
+
+    static const char* gTileModeName[SkShader::kTileModeCount] = {
+        "clamp", "repeat", "mirror"
+    };
+
+    str->append(" ");
+    str->append(gTileModeName[fTileMode]);
+
+    // TODO: add "fMapper->toString(str);" when SkUnitMapper::toString is added
+
+    this->INHERITED::toString(str);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -691,7 +704,7 @@
 }
 
 void GrGLGradientEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
-    const GrGradientEffect& e = static_cast<const GrGradientEffect&>(*stage.getEffect());
+    const GrGradientEffect& e = GetEffectFromStage<GrGradientEffect>(stage);
     const GrTexture* texture = e.texture(0);
     fEffectMatrix.setData(uman, e.getMatrix(), stage.getCoordChangeMatrix(), texture);
 
@@ -703,7 +716,7 @@
 }
 
 GrGLEffect::EffectKey GrGLGradientEffect::GenMatrixKey(const GrEffectStage& s) {
-    const GrGradientEffect& e = static_cast<const GrGradientEffect&>(*s.getEffect());
+    const GrGradientEffect& e = GetEffectFromStage<GrGradientEffect>(s);
     const GrTexture* texture = e.texture(0);
     return GrGLEffectMatrix::GenKey(e.getMatrix(), s.getCoordChangeMatrix(), texture);
 }
@@ -742,8 +755,7 @@
 GrGradientEffect::GrGradientEffect(GrContext* ctx,
                                    const SkGradientShaderBase& shader,
                                    const SkMatrix& matrix,
-                                   SkShader::TileMode tileMode)
-    : INHERITED(1) {
+                                   SkShader::TileMode tileMode) {
     // TODO: check for simple cases where we don't need a texture:
     //GradientInfo info;
     //shader.asAGradient(&info);
@@ -754,6 +766,8 @@
     SkBitmap bitmap;
     shader.getGradientTableBitmap(&bitmap);
 
+    fIsOpaque = shader.isOpaque();
+
     GrTextureStripAtlas::Desc desc;
     desc.fWidth  = bitmap.width();
     desc.fHeight = 32;
@@ -774,15 +788,16 @@
                   fAtlas->getVerticalScaleFactor();
         fTextureAccess.reset(fAtlas->getTexture(), params);
     } else {
-        GrTexture* texture = GrLockCachedBitmapTexture(ctx, bitmap, &params);
+        GrTexture* texture = GrLockAndRefCachedBitmapTexture(ctx, bitmap, &params);
         fTextureAccess.reset(texture, params);
         fYCoord = SK_ScalarHalf;
 
         // Unlock immediately, this is not great, but we don't have a way of
         // knowing when else to unlock it currently, so it may get purged from
         // the cache, but it'll still be ref'd until it's no longer being used.
-        GrUnlockCachedBitmapTexture(texture);
+        GrUnlockAndUnrefCachedBitmapTexture(texture);
     }
+    this->addTextureAccess(&fTextureAccess);
 }
 
 GrGradientEffect::~GrGradientEffect() {
@@ -791,9 +806,22 @@
     }
 }
 
-const GrTextureAccess& GrGradientEffect::textureAccess(int index) const {
-    GrAssert(0 == index);
-    return fTextureAccess;
+bool GrGradientEffect::onIsEqual(const GrEffect& effect) const {
+    const GrGradientEffect& s = CastEffect<GrGradientEffect>(effect);
+    return fTextureAccess.getTexture() == s.fTextureAccess.getTexture()  &&
+           fTextureAccess.getParams().getTileModeX() ==
+                s.fTextureAccess.getParams().getTileModeX() &&
+           this->useAtlas() == s.useAtlas() &&
+           fYCoord == s.getYCoord() &&
+           fMatrix.cheapEqualTo(s.getMatrix());
+}
+
+void GrGradientEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    if (fIsOpaque && (kA_ValidComponentFlag & *validFlags) && 0xff == GrColorUnpackA(*color)) {
+        *validFlags = kA_ValidComponentFlag;
+    } else {
+        *validFlags = 0;
+    }
 }
 
 int GrGradientEffect::RandomGradientParams(SkRandom* random,
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 829d153..4d14dc6 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -24,7 +24,7 @@
     #define USE_DITHER_32BIT_GRADIENT
 #endif
 
-static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
+static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
                                int count) {
     if (count > 0) {
         if (v0 == v1) {
@@ -44,13 +44,13 @@
 
 //  Clamp
 
-static SkFixed clamp_tileproc(SkFixed x) {
+static inline SkFixed clamp_tileproc(SkFixed x) {
     return SkClampMax(x, 0xFFFF);
 }
 
 // Repeat
 
-static SkFixed repeat_tileproc(SkFixed x) {
+static inline SkFixed repeat_tileproc(SkFixed x) {
     return x & 0xFFFF;
 }
 
@@ -101,26 +101,14 @@
         /// Seems like enough for visual accuracy. TODO: if pos[] deserves
         /// it, use a larger cache.
         kCache16Bits    = 8,
-        kGradient16Length = (1 << kCache16Bits),
-        /// Each cache gets 1 extra entry at the end so we don't have to
-        /// test for end-of-cache in lerps. This is also the value used
-        /// to stride *writes* into the dither cache; it must not be zero.
-        /// Total space for a cache is 2x kCache16Count entries: one
-        /// regular cache, one for dithering.
-        kCache16Count   = kGradient16Length + 1,
+        kCache16Count = (1 << kCache16Bits),
         kCache16Shift   = 16 - kCache16Bits,
         kSqrt16Shift    = 8 - kCache16Bits,
 
         /// Seems like enough for visual accuracy. TODO: if pos[] deserves
         /// it, use a larger cache.
         kCache32Bits    = 8,
-        kGradient32Length = (1 << kCache32Bits),
-        /// Each cache gets 1 extra entry at the end so we don't have to
-        /// test for end-of-cache in lerps. This is also the value used
-        /// to stride *writes* into the dither cache; it must not be zero.
-        /// Total space for a cache is 2x kCache32Count entries: one
-        /// regular cache, one for dithering.
-        kCache32Count   = kGradient32Length + 1,
+        kCache32Count = (1 << kCache32Bits),
         kCache32Shift   = 16 - kCache32Bits,
         kSqrt32Shift    = 8 - kCache32Bits,
 
@@ -132,13 +120,13 @@
         kDitherStride32 = 0,
 #endif
         kDitherStride16 = kCache16Count,
-        kLerpRemainderMask32 = (1 << (16 - kCache32Bits)) - 1
     };
 
 
 protected:
     SkGradientShaderBase(SkFlattenableReadBuffer& );
     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+    SK_DEVELOPER_TO_STRING()
 
     SkUnitMapper* fMapper;
     SkMatrix    fPtsToUnit;     // set by subclass
@@ -187,6 +175,22 @@
     typedef SkShader INHERITED;
 };
 
+static inline int init_dither_toggle(int x, int y) {
+    return ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride32;
+}
+
+static inline int next_dither_toggle(int toggle) {
+    return toggle ^ SkGradientShaderBase::kDitherStride32;
+}
+
+static inline int init_dither_toggle16(int x, int y) {
+    return ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride16;
+}
+
+static inline int next_dither_toggle16(int toggle) {
+    return toggle ^ SkGradientShaderBase::kDitherStride16;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #if SK_SUPPORT_GPU
@@ -233,17 +237,11 @@
 
     virtual ~GrGradientEffect();
 
-    virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
-
     bool useAtlas() const { return SkToBool(-1 != fRow); }
     SkScalar getYCoord() const { return fYCoord; };
     const SkMatrix& getMatrix() const { return fMatrix;}
 
-    virtual bool isEqual(const GrEffect& effect) const SK_OVERRIDE {
-        const GrGradientEffect& s = static_cast<const GrGradientEffect&>(effect);
-        return INHERITED::isEqual(effect) && this->useAtlas() == s.useAtlas() &&
-               fYCoord == s.getYCoord() && fMatrix.cheapEqualTo(s.getMatrix());
-    }
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
 protected:
 
@@ -260,12 +258,16 @@
                                     SkScalar** stops,
                                     SkShader::TileMode* tm);
 
+    virtual bool onIsEqual(const GrEffect& effect) const SK_OVERRIDE;
+
 private:
+
     GrTextureAccess fTextureAccess;
     SkScalar fYCoord;
     GrTextureStripAtlas* fAtlas;
     int fRow;
     SkMatrix fMatrix;
+    bool fIsOpaque;
 
     typedef GrEffect INHERITED;
 
@@ -336,4 +338,3 @@
 #endif
 
 #endif
-
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
index 7161450..0d3cc7e 100644
--- a/src/effects/gradients/SkLinearGradient.cpp
+++ b/src/effects/gradients/SkLinearGradient.cpp
@@ -111,7 +111,7 @@
     SkASSERT(fi <= 0xFF);           \
     fx += dx;                       \
     *dstC++ = cache[toggle + fi];   \
-    toggle ^= SkGradientShaderBase::kDitherStride32; \
+    toggle = next_dither_toggle(toggle); \
     } while (0)
 
 namespace {
@@ -120,23 +120,6 @@
                                 SkPMColor* dstC, const SkPMColor* cache,
                                 int toggle, int count);
 
-// This function is deprecated, and will be replaced by
-// shadeSpan_linear_vertical_lerp() once Chrome has been weaned off of it.
-void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
-                               SkPMColor* SK_RESTRICT dstC,
-                               const SkPMColor* SK_RESTRICT cache,
-                               int toggle, int count) {
-    // We're a vertical gradient, so no change in a span.
-    // If colors change sharply across the gradient, dithering is
-    // insufficient (it subsamples the color space) and we need to lerp.
-    unsigned fullIndex = proc(fx);
-    unsigned fi = fullIndex >> (16 - SkGradientShaderBase::kCache32Bits);
-    sk_memset32_dither(dstC,
-            cache[toggle + fi],
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi],
-            count);
-}
-
 // Linear interpolation (lerp) is unnecessary if there are no sharp
 // discontinuities in the gradient - which must be true if there are
 // only 2 colors - but it's cheap.
@@ -148,16 +131,18 @@
     // If colors change sharply across the gradient, dithering is
     // insufficient (it subsamples the color space) and we need to lerp.
     unsigned fullIndex = proc(fx);
-    unsigned fi = fullIndex >> (16 - SkGradientShaderBase::kCache32Bits);
-    unsigned remainder = fullIndex & SkGradientShaderBase::kLerpRemainderMask32;
-    SkPMColor lerp =
-        SkFastFourByteInterp(
-            cache[toggle + fi + 1],
-            cache[toggle + fi], remainder);
-    SkPMColor dlerp =
-        SkFastFourByteInterp(
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi + 1],
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi], remainder);
+    unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
+    unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
+    
+    int index0 = fi + toggle;
+    int index1 = index0;
+    if (fi < SkGradientShaderBase::kCache32Count - 1) {
+        index1 += 1;
+    }
+    SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
+    index0 ^= SkGradientShaderBase::kDitherStride32;
+    index1 ^= SkGradientShaderBase::kDitherStride32;
+    SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
     sk_memset32_dither(dstC, lerp, dlerp, count);
 }
 
@@ -166,12 +151,12 @@
                             const SkPMColor* SK_RESTRICT cache,
                             int toggle, int count) {
     SkClampRange range;
-    range.init(fx, dx, count, 0, SkGradientShaderBase::kGradient32Length);
+    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
 
     if ((count = range.fCount0) > 0) {
         sk_memset32_dither(dstC,
             cache[toggle + range.fV0],
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + range.fV0],
+            cache[next_dither_toggle(toggle) + range.fV0],
             count);
         dstC += count;
     }
@@ -193,7 +178,7 @@
     if ((count = range.fCount2) > 0) {
         sk_memset32_dither(dstC,
             cache[toggle + range.fV1],
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + range.fV1],
+            cache[next_dither_toggle(toggle) + range.fV1],
             count);
     }
 }
@@ -207,7 +192,7 @@
         SkASSERT(fi <= 0xFF);
         fx += dx;
         *dstC++ = cache[toggle + fi];
-        toggle ^= SkGradientShaderBase::kDitherStride32;
+        toggle = next_dither_toggle(toggle);
     } while (--count != 0);
 }
 
@@ -220,7 +205,7 @@
         SkASSERT(fi <= 0xFF);
         fx += dx;
         *dstC++ = cache[toggle + fi];
-        toggle ^= SkGradientShaderBase::kDitherStride32;
+        toggle = next_dither_toggle(toggle);
     } while (--count != 0);
 }
 
@@ -235,7 +220,7 @@
     TileProc            proc = fTileProc;
     const SkPMColor* SK_RESTRICT cache = this->getCache32();
 #ifdef USE_DITHER_32BIT_GRADIENT
-    int                 toggle = ((x ^ y) & 1) * kDitherStride32;
+    int                 toggle = init_dither_toggle(x, y);
 #else
     int toggle = 0;
 #endif
@@ -256,15 +241,7 @@
 
         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
         if (SkFixedNearlyZero(dx)) {
-#ifdef SK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS
-            if (fColorCount > 2) {
-                shadeProc = shadeSpan_linear_vertical_lerp;
-            } else {
-                shadeProc = shadeSpan_linear_vertical;
-            }
-#else
             shadeProc = shadeSpan_linear_vertical_lerp;
-#endif
         } else if (SkShader::kClamp_TileMode == fTileMode) {
             shadeProc = shadeSpan_linear_clamp;
         } else if (SkShader::kMirror_TileMode == fTileMode) {
@@ -281,7 +258,7 @@
             unsigned fi = proc(SkScalarToFixed(srcPt.fX));
             SkASSERT(fi <= 0xFFFF);
             *dstC++ = cache[toggle + (fi >> kCache32Shift)];
-            toggle ^= SkGradientShaderBase::kDitherStride32;
+            toggle = next_dither_toggle(toggle);
             dstX += SK_Scalar1;
         } while (--count != 0);
     }
@@ -333,7 +310,7 @@
     SkASSERT(fi < SkGradientShaderBase::kCache16Count);       \
     fx += dx;                           \
     *dstC++ = cache[toggle + fi];       \
-    toggle ^= SkGradientShaderBase::kDitherStride16;            \
+    toggle = next_dither_toggle16(toggle);            \
     } while (0)
 
 namespace {
@@ -350,7 +327,7 @@
     unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
     SkASSERT(fi < SkGradientShaderBase::kCache16Count);
     dither_memset16(dstC, cache[toggle + fi],
-        cache[(toggle ^ SkGradientShaderBase::kDitherStride16) + fi], count);
+        cache[next_dither_toggle16(toggle) + fi], count);
 
 }
 
@@ -359,12 +336,12 @@
                               const uint16_t* SK_RESTRICT cache,
                               int toggle, int count) {
     SkClampRange range;
-    range.init(fx, dx, count, 0, SkGradientShaderBase::kGradient16Length);
+    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
 
     if ((count = range.fCount0) > 0) {
         dither_memset16(dstC,
             cache[toggle + range.fV0],
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride16) + range.fV0],
+            cache[next_dither_toggle16(toggle) + range.fV0],
             count);
         dstC += count;
     }
@@ -386,7 +363,7 @@
     if ((count = range.fCount2) > 0) {
         dither_memset16(dstC,
             cache[toggle + range.fV1],
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride16) + range.fV1],
+            cache[next_dither_toggle16(toggle) + range.fV1],
             count);
     }
 }
@@ -401,7 +378,7 @@
         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
         fx += dx;
         *dstC++ = cache[toggle + fi];
-        toggle ^= SkGradientShaderBase::kDitherStride16;
+        toggle = next_dither_toggle16(toggle);
     } while (--count != 0);
 }
 
@@ -415,7 +392,7 @@
         SkASSERT(fi < SkGradientShaderBase::kCache16Count);
         fx += dx;
         *dstC++ = cache[toggle + fi];
-        toggle ^= SkGradientShaderBase::kDitherStride16;
+        toggle = next_dither_toggle16(toggle);
     } while (--count != 0);
 }
 }
@@ -428,7 +405,7 @@
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
     TileProc            proc = fTileProc;
     const uint16_t* SK_RESTRICT cache = this->getCache16();
-    int                 toggle = ((x ^ y) & 1) * kDitherStride16;
+    int                 toggle = init_dither_toggle16(x, y);
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
@@ -465,7 +442,7 @@
 
             int index = fi >> kCache16Shift;
             *dstC++ = cache[toggle + index];
-            toggle ^= SkGradientShaderBase::kDitherStride16;
+            toggle = next_dither_toggle16(toggle);
 
             dstX += SK_Scalar1;
         } while (--count != 0);
@@ -481,8 +458,7 @@
 class GrGLLinearGradient : public GrGLGradientEffect {
 public:
 
-    GrGLLinearGradient(const GrBackendEffectFactory& factory,
-                       const GrEffect&)
+    GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrEffectRef&)
                        : INHERITED (factory) { }
 
     virtual ~GrGLLinearGradient() { }
@@ -509,11 +485,14 @@
 class GrLinearGradient : public GrGradientEffect {
 public:
 
-    GrLinearGradient(GrContext* ctx,
-                     const SkLinearGradient& shader,
-                     const SkMatrix& matrix,
-                     SkShader::TileMode tm)
-        : INHERITED(ctx, shader, matrix, tm) { }
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkLinearGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrLinearGradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
+    }
+
     virtual ~GrLinearGradient() { }
 
     static const char* Name() { return "Linear Gradient"; }
@@ -524,6 +503,11 @@
     typedef GrGLLinearGradient GLEffect;
 
 private:
+    GrLinearGradient(GrContext* ctx,
+                     const SkLinearGradient& shader,
+                     const SkMatrix& matrix,
+                     SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm) { }
     GR_DECLARE_EFFECT_TEST;
 
     typedef GrGradientEffect INHERITED;
@@ -533,9 +517,9 @@
 
 GR_DEFINE_EFFECT_TEST(GrLinearGradient);
 
-GrEffect* GrLinearGradient::TestCreate(SkRandom* random,
-                                       GrContext* context,
-                                       GrTexture**) {
+GrEffectRef* GrLinearGradient::TestCreate(SkRandom* random,
+                                          GrContext* context,
+                                          GrTexture**) {
     SkPoint points[] = {{random->nextUScalar1(), random->nextUScalar1()},
                         {random->nextUScalar1(), random->nextUScalar1()}};
 
@@ -547,12 +531,8 @@
     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
                                                                  colors, stops, colorCount,
                                                                  tm));
-    GrEffectStage stage;
-    shader->asNewEffect(context, &stage);
-    GrAssert(NULL != stage.getEffect());
-    // const_cast and ref is a hack! Will remove when asNewEffect returns GrEffect*
-    stage.getEffect()->ref();
-    return const_cast<GrEffect*>(stage.getEffect());
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -575,22 +555,34 @@
 
 /////////////////////////////////////////////////////////////////////
 
-bool SkLinearGradient::asNewEffect(GrContext* context, GrEffectStage* stage) const {
-    SkASSERT(NULL != context && NULL != stage);
+GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
     SkMatrix matrix;
     if (!this->getLocalMatrix().invert(&matrix)) {
-        return false;
+        return NULL;
     }
     matrix.postConcat(fPtsToUnit);
-    stage->setEffect(SkNEW_ARGS(GrLinearGradient, (context, *this, matrix, fTileMode)))->unref();
-    return true;
+    return GrLinearGradient::Create(context, *this, matrix, fTileMode);
 }
 
 #else
 
-bool SkLinearGradient::asNewEffect(GrContext*, GrEffectStage*) const {
+GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const {
     SkDEBUGFAIL("Should not call in GPU-less build");
-    return false;
+    return NULL;
 }
 
 #endif
+
+#ifdef SK_DEVELOPER
+void SkLinearGradient::toString(SkString* str) const {
+    str->append("SkLinearGradient (");
+
+    str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
+    str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h
index 7d87926..ff1796b 100644
--- a/src/effects/gradients/SkLinearGradient.h
+++ b/src/effects/gradients/SkLinearGradient.h
@@ -22,8 +22,9 @@
     virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
     virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
-    virtual bool asNewEffect(GrContext* context, GrEffectStage* stage) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLinearGradient)
 
 protected:
@@ -37,4 +38,3 @@
 };
 
 #endif
-
diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp
index 02a56da..328fe76 100644
--- a/src/effects/gradients/SkRadialGradient.cpp
+++ b/src/effects/gradients/SkRadialGradient.cpp
@@ -88,7 +88,7 @@
             fx += dx;
             *dstC++ = cache[toggle +
                             (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
-            toggle ^= SkGradientShaderBase::kDitherStride16;
+            toggle = next_dither_toggle16(toggle);
         } while (--count != 0);
     } else {
         do {
@@ -100,7 +100,7 @@
             fy += dy;
             *dstC++ = cache[toggle +
                             (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
-            toggle ^= SkGradientShaderBase::kDitherStride16;
+            toggle = next_dither_toggle16(toggle);
         } while (--count != 0);
     }
 }
@@ -123,7 +123,7 @@
         unsigned fi = mirror_tileproc(dist);
         SkASSERT(fi <= 0xFFFF);
         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
-        toggle ^= SkGradientShaderBase::kDitherStride16;
+        toggle = next_dither_toggle16(toggle);
         sfx += sdx;
         sfy += sdy;
     } while (--count != 0);
@@ -144,7 +144,7 @@
         fx += dx;
         fy += dy;
         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
-        toggle ^= SkGradientShaderBase::kDitherStride16;
+        toggle = next_dither_toggle16(toggle);
     } while (--count != 0);
 }
 
@@ -175,7 +175,7 @@
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
     TileProc            proc = fTileProc;
     const uint16_t* SK_RESTRICT cache = this->getCache16();
-    int                 toggle = ((x ^ y) & 1) * kDitherStride16;
+    int                 toggle = init_dither_toggle16(x, y);
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
@@ -214,7 +214,7 @@
 
             int index = fi >> (16 - kCache16Bits);
             *dstC++ = cache[toggle + index];
-            toggle ^= kDitherStride16;
+            toggle = next_dither_toggle16(toggle);
 
             dstX += SK_Scalar1;
         } while (--count != 0);
@@ -227,8 +227,8 @@
         this->getGradientTableBitmap(bitmap);
     }
     if (matrix) {
-        matrix->setScale(SkIntToScalar(kGradient32Length),
-                         SkIntToScalar(kGradient32Length));
+        matrix->setScale(SkIntToScalar(kCache32Count),
+                         SkIntToScalar(kCache32Count));
         matrix->preConcat(fPtsToUnit);
     }
     if (xy) {
@@ -296,7 +296,7 @@
     fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
     *dstC++ = cache[toggle + \
                     (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
-    toggle ^= SkGradientShaderBase::kDitherStride32; \
+    toggle = next_dither_toggle(toggle); \
     fx += dx; \
     fy += dy;
 
@@ -318,10 +318,10 @@
     SkFixed fy = SkScalarToFixed(sfy) >> 1;
     SkFixed dy = SkScalarToFixed(sdy) >> 1;
     if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
-        unsigned fi = SkGradientShaderBase::kGradient32Length;
+        unsigned fi = SkGradientShaderBase::kCache32Count - 1;
         sk_memset32_dither(dstC,
             cache[toggle + fi],
-            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi],
+            cache[next_dither_toggle(toggle) + fi],
             count);
     } else if ((count > 4) &&
                no_need_for_radial_pin(fx, dx, fy, dy, count)) {
@@ -347,7 +347,7 @@
                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
                     SkGradientShaderBase::kSqrt32Shift)];
-                toggle ^= SkGradientShaderBase::kDitherStride32;
+                toggle = next_dither_toggle(toggle);
                 fx += dx;
             } while (--count != 0);
         } else {
@@ -358,7 +358,7 @@
                 fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
                 *dstC++ = cache[toggle + (sqrt_table[fi] >>
                     SkGradientShaderBase::kSqrt32Shift)];
-                toggle ^= SkGradientShaderBase::kDitherStride32;
+                toggle = next_dither_toggle(toggle);
                 fx += dx;
                 fy += dy;
             } while (--count != 0);
@@ -387,7 +387,7 @@
         unsigned fi = mirror_tileproc(dist);
         SkASSERT(fi <= 0xFFFF);
         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
-        toggle ^= SkGradientShaderBase::kDitherStride32;
+        toggle = next_dither_toggle(toggle);
         sfx += sdx;
         sfy += sdy;
     } while (--count != 0);
@@ -410,7 +410,7 @@
         unsigned fi = repeat_tileproc(dist);
         SkASSERT(fi <= 0xFFFF);
         *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
-        toggle ^= SkGradientShaderBase::kDitherStride32;
+        toggle = next_dither_toggle(toggle);
         fx += dx;
         fy += dy;
     } while (--count != 0);
@@ -426,7 +426,7 @@
     TileProc            proc = fTileProc;
     const SkPMColor* SK_RESTRICT cache = this->getCache32();
 #ifdef USE_DITHER_32BIT_GRADIENT
-    int toggle = ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride32;
+    int toggle = init_dither_toggle(x, y);
 #else
     int toggle = 0;
 #endif
@@ -479,7 +479,7 @@
 public:
 
     GrGLRadialGradient(const GrBackendEffectFactory& factory,
-                       const GrEffect&) : INHERITED (factory) { }
+                       const GrEffectRef&) : INHERITED (factory) { }
     virtual ~GrGLRadialGradient() { }
 
     virtual void emitCode(GrGLShaderBuilder*,
@@ -504,12 +504,12 @@
 
 class GrRadialGradient : public GrGradientEffect {
 public:
-
-    GrRadialGradient(GrContext* ctx,
-                     const SkRadialGradient& shader,
-                     const SkMatrix& matrix,
-                     SkShader::TileMode tm)
-        : INHERITED(ctx, shader, matrix, tm) {
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkRadialGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
     }
 
     virtual ~GrRadialGradient() { }
@@ -522,6 +522,13 @@
     typedef GrGLRadialGradient GLEffect;
 
 private:
+    GrRadialGradient(GrContext* ctx,
+                     const SkRadialGradient& shader,
+                     const SkMatrix& matrix,
+                     SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm) {
+    }
+
     GR_DECLARE_EFFECT_TEST;
 
     typedef GrGradientEffect INHERITED;
@@ -531,9 +538,9 @@
 
 GR_DEFINE_EFFECT_TEST(GrRadialGradient);
 
-GrEffect* GrRadialGradient::TestCreate(SkRandom* random,
-                                       GrContext* context,
-                                       GrTexture**) {
+GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
+                                          GrContext* context,
+                                          GrTexture**) {
     SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
     SkScalar radius = random->nextUScalar1();
 
@@ -545,12 +552,8 @@
     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
                                                                  colors, stops, colorCount,
                                                                  tm));
-    GrEffectStage stage;
-    shader->asNewEffect(context, &stage);
-    GrAssert(NULL != stage.getEffect());
-    // const_cast and ref is a hack! Will remove when asNewEffect returns GrEffect*
-    stage.getEffect()->ref();
-    return const_cast<GrEffect*>(stage.getEffect());
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -573,23 +576,40 @@
 
 /////////////////////////////////////////////////////////////////////
 
-bool SkRadialGradient::asNewEffect(GrContext* context, GrEffectStage* stage) const {
-    SkASSERT(NULL != context && NULL != stage);
+GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
 
     SkMatrix matrix;
     if (!this->getLocalMatrix().invert(&matrix)) {
-        return false;
+        return NULL;
     }
     matrix.postConcat(fPtsToUnit);
-    stage->setEffect(SkNEW_ARGS(GrRadialGradient, (context, *this, matrix, fTileMode)))->unref();
-    return true;
+    return GrRadialGradient::Create(context, *this, matrix, fTileMode);
 }
 
 #else
 
-bool SkRadialGradient::asNewEffect(GrContext*, GrEffectStage*) const {
+GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
     SkDEBUGFAIL("Should not call in GPU-less build");
-    return false;
+    return NULL;
 }
 
 #endif
+
+#ifdef SK_DEVELOPER
+void SkRadialGradient::toString(SkString* str) const {
+    str->append("SkRadialGradient: (");
+
+    str->append("center: (");
+    str->appendScalar(fCenter.fX);
+    str->append(", ");
+    str->appendScalar(fCenter.fY);
+    str->append(") radius: ");
+    str->appendScalar(fRadius);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkRadialGradient.h b/src/effects/gradients/SkRadialGradient.h
index cf0d43d..83f79ae 100644
--- a/src/effects/gradients/SkRadialGradient.h
+++ b/src/effects/gradients/SkRadialGradient.h
@@ -24,8 +24,9 @@
                                  SkMatrix* matrix,
                                  TileMode* xy) const SK_OVERRIDE;
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
-    virtual bool asNewEffect(GrContext* context, GrEffectStage* stage) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialGradient)
 
 protected:
@@ -39,4 +40,3 @@
 };
 
 #endif
-
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
index 589cf4a..db18521 100644
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -337,7 +337,7 @@
     SkMatrix::MapXYProc proc = fDstToIndexProc;
     const SkMatrix&     matrix = fDstToIndex;
     const uint16_t* SK_RESTRICT cache = this->getCache16();
-    int                 toggle = ((x ^ y) & 1) * kDitherStride16;
+    int                 toggle = init_dither_toggle16(x, y);
     SkPoint             srcPt;
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
@@ -361,7 +361,7 @@
         for (; count > 0; --count) {
             int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
             *dstC++ = cache[toggle + index];
-            toggle ^= kDitherStride16;
+            toggle = next_dither_toggle16(toggle);
             fx += dx;
             fy += dy;
         }
@@ -373,7 +373,7 @@
             int index = SkATan2_255(srcPt.fY, srcPt.fX);
             index >>= (8 - kCache16Bits);
             *dstC++ = cache[toggle + index];
-            toggle ^= kDitherStride16;
+            toggle = next_dither_toggle16(toggle);
         }
     }
 }
@@ -388,7 +388,7 @@
 public:
 
     GrGLSweepGradient(const GrBackendEffectFactory& factory,
-                      const GrEffect&) : INHERITED (factory) { }
+                      const GrEffectRef&) : INHERITED (factory) { }
     virtual ~GrGLSweepGradient() { }
 
     virtual void emitCode(GrGLShaderBuilder*,
@@ -413,11 +413,12 @@
 
 class GrSweepGradient : public GrGradientEffect {
 public:
-
-    GrSweepGradient(GrContext* ctx,
-                    const SkSweepGradient& shader,
-                    const SkMatrix& matrix)
-    : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkSweepGradient& shader,
+                               const SkMatrix& matrix) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix)));
+        return CreateEffectRef(effect);
+    }
     virtual ~GrSweepGradient() { }
 
     static const char* Name() { return "Sweep Gradient"; }
@@ -428,6 +429,10 @@
     typedef GrGLSweepGradient GLEffect;
 
 private:
+    GrSweepGradient(GrContext* ctx,
+                    const SkSweepGradient& shader,
+                    const SkMatrix& matrix)
+    : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
     GR_DECLARE_EFFECT_TEST;
 
     typedef GrGradientEffect INHERITED;
@@ -437,9 +442,9 @@
 
 GR_DEFINE_EFFECT_TEST(GrSweepGradient);
 
-GrEffect* GrSweepGradient::TestCreate(SkRandom* random,
-                                      GrContext* context,
-                                      GrTexture**) {
+GrEffectRef* GrSweepGradient::TestCreate(SkRandom* random,
+                                         GrContext* context,
+                                         GrTexture**) {
     SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
 
     SkColor colors[kMaxRandomGradientColors];
@@ -449,12 +454,8 @@
     int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
     SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
                                                                 colors, stops, colorCount));
-    GrEffectStage stage;
-    shader->asNewEffect(context, &stage);
-    GrAssert(NULL != stage.getEffect());
-    // const_cast and ref is a hack! Will remove when asNewEffect returns GrEffect*
-    stage.getEffect()->ref();
-    return const_cast<GrEffect*>(stage.getEffect());
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -476,21 +477,36 @@
 
 /////////////////////////////////////////////////////////////////////
 
-bool SkSweepGradient::asNewEffect(GrContext* context, GrEffectStage* stage) const {
+GrEffectRef* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
     SkMatrix matrix;
     if (!this->getLocalMatrix().invert(&matrix)) {
-        return false;
+        return NULL;
     }
     matrix.postConcat(fPtsToUnit);
-    stage->setEffect(SkNEW_ARGS(GrSweepGradient, (context, *this, matrix)))->unref();
-    return true;
+    return GrSweepGradient::Create(context, *this, matrix);
 }
 
 #else
 
-bool SkSweepGradient::asNewEffect(GrContext*, GrEffectStage*) const {
+GrEffectRef* SkSweepGradient::asNewEffect(GrContext*, const SkPaint&) const {
     SkDEBUGFAIL("Should not call in GPU-less build");
-    return false;
+    return NULL;
 }
 
 #endif
+
+#ifdef SK_DEVELOPER
+void SkSweepGradient::toString(SkString* str) const {
+    str->append("SkSweepGradient: (");
+
+    str->append("center: (");
+    str->appendScalar(fCenter.fX);
+    str->append(", ");
+    str->appendScalar(fCenter.fY);
+    str->append(") ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h
index a44b4c1..d572429 100644
--- a/src/effects/gradients/SkSweepGradient.h
+++ b/src/effects/gradients/SkSweepGradient.h
@@ -24,8 +24,9 @@
 
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
 
-    virtual bool asNewEffect(GrContext* context, GrEffectStage* stage) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient)
 
 protected:
@@ -38,4 +39,3 @@
 };
 
 #endif
-
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
index 5c06bce..e239530 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -325,7 +325,7 @@
 public:
 
     GrGLConical2Gradient(const GrBackendEffectFactory& factory,
-                         const GrEffect&);
+                         const GrEffectRef&);
     virtual ~GrGLConical2Gradient() { }
 
     virtual void emitCode(GrGLShaderBuilder*,
@@ -369,14 +369,13 @@
 class GrConical2Gradient : public GrGradientEffect {
 public:
 
-    GrConical2Gradient(GrContext* ctx,
-                       const SkTwoPointConicalGradient& shader,
-                       const SkMatrix& matrix,
-                       SkShader::TileMode tm)
-        : INHERITED(ctx, shader, matrix, tm)
-        , fCenterX1(shader.getCenterX1())
-        , fRadius0(shader.getStartRadius())
-        , fDiffRadius(shader.getDiffRadius()) { }
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointConicalGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
+    }
 
     virtual ~GrConical2Gradient() { }
 
@@ -384,13 +383,6 @@
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendEffectFactory<GrConical2Gradient>::getInstance();
     }
-    virtual bool isEqual(const GrEffect& sBase) const SK_OVERRIDE {
-        const GrConical2Gradient& s = static_cast<const GrConical2Gradient&>(sBase);
-        return (INHERITED::isEqual(sBase) &&
-                this->fCenterX1 == s.fCenterX1 &&
-                this->fRadius0 == s.fRadius0 &&
-                this->fDiffRadius == s.fDiffRadius);
-    }
 
     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
     bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
@@ -401,6 +393,23 @@
     typedef GrGLConical2Gradient GLEffect;
 
 private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fDiffRadius == s.fDiffRadius);
+    }
+
+    GrConical2Gradient(GrContext* ctx,
+                       const SkTwoPointConicalGradient& shader,
+                       const SkMatrix& matrix,
+                       SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm)
+        , fCenterX1(shader.getCenterX1())
+        , fRadius0(shader.getStartRadius())
+        , fDiffRadius(shader.getDiffRadius()) { }
+
     GR_DECLARE_EFFECT_TEST;
 
     // @{
@@ -418,9 +427,9 @@
 
 GR_DEFINE_EFFECT_TEST(GrConical2Gradient);
 
-GrEffect* GrConical2Gradient::TestCreate(SkRandom* random,
-                                         GrContext* context,
-                                         GrTexture**) {
+GrEffectRef* GrConical2Gradient::TestCreate(SkRandom* random,
+                                            GrContext* context,
+                                            GrTexture**) {
     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
     SkScalar radius1 = random->nextUScalar1();
     SkPoint center2;
@@ -440,20 +449,15 @@
                                                                           center2, radius2,
                                                                           colors, stops, colorCount,
                                                                           tm));
-    GrEffectStage stage;
-    shader->asNewEffect(context, &stage);
-    GrAssert(NULL != stage.getEffect());
-    // const_cast and ref is a hack! Will remove when asNewEffect returns GrEffect*
-    stage.getEffect()->ref();
-    return const_cast<GrEffect*>(stage.getEffect());
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
 }
 
 
 /////////////////////////////////////////////////////////////////////
 
-GrGLConical2Gradient::GrGLConical2Gradient(
-        const GrBackendEffectFactory& factory,
-        const GrEffect& baseData)
+GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory,
+                                           const GrEffectRef& baseData)
     : INHERITED(factory)
     , fVSParamUni(kInvalidUniformHandle)
     , fFSParamUni(kInvalidUniformHandle)
@@ -463,8 +467,7 @@
     , fCachedRadius(-SK_ScalarMax)
     , fCachedDiffRadius(-SK_ScalarMax) {
 
-    const GrConical2Gradient& data =
-        static_cast<const GrConical2Gradient&>(baseData);
+    const GrConical2Gradient& data = CastEffect<GrConical2Gradient>(baseData);
     fIsDegenerate = data.isDegenerate();
 }
 
@@ -640,7 +643,7 @@
 
 void GrGLConical2Gradient::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
     INHERITED::setData(uman, stage);
-    const GrConical2Gradient& data = static_cast<const GrConical2Gradient&>(*stage.getEffect());
+    const GrConical2Gradient& data = GetEffectFromStage<GrConical2Gradient>(stage);
     GrAssert(data.isDegenerate() == fIsDegenerate);
     SkScalar centerX1 = data.center();
     SkScalar radius0 = data.radius();
@@ -680,7 +683,7 @@
     };
 
     EffectKey key = GenMatrixKey(s);
-    if (static_cast<const GrConical2Gradient&>(*s.getEffect()).isDegenerate()) {
+    if (GetEffectFromStage<GrConical2Gradient>(s).isDegenerate()) {
         key |= kIsDegenerate;
     }
     return key;
@@ -688,14 +691,13 @@
 
 /////////////////////////////////////////////////////////////////////
 
-bool SkTwoPointConicalGradient::asNewEffect(GrContext* context,
-                                            GrEffectStage* stage) const {
-    SkASSERT(NULL != context && NULL != stage);
+GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
     SkASSERT(fPtsToUnit.isIdentity());
     // invert the localM, translate to center1, rotate so center2 is on x axis.
     SkMatrix matrix;
     if (!this->getLocalMatrix().invert(&matrix)) {
-        return false;
+        return NULL;
     }
     matrix.postTranslate(-fCenter1.fX, -fCenter1.fY);
 
@@ -709,16 +711,40 @@
         matrix.postConcat(rot);
     }
 
-    stage->setEffect(SkNEW_ARGS(GrConical2Gradient, (context, *this, matrix, fTileMode)))->unref();
-
-    return true;
+    return GrConical2Gradient::Create(context, *this, matrix, fTileMode);
 }
 
 #else
 
-bool SkTwoPointConicalGradient::asNewEffect(GrContext*, GrEffectStage*) const {
+GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&) const {
     SkDEBUGFAIL("Should not call in GPU-less build");
-    return false;
+    return NULL;
 }
 
 #endif
+
+#ifdef SK_DEVELOPER
+void SkTwoPointConicalGradient::toString(SkString* str) const {
+    str->append("SkTwoPointConicalGradient: (");
+
+    str->append("center1: (");
+    str->appendScalar(fCenter1.fX);
+    str->append(", ");
+    str->appendScalar(fCenter1.fY);
+    str->append(") radius1: ");
+    str->appendScalar(fRadius1);
+    str->append(" ");
+
+    str->append("center2: (");
+    str->appendScalar(fCenter2.fX);
+    str->append(", ");
+    str->appendScalar(fCenter2.fY);
+    str->append(") radius2: ");
+    str->appendScalar(fRadius2);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h
index d199650..e02c390 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.h
+++ b/src/effects/gradients/SkTwoPointConicalGradient.h
@@ -61,12 +61,13 @@
                                  SkMatrix* matrix,
                                  TileMode* xy) const;
     virtual SkShader::GradientType asAGradient(GradientInfo* info) const  SK_OVERRIDE;
-    virtual bool asNewEffect(GrContext* context, GrEffectStage* stage) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint& paint) const SK_OVERRIDE;
 
     SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
     SkScalar getStartRadius() const { return fRadius1; }
     SkScalar getDiffRadius() const { return fRadius2 - fRadius1; }
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient)
 
 protected:
@@ -82,4 +83,3 @@
 };
 
 #endif
-
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp
index 9aa923b..4eaf487 100644
--- a/src/effects/gradients/SkTwoPointRadialGradient.cpp
+++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -309,6 +309,32 @@
     return true;
 }
 
+#ifdef SK_DEVELOPER
+void SkTwoPointRadialGradient::toString(SkString* str) const {
+    str->append("SkTwoPointRadialGradient: (");
+
+    str->append("center1: (");
+    str->appendScalar(fCenter1.fX);
+    str->append(", ");
+    str->appendScalar(fCenter1.fY);
+    str->append(") radius1: ");
+    str->appendScalar(fRadius1);
+    str->append(" ");
+
+    str->append("center2: (");
+    str->appendScalar(fCenter2.fX);
+    str->append(", ");
+    str->appendScalar(fCenter2.fY);
+    str->append(") radius2: ");
+    str->appendScalar(fRadius2);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
     SkFlattenableReadBuffer& buffer)
     : INHERITED(buffer),
@@ -358,8 +384,7 @@
 
 public:
 
-    GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
-                        const GrEffect&);
+    GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrEffectRef&);
     virtual ~GrGLRadial2Gradient() { }
 
     virtual void emitCode(GrGLShaderBuilder*,
@@ -402,28 +427,20 @@
 
 class GrRadial2Gradient : public GrGradientEffect {
 public:
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointRadialGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
+    }
 
-    GrRadial2Gradient(GrContext* ctx,
-                      const SkTwoPointRadialGradient& shader,
-                      const SkMatrix& matrix,
-                      SkShader::TileMode tm)
-        : INHERITED(ctx, shader, matrix, tm)
-        , fCenterX1(shader.getCenterX1())
-        , fRadius0(shader.getStartRadius())
-        , fPosRoot(shader.getDiffRadius() < 0) { }
     virtual ~GrRadial2Gradient() { }
 
     static const char* Name() { return "Two-Point Radial Gradient"; }
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
         return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
     }
-    virtual bool isEqual(const GrEffect& sBase) const SK_OVERRIDE {
-        const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
-        return (INHERITED::isEqual(sBase) &&
-                this->fCenterX1 == s.fCenterX1 &&
-                this->fRadius0 == s.fRadius0 &&
-                this->fPosRoot == s.fPosRoot);
-    }
 
     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
     bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
@@ -434,6 +451,23 @@
     typedef GrGLRadial2Gradient GLEffect;
 
 private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fPosRoot == s.fPosRoot);
+    }
+
+    GrRadial2Gradient(GrContext* ctx,
+                      const SkTwoPointRadialGradient& shader,
+                      const SkMatrix& matrix,
+                      SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm)
+        , fCenterX1(shader.getCenterX1())
+        , fRadius0(shader.getStartRadius())
+        , fPosRoot(shader.getDiffRadius() < 0) { }
+
     GR_DECLARE_EFFECT_TEST;
 
     // @{
@@ -453,9 +487,9 @@
 
 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
 
-GrEffect* GrRadial2Gradient::TestCreate(SkRandom* random,
-                                        GrContext* context,
-                                        GrTexture**) {
+GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
+                                           GrContext* context,
+                                           GrTexture**) {
     SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
     SkScalar radius1 = random->nextUScalar1();
     SkPoint center2;
@@ -463,7 +497,7 @@
     do {
         center2.set(random->nextUScalar1(), random->nextUScalar1());
         radius2 = random->nextUScalar1 ();
-        // There is a bug in two point radial gradients with idenitical radii
+        // There is a bug in two point radial gradients with identical radii
     } while (radius1 == radius2);
 
     SkColor colors[kMaxRandomGradientColors];
@@ -475,19 +509,14 @@
                                                                          center2, radius2,
                                                                          colors, stops, colorCount,
                                                                          tm));
-    GrEffectStage stage;
-    shader->asNewEffect(context, &stage);
-    GrAssert(NULL != stage.getEffect());
-    // const_cast and ref is a hack! Will remove when asNewEffect returns GrEffect*
-    stage.getEffect()->ref();
-    return const_cast<GrEffect*>(stage.getEffect());
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
 }
 
 /////////////////////////////////////////////////////////////////////
 
-GrGLRadial2Gradient::GrGLRadial2Gradient(
-        const GrBackendEffectFactory& factory,
-        const GrEffect& baseData)
+GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
+                                         const GrEffectRef& baseData)
     : INHERITED(factory)
     , fVSParamUni(kInvalidUniformHandle)
     , fFSParamUni(kInvalidUniformHandle)
@@ -497,8 +526,7 @@
     , fCachedRadius(-SK_ScalarMax)
     , fCachedPosRoot(0) {
 
-    const GrRadial2Gradient& data =
-        static_cast<const GrRadial2Gradient&>(baseData);
+    const GrRadial2Gradient& data = CastEffect<GrRadial2Gradient>(baseData);
     fIsDegenerate = data.isDegenerate();
 }
 
@@ -615,7 +643,7 @@
 
 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
     INHERITED::setData(uman, stage);
-    const GrRadial2Gradient& data = static_cast<const GrRadial2Gradient&>(*stage.getEffect());
+    const GrRadial2Gradient& data = GetEffectFromStage<GrRadial2Gradient>(stage);
     GrAssert(data.isDegenerate() == fIsDegenerate);
     SkScalar centerX1 = data.center();
     SkScalar radius0 = data.radius();
@@ -653,7 +681,7 @@
     };
 
     EffectKey key = GenMatrixKey(s);
-    if (static_cast<const GrRadial2Gradient&>(*s.getEffect()).isDegenerate()) {
+    if (GetEffectFromStage<GrRadial2Gradient>(s).isDegenerate()) {
         key |= kIsDegenerate;
     }
     return key;
@@ -661,13 +689,12 @@
 
 /////////////////////////////////////////////////////////////////////
 
-bool SkTwoPointRadialGradient::asNewEffect(GrContext* context,
-                                           GrEffectStage* stage) const {
-    SkASSERT(NULL != context && NULL != stage);
+GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
     // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
     SkMatrix matrix;
     if (!this->getLocalMatrix().invert(&matrix)) {
-        return false;
+        return NULL;
     }
     matrix.postConcat(fPtsToUnit);
 
@@ -680,15 +707,14 @@
         matrix.postConcat(rot);
     }
 
-    stage->setEffect(SkNEW_ARGS(GrRadial2Gradient, (context, *this, matrix, fTileMode)))->unref();
-    return true;
+    return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
 }
 
 #else
 
-bool SkTwoPointRadialGradient::asNewEffect(GrContext*, GrEffectStage*) const {
+GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
     SkDEBUGFAIL("Should not call in GPU-less build");
-    return false;
+    return NULL;
 }
 
 #endif
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.h b/src/effects/gradients/SkTwoPointRadialGradient.h
index e7e451a..e82fc75 100644
--- a/src/effects/gradients/SkTwoPointRadialGradient.h
+++ b/src/effects/gradients/SkTwoPointRadialGradient.h
@@ -23,7 +23,7 @@
                                  SkMatrix* matrix,
                                  TileMode* xy) const SK_OVERRIDE;
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
-    virtual bool asNewEffect(GrContext* context, GrEffectStage* stage) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
 
     virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
                            int count) SK_OVERRIDE;
@@ -35,6 +35,7 @@
     SkScalar getStartRadius() const { return fStartRadius; }
     SkScalar getDiffRadius() const { return fDiffRadius; }
 
+    SK_DEVELOPER_TO_STRING()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointRadialGradient)
 
 protected:
@@ -54,4 +55,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/FlingState.cpp b/src/gpu/FlingState.cpp
index 8d9e1c5..f0db501 100644
--- a/src/gpu/FlingState.cpp
+++ b/src/gpu/FlingState.cpp
@@ -123,5 +123,3 @@
     }
     return fValue0 + t * (fValue1 - fValue0);
 }
-
-
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index e0a80d3..930d8a2 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -454,7 +454,7 @@
     const SkMatrix* vm = &adcd.getOriginalMatrix();
 
     GrVertexLayout layout = 0;
-    layout |= GrDrawTarget::kEdge_VertexLayoutBit;
+    layout |= GrDrawState::kEdge_VertexLayoutBit;
 
     // We use the fact that SkPath::transform path does subdivision based on
     // perspective. Otherwise, we apply the view matrix when copying to the
@@ -501,4 +501,3 @@
 
     return true;
 }
-
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 347e4b5..c229f66 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -502,7 +502,7 @@
     target->getClip()->getConservativeBounds(drawState.getRenderTarget(),
                                              &devClipBounds);
 
-    GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
+    GrVertexLayout layout = GrDrawState::kEdge_VertexLayoutBit;
     SkMatrix viewM = drawState.getViewMatrix();
 
     PREALLOC_PTARRAY(128) lines;
@@ -514,7 +514,7 @@
     *lineCnt = lines.count() / 2;
     int vertCnt = kVertsPerLineSeg * *lineCnt + kVertsPerQuad * *quadCnt;
 
-    GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout));
+    GrAssert(sizeof(Vertex) == GrDrawState::VertexSize(layout));
 
     if (!arg->set(target, layout, vertCnt, 0)) {
         return false;
@@ -624,4 +624,3 @@
     drawState->setVertexEdgeType(oldEdgeType);
     return true;
 }
-
diff --git a/src/gpu/GrAAHairLinePathRenderer.h b/src/gpu/GrAAHairLinePathRenderer.h
index 20be696..7d1a6db 100644
--- a/src/gpu/GrAAHairLinePathRenderer.h
+++ b/src/gpu/GrAAHairLinePathRenderer.h
@@ -48,4 +48,3 @@
 
 
 #endif
-
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index b9d17f8..b23ed9a 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -16,9 +16,9 @@
 static GrVertexLayout aa_rect_layout(bool useCoverage) {
     GrVertexLayout layout = 0;
     if (useCoverage) {
-        layout |= GrDrawTarget::kCoverage_VertexLayoutBit;
+        layout |= GrDrawState::kCoverage_VertexLayoutBit;
     } else {
-        layout |= GrDrawTarget::kColor_VertexLayoutBit;
+        layout |= GrDrawState::kColor_VertexLayoutBit;
     }
     return layout;
 }
@@ -127,7 +127,7 @@
                                   bool useVertexCoverage) {
     GrVertexLayout layout = aa_rect_layout(useVertexCoverage);
 
-    size_t vsize = GrDrawTarget::VertexSize(layout);
+    size_t vsize = GrDrawState::VertexSize(layout);
 
     GrDrawTarget::AutoReleaseGeometry geo(target, layout, 8, 0);
     if (!geo.succeeded()) {
@@ -196,7 +196,7 @@
         return;
     }
     GrVertexLayout layout = aa_rect_layout(useVertexCoverage);
-    size_t vsize = GrDrawTarget::VertexSize(layout);
+    size_t vsize = GrDrawState::VertexSize(layout);
 
     GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0);
     if (!geo.succeeded()) {
diff --git a/src/gpu/GrAddPathRenderers_default.cpp b/src/gpu/GrAddPathRenderers_default.cpp
index 69be15a..4f17243 100644
--- a/src/gpu/GrAddPathRenderers_default.cpp
+++ b/src/gpu/GrAddPathRenderers_default.cpp
@@ -10,8 +10,20 @@
 #include "GrStencilAndCoverPathRenderer.h"
 #include "GrAAHairLinePathRenderer.h"
 #include "GrAAConvexPathRenderer.h"
+#if GR_STROKE_PATH_RENDERING
+#include "../../experimental/StrokePathRenderer/GrStrokePathRenderer.h"
+#endif
+#if GR_ANDROID_PATH_RENDERING
+#include "../../experimental/AndroidPathRenderer/GrAndroidPathRenderer.h"
+#endif
 
 void GrPathRenderer::AddPathRenderers(GrContext* ctx, GrPathRendererChain* chain) {
+#if GR_STROKE_PATH_RENDERING
+    chain->addPathRenderer(SkNEW(GrStrokePathRenderer))->unref();
+#endif
+#if GR_ANDROID_PATH_RENDERING
+    chain->addPathRenderer(SkNEW(GrAndroidPathRenderer))->unref();
+#endif
     if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(ctx)) {
         chain->addPathRenderer(pr)->unref();
     }
diff --git a/src/gpu/GrAllocPool.cpp b/src/gpu/GrAllocPool.cpp
index 39f8350..971f8ee 100644
--- a/src/gpu/GrAllocPool.cpp
+++ b/src/gpu/GrAllocPool.cpp
@@ -116,5 +116,3 @@
 }
 
 #endif
-
-
diff --git a/src/gpu/GrAllocPool.h b/src/gpu/GrAllocPool.h
index 3489cb9..4f58f00 100644
--- a/src/gpu/GrAllocPool.h
+++ b/src/gpu/GrAllocPool.h
@@ -61,4 +61,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp
index 491f6cf..48a4f39 100644
--- a/src/gpu/GrAtlas.cpp
+++ b/src/gpu/GrAtlas.cpp
@@ -177,6 +177,7 @@
     GrAssert(0 == kA8_GrMaskFormat);
     GrAssert(1 == kA565_GrMaskFormat);
     if (NULL == fTexture[format]) {
+        // TODO: Update this to use the cache rather than directly creating a texture.
         GrTextureDesc desc;
         desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
         desc.fWidth = GR_ATLAS_TEXTURE_WIDTH;
@@ -203,5 +204,3 @@
     GrAssert(fPlotMgr->isBusy(x, y));
     fPlotMgr->freePlot(x, y);
 }
-
-
diff --git a/src/gpu/GrAtlas.h b/src/gpu/GrAtlas.h
index f0114e3..40a2154 100644
--- a/src/gpu/GrAtlas.h
+++ b/src/gpu/GrAtlas.h
@@ -13,7 +13,6 @@
 
 #include "GrPoint.h"
 #include "GrTexture.h"
-#include "GrTDArray.h"
 
 class GrGpu;
 class GrRectanizer;
@@ -80,5 +79,3 @@
 };
 
 #endif
-
-
diff --git a/src/gpu/GrBinHashKey.h b/src/gpu/GrBinHashKey.h
index d2194e9..8fa53ef 100644
--- a/src/gpu/GrBinHashKey.h
+++ b/src/gpu/GrBinHashKey.h
@@ -16,25 +16,27 @@
  *  Hash function class that can take a data chunk of any predetermined length. The hash function
  *  used is the One-at-a-Time Hash (http://burtleburtle.net/bob/hash/doobs.html).
  *
- *  Keys are computed from Entry objects. Entry must be fully ordered by a member:
- *      int compare(const GrTBinHashKey<Entry, ..>& k);
- *  which returns negative if the Entry < k, 0 if it equals k, and positive if k < the Entry.
- *  Additionally, Entry must be flattenable into the key using setKeyData.
+ *  Keys are computed from ENTRY objects. ENTRY must be fully ordered by a member:
+ *      int compare(const GrTBinHashKey<ENTRY, ..>& k);
+ *  which returns negative if the ENTRY < k, 0 if it equals k, and positive if k < the ENTRY.
+ *  Additionally, ENTRY must be flattenable into the key using setKeyData.
  *
  *  This class satisfies the requirements to be a key for a GrTHashTable.
  */
-template<typename Entry, size_t KeySize>
+template<typename ENTRY, size_t KEY_SIZE>
 class GrTBinHashKey {
 public:
+    enum { kKeySize = KEY_SIZE };
+
     GrTBinHashKey() {
         this->reset();
     }
 
-    GrTBinHashKey(const GrTBinHashKey<Entry, KeySize>& other) {
+    GrTBinHashKey(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) {
         *this = other;
     }
 
-    GrTBinHashKey<Entry, KeySize>& operator=(const GrTBinHashKey<Entry, KeySize>& other) {
+    GrTBinHashKey<ENTRY, KEY_SIZE>& operator=(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) {
         memcpy(this, &other, sizeof(*this));
         return *this;
     }
@@ -50,11 +52,11 @@
     }
 
     void setKeyData(const uint32_t* SK_RESTRICT data) {
-        GrAssert(GrIsALIGN4(KeySize));
-        memcpy(&fData, data, KeySize);
+        GrAssert(GrIsALIGN4(KEY_SIZE));
+        memcpy(&fData, data, KEY_SIZE);
 
         uint32_t hash = 0;
-        size_t len = KeySize;
+        size_t len = KEY_SIZE;
         while (len >= 4) {
             hash += *data++;
             hash += (fHash << 10);
@@ -70,17 +72,17 @@
         fHash = hash;
     }
 
-    int compare(const GrTBinHashKey<Entry, KeySize>& key) const {
+    int compare(const GrTBinHashKey<ENTRY, KEY_SIZE>& key) const {
         GrAssert(fIsValid && key.fIsValid);
-        return memcmp(fData, key.fData, KeySize);
+        return memcmp(fData, key.fData, KEY_SIZE);
     }
 
-    static bool EQ(const Entry& entry, const GrTBinHashKey<Entry, KeySize>& key) {
+    static bool EQ(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) {
         GrAssert(key.fIsValid);
         return 0 == entry.compare(key);
     }
 
-    static bool LT(const Entry& entry, const GrTBinHashKey<Entry, KeySize>& key) {
+    static bool LT(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) {
         GrAssert(key.fIsValid);
         return entry.compare(key) < 0;
     }
@@ -90,9 +92,14 @@
         return fHash;
     }
 
+    const uint8_t* getData() const {
+        GrAssert(fIsValid);
+        return fData;
+    }
+
 private:
     uint32_t            fHash;
-    uint8_t             fData[KeySize];  // Buffer for key storage
+    uint8_t             fData[KEY_SIZE];  // Buffer for key storage
 
 #if GR_DEBUG
 public:
diff --git a/src/gpu/GrBufferAllocPool.cpp b/src/gpu/GrBufferAllocPool.cpp
index 66b74e4..ec8a9c9 100644
--- a/src/gpu/GrBufferAllocPool.cpp
+++ b/src/gpu/GrBufferAllocPool.cpp
@@ -373,7 +373,7 @@
                     preallocBufferCnt) {
 }
 
-void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout,
+void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
                                          int vertexCount,
                                          const GrVertexBuffer** buffer,
                                          int* startVertex) {
@@ -382,43 +382,41 @@
     GrAssert(NULL != buffer);
     GrAssert(NULL != startVertex);
 
-    size_t vSize = GrDrawTarget::VertexSize(layout);
     size_t offset = 0; // assign to suppress warning
     const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
-    void* ptr = INHERITED::makeSpace(vSize * vertexCount,
-                                     vSize,
+    void* ptr = INHERITED::makeSpace(vertexSize * vertexCount,
+                                     vertexSize,
                                      &geomBuffer,
                                      &offset);
 
     *buffer = (const GrVertexBuffer*) geomBuffer;
-    GrAssert(0 == offset % vSize);
-    *startVertex = offset / vSize;
+    GrAssert(0 == offset % vertexSize);
+    *startVertex = offset / vertexSize;
     return ptr;
 }
 
-bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout,
+bool GrVertexBufferAllocPool::appendVertices(size_t vertexSize,
                                              int vertexCount,
                                              const void* vertices,
                                              const GrVertexBuffer** buffer,
                                              int* startVertex) {
-    void* space = makeSpace(layout, vertexCount, buffer, startVertex);
+    void* space = makeSpace(vertexSize, vertexCount, buffer, startVertex);
     if (NULL != space) {
         memcpy(space,
                vertices,
-               GrDrawTarget::VertexSize(layout) * vertexCount);
+               vertexSize * vertexCount);
         return true;
     } else {
         return false;
     }
 }
 
-int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
-    return INHERITED::preallocatedBufferSize() /
-            GrDrawTarget::VertexSize(layout);
+int GrVertexBufferAllocPool::preallocatedBufferVertices(size_t vertexSize) const {
+    return INHERITED::preallocatedBufferSize() / vertexSize;
 }
 
-int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
-    return currentBufferItems(GrDrawTarget::VertexSize(layout));
+int GrVertexBufferAllocPool::currentBufferVertices(size_t vertexSize) const {
+    return currentBufferItems(vertexSize);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrBufferAllocPool.h b/src/gpu/GrBufferAllocPool.h
index 7ed4b0c..ffd8c34 100644
--- a/src/gpu/GrBufferAllocPool.h
+++ b/src/gpu/GrBufferAllocPool.h
@@ -12,9 +12,9 @@
 #define GrBufferAllocPool_DEFINED
 
 #include "GrNoncopyable.h"
-#include "GrTDArray.h"
 
 #include "SkTArray.h"
+#include "SkTDArray.h"
 
 class GrGeometryBuffer;
 class GrGpu;
@@ -170,7 +170,7 @@
     GrGpu*                          fGpu;
     bool                            fGpuIsReffed;
     bool                            fFrequentResetHint;
-    GrTDArray<GrGeometryBuffer*>    fPreallocBuffers;
+    SkTDArray<GrGeometryBuffer*>    fPreallocBuffers;
     size_t                          fMinBlockSize;
     BufferType                      fBufferType;
 
@@ -222,7 +222,7 @@
      * the buffer at the offset indicated by startVertex. Until that time they
      * may be in temporary storage and/or the buffer may be locked.
      *
-     * @param layout       specifies type of vertices to allocate space for
+     * @param vertexSize   specifies size of a vertex to allocate space for
      * @param vertexCount  number of vertices to allocate space for
      * @param buffer       returns the vertex buffer that will hold the
      *                     vertices.
@@ -230,7 +230,7 @@
      *                     In units of the size of a vertex from layout param.
      * @return pointer to first vertex.
      */
-    void* makeSpace(GrVertexLayout layout,
+    void* makeSpace(size_t vertexSize,
                     int vertexCount,
                     const GrVertexBuffer** buffer,
                     int* startVertex);
@@ -238,7 +238,7 @@
     /**
      * Shortcut to make space and then write verts into the made space.
      */
-    bool appendVertices(GrVertexLayout layout,
+    bool appendVertices(size_t vertexSize,
                         int vertexCount,
                         const void* vertices,
                         const GrVertexBuffer** buffer,
@@ -251,21 +251,21 @@
      * would fit in the next available preallocated buffer. If any makeSpace
      * would force a new VB to be created the return value will be zero.
      *
-     * @param   the format of vertices to compute space for.
+     * @param   the size of a vertex to compute space for.
      * @return the number of vertices that would fit in the current buffer.
      */
-    int currentBufferVertices(GrVertexLayout layout) const;
+    int currentBufferVertices(size_t vertexSize) const;
 
     /**
      * Gets the number of vertices that can fit in a  preallocated vertex buffer.
      * Zero if no preallocated buffers.
      *
-     * @param   the format of vertices to compute space for.
+     * @param   the size of a vertex to compute space for.
      *
      * @return number of vertices that fit in one of the preallocated vertex
      *         buffers.
      */
-    int preallocatedBufferVertices(GrVertexLayout layout) const;
+    int preallocatedBufferVertices(size_t vertexSize) const;
 
 private:
     typedef GrBufferAllocPool INHERITED;
diff --git a/src/gpu/GrCacheID.cpp b/src/gpu/GrCacheID.cpp
index 4c6dd49..c3d1d66 100644
--- a/src/gpu/GrCacheID.cpp
+++ b/src/gpu/GrCacheID.cpp
@@ -5,40 +5,21 @@
  * found in the LICENSE file.
  */
 
-#include "GrCacheID.h"
+#include "GrTypes.h"
 #include "SkThread.h"       // for sk_atomic_inc
 
-uint8_t GrCacheID::GetNextDomain() {
-    // 0 reserved for kUnrestricted_ResourceDomain
-    static int32_t gNextDomain = 1;
+static GrCacheID::Key kAssertKey;
+GR_STATIC_ASSERT(sizeof(kAssertKey.fData8)  == sizeof(kAssertKey.fData32));
+GR_STATIC_ASSERT(sizeof(kAssertKey.fData8) == sizeof(kAssertKey.fData64));
+GR_STATIC_ASSERT(sizeof(kAssertKey.fData8) == sizeof(kAssertKey));
+
+GrCacheID::Domain GrCacheID::GenerateDomain() {
+    static int32_t gNextDomain = kInvalid_Domain + 1;
 
     int32_t domain = sk_atomic_inc(&gNextDomain);
-    if (domain >= 256) {
+    if (domain >= 1 << (8 * sizeof(Domain))) {
         GrCrash("Too many Cache Domains");
     }
 
-    return (uint8_t) domain;
-}
-
-uint8_t GrCacheID::GetNextResourceType() {
-    // 0 reserved for kInvalid_ResourceType
-    static int32_t gNextResourceType = 1;
-
-    int32_t type = sk_atomic_inc(&gNextResourceType);
-    if (type >= 256) {
-        GrCrash("Too many Cache Resource Types");
-    }
-
-    return (uint8_t) type;
-}
-
-void GrCacheID::toRaw(uint32_t v[4]) {
-    GrAssert(4*sizeof(uint32_t) == sizeof(GrCacheID));
-
-    v[0] = (uint32_t) (fPublicID & 0xffffffffUL);
-    v[1] = (uint32_t) ((fPublicID >> 32) & 0xffffffffUL);
-    v[2] = fResourceSpecific32;
-    v[3] = fDomain << 24 |
-           fResourceType << 16 |
-           fResourceSpecific16;
+    return static_cast<Domain>(domain);
 }
diff --git a/src/gpu/GrClipData.cpp b/src/gpu/GrClipData.cpp
index 7c84c18..ce9dcdf 100644
--- a/src/gpu/GrClipData.cpp
+++ b/src/gpu/GrClipData.cpp
@@ -32,4 +32,3 @@
 
     devBounds.roundOut(devResult);
 }
-
diff --git a/src/gpu/GrClipMaskCache.cpp b/src/gpu/GrClipMaskCache.cpp
index b7cfb65..470cc4a 100644
--- a/src/gpu/GrClipMaskCache.cpp
+++ b/src/gpu/GrClipMaskCache.cpp
@@ -19,4 +19,3 @@
 void GrClipMaskCache::push() {
     SkNEW_PLACEMENT(fStack.push_back(), GrClipStackFrame);
 }
-
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index a596088..d2bfe7b 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -18,12 +18,9 @@
 #include "GrAAConvexPathRenderer.h"
 #include "GrAAHairLinePathRenderer.h"
 #include "GrSWMaskHelper.h"
-#include "GrCacheID.h"
 
 #include "SkTLazy.h"
 
-GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrClipMaskManager, GetAlphaMaskDomain)
-
 #define GR_AA_CLIP 1
 
 typedef SkClipStack::Element Element;
@@ -48,12 +45,10 @@
                      SkIntToScalar(-devBound.fTop));
     mat.preConcat(drawState->getViewMatrix());
 
-    drawState->stage(kMaskStage)->reset();
-
     SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
     // This could be a long-lived effect that is cached with the alpha-mask.
-    drawState->stage(kMaskStage)->setEffect(
-        GrTextureDomainEffect::Create(result,
+    drawState->setEffect(kMaskStage,
+                         GrTextureDomainEffect::Create(result,
                                       mat,
                                       GrTextureDomainEffect::MakeTexelDomain(result, domainTexels),
                                       GrTextureDomainEffect::kDecal_WrapMode))->unref();
@@ -263,31 +258,6 @@
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////
-bool draw_path_in_software(GrContext* context,
-                           GrGpu* gpu,
-                           const SkPath& path,
-                           bool doAA,
-                           const GrIRect& resultBounds) {
-    SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
-
-    SkAutoTUnref<GrTexture> texture(
-                GrSWMaskHelper::DrawPathMaskToTexture(context, path,
-                                                      rec,
-                                                      resultBounds,
-                                                      doAA, NULL));
-    if (NULL == texture) {
-        return false;
-    }
-
-    // The ClipMaskManager accumulates the clip mask in the UL corner
-    GrIRect rect = GrIRect::MakeWH(resultBounds.width(), resultBounds.height());
-
-    GrSWMaskHelper::DrawToTargetWithPathMask(texture, gpu, rect);
-
-    GrAssert(!path.isInverseFillType());
-    return true;
-}
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -380,7 +350,7 @@
 
     SkMatrix sampleM;
     sampleM.setIDiv(srcMask->width(), srcMask->height());
-    drawState->stage(0)->setEffect(
+    drawState->setEffect(0,
         GrTextureDomainEffect::Create(srcMask,
                                       sampleM,
                                       GrTextureDomainEffect::MakeTexelDomain(srcMask, srcBound),
@@ -635,15 +605,12 @@
         SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
         clipBit = (1 << (clipBit-1));
 
-        GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height());
-
         fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState);
 
         // walk through each clip element and perform its set op
         // with the existing clip.
         for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) {
             const Element* element = iter.get();
-            SkPath::FillType fill;
             bool fillInverted = false;
             // enabled at bottom of loop
             drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
@@ -664,16 +631,13 @@
             SkTCopyOnFirstWrite<SkPath> clipPath;
             if (Element::kRect_Type == element->getType()) {
                 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
-                fill = SkPath::kEvenOdd_FillType;
                 fillInverted = false;
             } else {
                 GrAssert(Element::kPath_Type == element->getType());
                 clipPath.init(element->getPath());
-                fill = clipPath->getFillType();
                 fillInverted = clipPath->isInverseFillType();
                 if (fillInverted) {
                     clipPath.writable()->toggleInverseFillType();
-                    fill = clipPath->getFillType();
                 }
                 pr = this->getContext()->getPathRenderer(*clipPath,
                                                          stroke,
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 48d17e1..534689a 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -41,8 +41,6 @@
  */
 class GrClipMaskManager : public GrNoncopyable {
 public:
-    GR_DECLARE_RESOURCE_CACHE_DOMAIN(GetAlphaMaskDomain)
-
     GrClipMaskManager()
         : fGpu(NULL)
         , fCurrClipMaskType(kNone_ClipMaskType) {
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index d00e062..b729d69 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -200,27 +200,31 @@
     GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kReset_ASRInit);
     GrDrawState* drawState = target->drawState();
     drawState->setRenderTarget(rt);
-    SkAutoTUnref<GrConvolutionEffect> conv(SkNEW_ARGS(GrConvolutionEffect,
-                                                      (texture, direction, radius,
-                                                       sigma)));
-    drawState->stage(0)->setEffect(conv);
+    SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture,
+                                                                       direction,
+                                                                       radius,
+                                                                       sigma));
+    drawState->setEffect(0, conv);
     target->drawSimpleRect(rect, NULL);
 }
 
 }
 
-GrTexture* GrContext::findTexture(const GrTextureDesc& desc,
-                                  const GrCacheData& cacheData,
-                                  const GrTextureParams* params) {
-    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false);
+////////////////////////////////////////////////////////////////////////////////
+
+GrTexture* GrContext::findAndRefTexture(const GrTextureDesc& desc,
+                                        const GrCacheID& cacheID,
+                                        const GrTextureParams* params) {
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
     GrResource* resource = fTextureCache->find(resourceKey);
+    SkSafeRef(resource);
     return static_cast<GrTexture*>(resource);
 }
 
 bool GrContext::isTextureInCache(const GrTextureDesc& desc,
-                                 const GrCacheData& cacheData,
+                                 const GrCacheID& cacheID,
                                  const GrTextureParams* params) const {
-    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false);
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
     return fTextureCache->hasKey(resourceKey);
 }
 
@@ -272,21 +276,19 @@
 // The desired texture is NPOT and tiled but that isn't supported by
 // the current hardware. Resize the texture to be a POT
 GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc,
-                                           const GrCacheData& cacheData,
+                                           const GrCacheID& cacheID,
                                            void* srcData,
                                            size_t rowBytes,
                                            bool needsFiltering) {
-    GrTexture* clampedTexture = this->findTexture(desc, cacheData, NULL);
+    SkAutoTUnref<GrTexture> clampedTexture(this->findAndRefTexture(desc, cacheID, NULL));
     if (NULL == clampedTexture) {
-        clampedTexture = this->createTexture(NULL, desc, cacheData, srcData, rowBytes);
+        clampedTexture.reset(this->createTexture(NULL, desc, cacheID, srcData, rowBytes));
 
         if (NULL == clampedTexture) {
             return NULL;
         }
     }
 
-    clampedTexture->ref();
-
     GrTextureDesc rtDesc = desc;
     rtDesc.fFlags =  rtDesc.fFlags |
                      kRenderTarget_GrTextureFlagBit |
@@ -307,19 +309,14 @@
         GrTextureParams params(SkShader::kClamp_TileMode, needsFiltering);
         drawState->createTextureEffect(0, clampedTexture, SkMatrix::I(), params);
 
-        static const GrVertexLayout layout =
-                            GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
+        static const GrVertexLayout layout = GrDrawState::StageTexCoordVertexLayoutBit(0,0);
         GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);
 
         if (arg.succeeded()) {
             GrPoint* verts = (GrPoint*) arg.vertices();
-            verts[0].setIRectFan(0, 0,
-                                    texture->width(),
-                                    texture->height(),
-                                    2*sizeof(GrPoint));
-            verts[1].setIRectFan(0, 0, 1, 1, 2*sizeof(GrPoint));
-            fGpu->drawNonIndexed(kTriangleFan_GrPrimitiveType,
-                                    0, 4);
+            verts[0].setIRectFan(0, 0, texture->width(), texture->height(), 2 * sizeof(GrPoint));
+            verts[1].setIRectFan(0, 0, 1, 1, 2 * sizeof(GrPoint));
+            fGpu->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
         }
         texture->releaseRenderTarget();
     } else {
@@ -339,35 +336,33 @@
 
         size_t stretchedRowBytes = rtDesc.fWidth * bpp;
 
-        GrTexture* texture = fGpu->createTexture(rtDesc, stretchedPixels.get(), stretchedRowBytes);
+        SkDEBUGCODE(GrTexture* texture = )fGpu->createTexture(rtDesc, stretchedPixels.get(), stretchedRowBytes);
         GrAssert(NULL != texture);
     }
 
-    clampedTexture->unref();
     return texture;
 }
 
-GrTexture* GrContext::createTexture(
-        const GrTextureParams* params,
-        const GrTextureDesc& desc,
-        const GrCacheData& cacheData,
-        void* srcData,
-        size_t rowBytes) {
-    SK_TRACE_EVENT0("GrContext::createAndLockTexture");
+GrTexture* GrContext::createTexture(const GrTextureParams* params,
+                                    const GrTextureDesc& desc,
+                                    const GrCacheID& cacheID,
+                                    void* srcData,
+                                    size_t rowBytes) {
+    SK_TRACE_EVENT0("GrContext::createTexture");
 
 #if GR_DUMP_TEXTURE_UPLOAD
-    GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);
+    GrPrintf("GrContext::createTexture[%d %d]\n", desc.fWidth, desc.fHeight);
 #endif
 
-    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false);
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
 
-    SkAutoTUnref<GrTexture> texture;
+    GrTexture* texture;
     if (GrTexture::NeedsResizing(resourceKey)) {
-        texture.reset(this->createResizedTexture(desc, cacheData,
+        texture = this->createResizedTexture(desc, cacheID,
                                              srcData, rowBytes,
-                                             GrTexture::NeedsFiltering(resourceKey)));
+                                             GrTexture::NeedsFiltering(resourceKey));
     } else {
-        texture.reset(fGpu->createTexture(desc, srcData, rowBytes));
+        texture= fGpu->createTexture(desc, srcData, rowBytes);
     }
 
     if (NULL != texture) {
@@ -377,15 +372,13 @@
     return texture;
 }
 
-GrTexture* GrContext::lockScratchTexture(const GrTextureDesc& inDesc,
-                                         ScratchTexMatch match) {
+GrTexture* GrContext::lockAndRefScratchTexture(const GrTextureDesc& inDesc, ScratchTexMatch match) {
     GrTextureDesc desc = inDesc;
-    GrCacheData cacheData(GrCacheData::kScratch_CacheID);
 
     GrAssert((desc.fFlags & kRenderTarget_GrTextureFlagBit) ||
              !(desc.fFlags & kNoStencil_GrTextureFlagBit));
 
-    if (kExact_ScratchTexMatch != match) {
+    if (kApprox_ScratchTexMatch == match) {
         // bin by pow2 with a reasonable min
         static const int MIN_SIZE = 256;
         desc.fWidth  = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
@@ -399,14 +392,19 @@
     bool doubledH = false;
 
     do {
-        GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL, desc, cacheData, true);
+        GrResourceKey key = GrTexture::ComputeScratchKey(desc);
         // Ensure we have exclusive access to the texture so future 'find' calls don't return it
         resource = fTextureCache->find(key, GrResourceCache::kHide_OwnershipFlag);
-        // if we miss, relax the fit of the flags...
-        // then try doubling width... then height.
-        if (NULL != resource || kExact_ScratchTexMatch == match) {
+        if (NULL != resource) {
+            resource->ref();
             break;
         }
+        if (kExact_ScratchTexMatch == match) {
+            break;
+        }
+        // We had a cache miss and we are in approx mode, relax the fit of the flags... then try
+        // doubling width... then the height.
+
         // We no longer try to reuse textures that were previously used as render targets in
         // situations where no RT is needed; doing otherwise can confuse the video driver and
         // cause significant performance problems in some cases.
@@ -431,12 +429,9 @@
         desc.fFlags = inDesc.fFlags;
         desc.fWidth = origWidth;
         desc.fHeight = origHeight;
-        SkAutoTUnref<GrTexture> texture(fGpu->createTexture(desc, NULL, 0));
+        GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
         if (NULL != texture) {
-            GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL,
-                                                      texture->desc(),
-                                                      cacheData,
-                                                      true);
+            GrResourceKey key = GrTexture::ComputeScratchKey(texture->desc());
             // Make the resource exclusive so future 'find' calls don't return it
             fTextureCache->addResource(key, texture, GrResourceCache::kHide_OwnershipFlag);
             resource = texture;
@@ -475,7 +470,7 @@
     // If this is a scratch texture we detached it from the cache
     // while it was locked (to avoid two callers simultaneously getting
     // the same texture).
-    if (GrTexture::IsScratchTexture(texture->getCacheEntry()->key())) {
+    if (texture->getCacheEntry()->key().isScratch()) {
         fTextureCache->makeNonExclusive(texture->getCacheEntry());
     }
 
@@ -842,7 +837,7 @@
         m.postConcat(*srcMatrix);
     }
 
-    drawState->stage(GrPaint::kFirstColorStage)->preConcatCoordChange(m);
+    drawState->preConcatStageMatrices(kExplicitCoordMask, m);
 
     const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
     if (NULL == sqVB) {
@@ -880,12 +875,12 @@
 
     GrVertexLayout layout = 0;
     if (NULL != texCoords) {
-        layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0, 0);
+        layout |= GrDrawState::StageTexCoordVertexLayoutBit(0, 0);
     }
     if (NULL != colors) {
-        layout |= GrDrawTarget::kColor_VertexLayoutBit;
+        layout |= GrDrawState::kColor_VertexLayoutBit;
     }
-    int vertexSize = GrDrawTarget::VertexSize(layout);
+    int vertexSize = GrDrawState::VertexSize(layout);
 
     if (sizeof(GrPoint) != vertexSize) {
         if (!geo.set(target, layout, vertexCount, 0)) {
@@ -894,7 +889,7 @@
         }
         int texOffsets[GrDrawState::kMaxTexCoords];
         int colorOffset;
-        GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
+        GrDrawState::VertexSizeAndOffsetsByIdx(layout,
                                                 texOffsets,
                                                 &colorOffset,
                                                 NULL,
@@ -937,60 +932,63 @@
     SkScalar fInnerRadius;
 };
 
-/* Returns true if will map a circle to another circle. This can be true
- * if the matrix only includes square-scale, rotation, translation.
- */
-inline bool isSimilarityTransformation(const SkMatrix& matrix,
-                                       SkScalar tol = SK_ScalarNearlyZero) {
-    if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) {
-        return true;
-    }
-    if (matrix.hasPerspective()) {
-        return false;
-    }
-
-    SkScalar mx = matrix.get(SkMatrix::kMScaleX);
-    SkScalar sx = matrix.get(SkMatrix::kMSkewX);
-    SkScalar my = matrix.get(SkMatrix::kMScaleY);
-    SkScalar sy = matrix.get(SkMatrix::kMSkewY);
-
-    if (mx == 0 && sx == 0 && my == 0 && sy == 0) {
-        return false;
-    }
-
-    // it has scales or skews, but it could also be rotation, check it out.
-    SkVector vec[2];
-    vec[0].set(mx, sx);
-    vec[1].set(sy, my);
-
-    return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
-           SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
-                SkScalarSquare(tol));
+inline bool circleStaysCircle(const SkMatrix& m) {
+    return m.isSimilarity();
 }
 
 }
 
-// TODO: strokeWidth can't be larger than zero right now.
-// It will be fixed when drawPath() can handle strokes.
 void GrContext::drawOval(const GrPaint& paint,
-                         const GrRect& rect,
-                         SkScalar strokeWidth) {
-    GrAssert(strokeWidth <= 0);
-    if (!isSimilarityTransformation(this->getMatrix()) ||
-        !paint.isAntiAlias() ||
-        rect.height() != rect.width()) {
+                         const GrRect& oval,
+                         const SkStrokeRec& stroke) {
+
+    if (!canDrawOval(paint, oval, stroke)) {
         SkPath path;
-        path.addOval(rect);
-        path.setFillType(SkPath::kWinding_FillType);
-        SkStrokeRec stroke(0 == strokeWidth ? SkStrokeRec::kHairline_InitStyle :
-                                           SkStrokeRec::kFill_InitStyle);
-        if (strokeWidth > 0) {
-            stroke.setStrokeStyle(strokeWidth, true);
-        }
-        this->internalDrawPath(paint, path, stroke);
+        path.addOval(oval);
+        this->drawPath(paint, path, stroke);
         return;
     }
 
+    internalDrawOval(paint, oval, stroke);
+}
+
+bool GrContext::canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const {
+
+    if (!paint.isAntiAlias()) {
+        return false;
+    }
+
+    // we can draw circles in any style
+    bool isCircle = SkScalarNearlyEqual(oval.width(), oval.height())
+                    && circleStaysCircle(this->getMatrix());
+    // and for now, axis-aligned ellipses only with fill or stroke-and-fill
+    SkStrokeRec::Style style = stroke.getStyle();
+    bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
+    bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
+
+    return isCircle || isFilledAxisAlignedEllipse;
+}
+
+void GrContext::internalDrawOval(const GrPaint& paint,
+                                 const GrRect& oval,
+                                 const SkStrokeRec& stroke) {
+
+    SkScalar xRadius = SkScalarHalf(oval.width());
+    SkScalar yRadius = SkScalarHalf(oval.height());
+
+    SkScalar strokeWidth = stroke.getWidth();
+    SkStrokeRec::Style style = stroke.getStyle();
+
+    bool isCircle = SkScalarNearlyEqual(xRadius, yRadius) && circleStaysCircle(this->getMatrix());
+#ifdef SK_DEBUG
+    {
+        // we should have checked for this previously
+        bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
+        bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
+        SkASSERT(paint.isAntiAlias() && (isCircle || isFilledAxisAlignedEllipse));
+    }
+#endif
+
     GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
 
     GrDrawState* drawState = target->drawState();
@@ -1007,24 +1005,8 @@
         return;
     }
 
-    GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
-    GrAssert(sizeof(CircleVertex) == GrDrawTarget::VertexSize(layout));
-
-    GrPoint center = GrPoint::Make(rect.centerX(), rect.centerY());
-    SkScalar radius = SkScalarHalf(rect.width());
-
-    vm.mapPoints(&center, 1);
-    radius = vm.mapRadius(radius);
-
-    SkScalar outerRadius = radius;
-    SkScalar innerRadius = 0;
-    SkScalar halfWidth = 0;
-    if (strokeWidth == 0) {
-        halfWidth = SkScalarHalf(SK_Scalar1);
-
-        outerRadius += halfWidth;
-        innerRadius = SkMaxScalar(0, radius - halfWidth);
-    }
+    GrVertexLayout layout = GrDrawState::kEdge_VertexLayoutBit;
+    GrAssert(sizeof(CircleVertex) == GrDrawState::VertexSize(layout));
 
     GrDrawTarget::AutoReleaseGeometry geo(target, layout, 4, 0);
     if (!geo.succeeded()) {
@@ -1034,26 +1016,93 @@
 
     CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
 
+    GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY());
+    vm.mapPoints(&center, 1);
+
+    SkScalar L;
+    SkScalar R;
+    SkScalar T;
+    SkScalar B;
+
+    if (isCircle) {
+        drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
+
+        xRadius = vm.mapRadius(xRadius);
+
+        SkScalar outerRadius = xRadius;
+        SkScalar innerRadius = 0;
+        SkScalar halfWidth = 0;
+        if (style != SkStrokeRec::kFill_Style) {
+            strokeWidth = vm.mapRadius(strokeWidth);
+            if (SkScalarNearlyZero(strokeWidth)) {
+                halfWidth = SK_ScalarHalf;
+            } else {
+                halfWidth = SkScalarHalf(strokeWidth);
+            }
+
+            outerRadius += halfWidth;
+            if (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style) {
+                innerRadius = SkMaxScalar(0, xRadius - halfWidth);
+            }
+        }
+
+        for (int i = 0; i < 4; ++i) {
+            verts[i].fCenter = center;
+            verts[i].fOuterRadius = outerRadius;
+            verts[i].fInnerRadius = innerRadius;
+        }
+
+        L = -outerRadius;
+        R = +outerRadius;
+        T = -outerRadius;
+        B = +outerRadius;
+    } else {  // is axis-aligned ellipse
+        drawState->setVertexEdgeType(GrDrawState::kEllipse_EdgeType);
+
+        SkRect xformedRect;
+        vm.mapRect(&xformedRect, oval);
+
+        xRadius = SkScalarHalf(xformedRect.width());
+        yRadius = SkScalarHalf(xformedRect.height());
+
+        if (style == SkStrokeRec::kStrokeAndFill_Style && strokeWidth > 0.0f) {
+            SkScalar halfWidth = SkScalarHalf(strokeWidth);
+            // do (potentially) anisotropic mapping
+            SkVector scaledStroke;
+            scaledStroke.set(halfWidth, halfWidth);
+            vm.mapVectors(&scaledStroke, 1);
+            // this is legit only if scale & translation (which should be the case at the moment)
+            xRadius += scaledStroke.fX;
+            yRadius += scaledStroke.fY;
+        }
+
+        SkScalar ratio = SkScalarDiv(xRadius, yRadius);
+
+        for (int i = 0; i < 4; ++i) {
+            verts[i].fCenter = center;
+            verts[i].fOuterRadius = xRadius;
+            verts[i].fInnerRadius = ratio;
+        }
+
+        L = -xRadius;
+        R = +xRadius;
+        T = -yRadius;
+        B = +yRadius;
+    }
+
     // The fragment shader will extend the radius out half a pixel
     // to antialias. Expand the drawn rect here so all the pixels
     // will be captured.
-    SkScalar L = center.fX - outerRadius - SkFloatToScalar(0.5f);
-    SkScalar R = center.fX + outerRadius + SkFloatToScalar(0.5f);
-    SkScalar T = center.fY - outerRadius - SkFloatToScalar(0.5f);
-    SkScalar B = center.fY + outerRadius + SkFloatToScalar(0.5f);
+    L += center.fX - SK_ScalarHalf;
+    R += center.fX + SK_ScalarHalf;
+    T += center.fY - SK_ScalarHalf;
+    B += center.fY + SK_ScalarHalf;
 
     verts[0].fPos = SkPoint::Make(L, T);
     verts[1].fPos = SkPoint::Make(R, T);
     verts[2].fPos = SkPoint::Make(L, B);
     verts[3].fPos = SkPoint::Make(R, B);
 
-    for (int i = 0; i < 4; ++i) {
-        verts[i].fCenter = center;
-        verts[i].fOuterRadius = outerRadius;
-        verts[i].fInnerRadius = innerRadius;
-    }
-
-    drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
     target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
 }
 
@@ -1066,24 +1115,15 @@
        return;
     }
 
-    const SkPath* pathPtr = &path;
-    SkPath tmpPath;
-    SkStrokeRec strokeRec(stroke);
-    if (!strokeRec.isHairlineStyle()) {
-        if (strokeRec.applyToPath(&tmpPath, *pathPtr)) {
-            pathPtr = &tmpPath;
-            strokeRec.setFillStyle();
-        }
-    }
-
     SkRect ovalRect;
-    if (!pathPtr->isInverseFillType() && pathPtr->isOval(&ovalRect)) {
-        SkScalar width = strokeRec.isHairlineStyle() ? 0 : -SK_Scalar1;
-        this->drawOval(paint, ovalRect, width);
+    bool isOval = path.isOval(&ovalRect);
+
+    if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, stroke)) {
+        this->drawOval(paint, ovalRect, stroke);
         return;
     }
 
-    this->internalDrawPath(paint, *pathPtr, strokeRec);
+    this->internalDrawPath(paint, path, stroke);
 }
 
 void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke) {
@@ -1112,7 +1152,25 @@
     GrPathRendererChain::DrawType type = prAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
                                                 GrPathRendererChain::kColor_DrawType;
 
-    GrPathRenderer* pr = this->getPathRenderer(path, stroke, target, true, type);
+    const SkPath* pathPtr = &path;
+    SkPath tmpPath;
+    SkStrokeRec strokeRec(stroke);
+
+    // Try a 1st time without stroking the path and without allowing the SW renderer
+    GrPathRenderer* pr = this->getPathRenderer(*pathPtr, strokeRec, target, false, type);
+
+    if (NULL == pr) {
+        if (!strokeRec.isHairlineStyle()) {
+            // It didn't work the 1st time, so try again with the stroked path
+            if (strokeRec.applyToPath(&tmpPath, *pathPtr)) {
+                pathPtr = &tmpPath;
+                strokeRec.setFillStyle();
+            }
+        }
+        // This time, allow SW renderer
+        pr = this->getPathRenderer(*pathPtr, strokeRec, target, true, type);
+    }
+
     if (NULL == pr) {
 #if GR_DEBUG
         GrPrintf("Unable to find path renderer compatible with path.\n");
@@ -1120,7 +1178,7 @@
         return;
     }
 
-    pr->drawPath(path, stroke, target, prAA);
+    pr->drawPath(*pathPtr, strokeRec, target, prAA);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1317,7 +1375,6 @@
         ast.set(this, desc, match);
         GrTexture* texture = ast.texture();
         if (texture) {
-            GrEffectStage stage;
             // compute a matrix to perform the draw
             SkMatrix textureMatrix;
             if (flipY) {
@@ -1329,30 +1386,30 @@
             }
             textureMatrix.postIDiv(src->width(), src->height());
 
-            bool effectInstalled = false;
+            SkAutoTUnref<const GrEffectRef> effect;
             if (unpremul) {
-                if (this->installPMToUPMEffect(src, swapRAndB, textureMatrix, &stage)) {
-                    effectInstalled = true;
+                effect.reset(this->createPMToUPMEffect(src, swapRAndB, textureMatrix));
+                if (NULL != effect) {
                     unpremul = false; // we no longer need to do this on CPU after the readback.
                 }
             }
             // If we failed to create a PM->UPM effect and have no other conversions to perform then
             // there is no longer any point to using the scratch.
-            if (effectInstalled || flipY || swapRAndB) {
-                if (!effectInstalled) {
-                    SkAssertResult(GrConfigConversionEffect::InstallEffect(
-                                            src,
-                                            swapRAndB,
-                                            GrConfigConversionEffect::kNone_PMConversion,
-                                            textureMatrix,
-                                            &stage));
+            if (NULL != effect || flipY || swapRAndB) {
+                if (!effect) {
+                    effect.reset(GrConfigConversionEffect::Create(
+                                                    src,
+                                                    swapRAndB,
+                                                    GrConfigConversionEffect::kNone_PMConversion,
+                                                    textureMatrix));
                 }
                 swapRAndB = false; // we will handle the swap in the draw.
                 flipY = false; // we already incorporated the y flip in the matrix
 
                 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
                 GrDrawState* drawState = fGpu->drawState();
-                *drawState->stage(0) = stage;
+                GrAssert(effect);
+                drawState->setEffect(0, effect);
 
                 drawState->setRenderTarget(texture->asRenderTarget());
                 GrRect rect = GrRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
@@ -1445,7 +1502,7 @@
 
     // Writes pending to the source texture are not tracked, so a flush
     // is required to ensure that the copy captures the most recent contents
-    // of the source texture. See similar behaviour in
+    // of the source texture. See similar behavior in
     // GrContext::resolveRenderTarget.
     this->flush();
 
@@ -1526,23 +1583,19 @@
         return;
     }
 
-    GrEffectStage stage;
+    SkAutoTUnref<const GrEffectRef> effect;
     SkMatrix textureMatrix;
     textureMatrix.setIDiv(texture->width(), texture->height());
 
     // allocate a tmp buffer and sw convert the pixels to premul
     SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
 
-    bool effectInstalled = false;
     if (kUnpremul_PixelOpsFlag & flags) {
         if (kRGBA_8888_GrPixelConfig != config && kBGRA_8888_GrPixelConfig != config) {
             return;
         }
-        effectInstalled = this->installUPMToPMEffect(texture,
-                                                     swapRAndB,
-                                                     textureMatrix,
-                                                     &stage);
-        if (!effectInstalled) {
+        effect.reset(this->createUPMToPMEffect(texture, swapRAndB, textureMatrix));
+        if (NULL == effect) {
             SkCanvas::Config8888 srcConfig8888, dstConfig8888;
             GR_DEBUGCODE(bool success = )
             grconfig_to_config8888(config, true, &srcConfig8888);
@@ -1559,13 +1612,11 @@
             rowBytes = 4 * width;
         }
     }
-    if (!effectInstalled) {
-        SkAssertResult(GrConfigConversionEffect::InstallEffect(
-                                                    texture,
-                                                    swapRAndB,
-                                                    GrConfigConversionEffect::kNone_PMConversion,
-                                                    textureMatrix,
-                                                    &stage));
+    if (NULL == effect) {
+        effect.reset(GrConfigConversionEffect::Create(texture,
+                                                      swapRAndB,
+                                                      GrConfigConversionEffect::kNone_PMConversion,
+                                                      textureMatrix));
     }
 
     this->writeTexturePixels(texture,
@@ -1575,7 +1626,8 @@
 
     GrDrawTarget::AutoStateRestore  asr(fGpu, GrDrawTarget::kReset_ASRInit);
     GrDrawState* drawState = fGpu->drawState();
-    *drawState->stage(0) = stage;
+    GrAssert(effect);
+    drawState->setEffect(0, effect);
 
     SkMatrix matrix;
     matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
@@ -1739,7 +1791,6 @@
                                           fDrawBufferVBAllocPool,
                                           fDrawBufferIBAllocPool));
 
-    fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer());
     if (fDrawBuffer) {
         fDrawBuffer->setAutoFlushTarget(fGpu);
         fDrawBuffer->setDrawState(fDrawState);
@@ -1764,10 +1815,9 @@
 }
 }
 
-bool GrContext::installPMToUPMEffect(GrTexture* texture,
-                                     bool swapRAndB,
-                                     const SkMatrix& matrix,
-                                     GrEffectStage* stage) {
+const GrEffectRef* GrContext::createPMToUPMEffect(GrTexture* texture,
+                                                  bool swapRAndB,
+                                                  const SkMatrix& matrix) {
     if (!fDidTestPMConversions) {
         test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
         fDidTestPMConversions = true;
@@ -1775,17 +1825,15 @@
     GrConfigConversionEffect::PMConversion pmToUPM =
         static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
     if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
-        GrConfigConversionEffect::InstallEffect(texture, swapRAndB, pmToUPM, matrix, stage);
-        return true;
+        return GrConfigConversionEffect::Create(texture, swapRAndB, pmToUPM, matrix);
     } else {
-        return false;
+        return NULL;
     }
 }
 
-bool GrContext::installUPMToPMEffect(GrTexture* texture,
-                                     bool swapRAndB,
-                                     const SkMatrix& matrix,
-                                     GrEffectStage* stage) {
+const GrEffectRef* GrContext::createUPMToPMEffect(GrTexture* texture,
+                                                  bool swapRAndB,
+                                                  const SkMatrix& matrix) {
     if (!fDidTestPMConversions) {
         test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
         fDidTestPMConversions = true;
@@ -1793,10 +1841,9 @@
     GrConfigConversionEffect::PMConversion upmToPM =
         static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
     if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) {
-        GrConfigConversionEffect::InstallEffect(texture, swapRAndB, upmToPM, matrix, stage);
-        return true;
+        return GrConfigConversionEffect::Create(texture, swapRAndB, upmToPM, matrix);
     } else {
-        return false;
+        return NULL;
     }
 }
 
@@ -1853,8 +1900,9 @@
         scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
                              i < scaleFactorY ? 0.5f : 1.0f);
 
-        paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect,
-                                                  (srcTexture, matrix, true)))->unref();
+        paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(srcTexture,
+                                                                     matrix,
+                                                                     true))->unref();
         this->drawRectToRect(paint, dstRect, srcRect);
         srcRect = dstRect;
         srcTexture = dstTexture;
@@ -1911,8 +1959,9 @@
         // FIXME:  This should be mitchell, not bilinear.
         matrix.setIDiv(srcTexture->width(), srcTexture->height());
         this->setRenderTarget(dstTexture->asRenderTarget());
-        paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect,(srcTexture,
-                                                                         matrix, true)))->unref();
+        paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(srcTexture,
+                                                                     matrix,
+                                                                     true))->unref();
         SkRect dstRect(srcRect);
         scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
         this->drawRectToRect(paint, dstRect, srcRect);
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index bdb1230..b6c521f 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -7,13 +7,16 @@
 
 #include "GrDrawState.h"
 
+#include "GrGpuVertex.h"
 #include "GrPaint.h"
 
 void GrDrawState::setFromPaint(const GrPaint& paint) {
     for (int i = 0; i < GrPaint::kMaxColorStages; ++i) {
         int s = i + GrPaint::kFirstColorStage;
         if (paint.isColorStageEnabled(i)) {
-            *this->stage(s) = paint.getColorStage(i);
+            fStages[s] = paint.getColorStage(i);
+        } else {
+            fStages[s].setEffect(NULL);
         }
     }
 
@@ -22,7 +25,9 @@
     for (int i = 0; i < GrPaint::kMaxCoverageStages; ++i) {
         int s = i + GrPaint::kFirstCoverageStage;
         if (paint.isCoverageStageEnabled(i)) {
-            *this->stage(s) = paint.getCoverageStage(i);
+            fStages[s] = paint.getCoverageStage(i);
+        } else {
+            fStages[s].setEffect(NULL);
         }
     }
 
@@ -43,12 +48,522 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+namespace {
+
+/**
+ * This function generates some masks that we like to have known at compile
+ * time. When the number of stages or tex coords is bumped or the way bits
+ * are defined in GrDrawState.h changes this function should be rerun to
+ * generate the new masks. (We attempted to force the compiler to generate the
+ * masks using recursive templates but always wound up with static initializers
+ * under gcc, even if they were just a series of immediate->memory moves.)
+ *
+ */
+void gen_mask_arrays(GrVertexLayout* stageTexCoordMasks,
+                     GrVertexLayout* texCoordMasks) {
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        stageTexCoordMasks[s] = 0;
+        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+            stageTexCoordMasks[s] |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
+        }
+    }
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        texCoordMasks[t] = 0;
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            texCoordMasks[t] |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
+        }
+    }
+}
+
+/**
+ * Uncomment and run the gen_globals function to generate
+ * the code that declares the global masks.
+ *
+ * #if 0'ed out to avoid unused function warning.
+ */
+
+#if 0
+void gen_globals() {
+    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
+    GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
+    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
+
+    GrPrintf("const GrVertexLayout gStageTexCoordMasks[] = {\n");
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrPrintf("    0x%x,\n", stageTexCoordMasks[s]);
+    }
+    GrPrintf("};\n");
+    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));\n\n");
+    GrPrintf("const GrVertexLayout gTexCoordMasks[] = {\n");
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        GrPrintf("    0x%x,\n", texCoordMasks[t]);
+    }
+    GrPrintf("};\n");
+    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));\n");
+}
+#endif
+
+/* These values were generated by the above function */
+
+const GrVertexLayout gStageTexCoordMasks[] = {
+    0x108421,
+    0x210842,
+    0x421084,
+    0x842108,
+    0x1084210,
+};
+GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));
+
+const GrVertexLayout gTexCoordMasks[] = {
+    0x1f,
+    0x3e0,
+    0x7c00,
+    0xf8000,
+    0x1f00000,
+};
+GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));
+
+#ifdef SK_DEBUG
+bool check_layout(GrVertexLayout layout) {
+    // can only have 1 or 0 bits set for each stage.
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        int stageBits = layout & gStageTexCoordMasks[s];
+        if (stageBits && !GrIsPow2(stageBits)) {
+            return false;
+        }
+    }
+    return true;
+}
+#endif
+
+int num_tex_coords(GrVertexLayout layout) {
+    int cnt = 0;
+    // figure out how many tex coordinates are present
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (gTexCoordMasks[t] & layout) {
+            ++cnt;
+        }
+    }
+    return cnt;
+}
+
+} //unnamed namespace
+
+size_t GrDrawState::VertexSize(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    size_t vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                        sizeof(GrGpuTextVertex) :
+                        sizeof(GrPoint);
+
+    size_t size = vecSize; // position
+    size += num_tex_coords(vertexLayout) * vecSize;
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
+    if (vertexLayout & kEdge_VertexLayoutBit) {
+        size += 4 * sizeof(SkScalar);
+    }
+    return size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Functions for computing offsets of various components from the layout
+ * bitfield.
+ *
+ * Order of vertex components:
+ * Position
+ * Tex Coord 0
+ * ...
+ * Tex Coord GrDrawState::kMaxTexCoords-1
+ * Color
+ * Coverage
+ */
+
+int GrDrawState::VertexStageCoordOffset(int stageIdx, GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (!StageUsesTexCoords(vertexLayout, stageIdx)) {
+        return 0;
+    }
+    int tcIdx = VertexTexCoordsForStage(stageIdx, vertexLayout);
+    if (tcIdx >= 0) {
+
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize; // position
+        // figure out how many tex coordinates are present and precede this one.
+        for (int t = 0; t < tcIdx; ++t) {
+            if (gTexCoordMasks[t] & vertexLayout) {
+                offset += vecSize;
+            }
+        }
+        return offset;
+    }
+
+    return -1;
+}
+
+int GrDrawState::VertexColorOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+    }
+    return -1;
+}
+
+int GrDrawState::VertexCoverageOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+
+        int offset = vecSize * (num_tex_coords(vertexLayout) + 1);
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        return offset;
+    }
+    return -1;
+}
+
+int GrDrawState::VertexEdgeOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    // edge pts are after the pos, tex coords, and color
+    if (vertexLayout & kEdge_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        if (vertexLayout & kCoverage_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        return offset;
+    }
+    return -1;
+}
+
+int GrDrawState::VertexSizeAndOffsetsByIdx(
+        GrVertexLayout vertexLayout,
+        int texCoordOffsetsByIdx[kMaxTexCoords],
+        int* colorOffset,
+        int* coverageOffset,
+        int* edgeOffset) {
+    GrAssert(check_layout(vertexLayout));
+
+    int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                                    sizeof(GrGpuTextVertex) :
+                                                    sizeof(GrPoint);
+    int size = vecSize; // position
+
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        if (gTexCoordMasks[t] & vertexLayout) {
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = size;
+            }
+            size += vecSize;
+        } else {
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = -1;
+            }
+        }
+    }
+    if (kColor_VertexLayoutBit & vertexLayout) {
+        if (NULL != colorOffset) {
+            *colorOffset = size;
+        }
+        size += sizeof(GrColor);
+    } else {
+        if (NULL != colorOffset) {
+            *colorOffset = -1;
+        }
+    }
+    if (kCoverage_VertexLayoutBit & vertexLayout) {
+        if (NULL != coverageOffset) {
+            *coverageOffset = size;
+        }
+        size += sizeof(GrColor);
+    } else {
+        if (NULL != coverageOffset) {
+            *coverageOffset = -1;
+        }
+    }
+    if (kEdge_VertexLayoutBit & vertexLayout) {
+        if (NULL != edgeOffset) {
+            *edgeOffset = size;
+        }
+        size += 4 * sizeof(SkScalar);
+    } else {
+        if (NULL != edgeOffset) {
+            *edgeOffset = -1;
+        }
+    }
+    return size;
+}
+
+int GrDrawState::VertexSizeAndOffsetsByStage(
+        GrVertexLayout vertexLayout,
+        int texCoordOffsetsByStage[GrDrawState::kNumStages],
+        int* colorOffset,
+        int* coverageOffset,
+        int* edgeOffset) {
+    GrAssert(check_layout(vertexLayout));
+
+    int texCoordOffsetsByIdx[kMaxTexCoords];
+    int size = VertexSizeAndOffsetsByIdx(vertexLayout,
+                                         (NULL == texCoordOffsetsByStage) ?
+                                               NULL :
+                                               texCoordOffsetsByIdx,
+                                         colorOffset,
+                                         coverageOffset,
+                                         edgeOffset);
+    if (NULL != texCoordOffsetsByStage) {
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            int tcIdx = VertexTexCoordsForStage(s, vertexLayout);
+            texCoordOffsetsByStage[s] =
+                tcIdx < 0 ? 0 : texCoordOffsetsByIdx[tcIdx];
+        }
+    }
+    return size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool GrDrawState::VertexUsesTexCoordIdx(int coordIndex,
+                                         GrVertexLayout vertexLayout) {
+    GrAssert(coordIndex < kMaxTexCoords);
+    GrAssert(check_layout(vertexLayout));
+    return !!(gTexCoordMasks[coordIndex] & vertexLayout);
+}
+
+int GrDrawState::VertexTexCoordsForStage(int stageIdx,
+                                          GrVertexLayout vertexLayout) {
+    GrAssert(stageIdx < GrDrawState::kNumStages);
+    GrAssert(check_layout(vertexLayout));
+    int bit = vertexLayout & gStageTexCoordMasks[stageIdx];
+    if (bit) {
+        // figure out which set of texture coordates is used
+        // bits are ordered T0S0, T0S1, T0S2, ..., T1S0, T1S1, ...
+        // and start at bit 0.
+        GR_STATIC_ASSERT(sizeof(GrVertexLayout) <= sizeof(uint32_t));
+        return (32 - SkCLZ(bit) - 1) / GrDrawState::kNumStages;
+    }
+    return -1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawState::VertexLayoutUnitTest() {
+    // Ensure that our globals mask arrays are correct
+    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
+    GrVertexLayout texCoordMasks[kMaxTexCoords];
+    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrAssert(stageTexCoordMasks[s] == gStageTexCoordMasks[s]);
+    }
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        GrAssert(texCoordMasks[t] == gTexCoordMasks[t]);
+    }
+
+    // not necessarily exhaustive
+    static bool run;
+    if (!run) {
+        run = true;
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+
+            GrVertexLayout stageMask = 0;
+            for (int t = 0; t < kMaxTexCoords; ++t) {
+                stageMask |= StageTexCoordVertexLayoutBit(s,t);
+            }
+            GrAssert(1 == kMaxTexCoords ||
+                     !check_layout(stageMask));
+            GrAssert(gStageTexCoordMasks[s] == stageMask);
+            GrAssert(!check_layout(stageMask));
+        }
+        for (int t = 0; t < kMaxTexCoords; ++t) {
+            GrVertexLayout tcMask = 0;
+            GrAssert(!VertexUsesTexCoordIdx(t, 0));
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                tcMask |= StageTexCoordVertexLayoutBit(s,t);
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+                GrAssert(VertexUsesTexCoordIdx(t, tcMask));
+                GrAssert(2*sizeof(GrPoint) == VertexSize(tcMask));
+                GrAssert(t == VertexTexCoordsForStage(s, tcMask));
+                for (int s2 = s + 1; s2 < GrDrawState::kNumStages; ++s2) {
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
+
+                #if GR_DEBUG
+                    GrVertexLayout posAsTex = tcMask;
+                #endif
+                    GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
+                    GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
+                    GrAssert(-1 == VertexEdgeOffset(posAsTex));
+                }
+                GrAssert(-1 == VertexEdgeOffset(tcMask));
+                GrAssert(-1 == VertexColorOffset(tcMask));
+                GrAssert(-1 == VertexCoverageOffset(tcMask));
+            #if GR_DEBUG
+                GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexCoverageOffset(withColor));
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
+            #if GR_DEBUG
+                GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexColorOffset(withEdge));
+                GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
+                GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
+            #if GR_DEBUG
+                GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
+            #endif
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
+                GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
+            #if GR_DEBUG
+                GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexColorOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage));
+            #if GR_DEBUG
+                GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit |
+                                                      kColor_VertexLayoutBit;
+            #endif
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor));
+            }
+            GrAssert(gTexCoordMasks[t] == tcMask);
+            GrAssert(check_layout(tcMask));
+
+            int stageOffsets[GrDrawState::kNumStages];
+            int colorOffset;
+            int edgeOffset;
+            int coverageOffset;
+            int size;
+            size = VertexSizeAndOffsetsByStage(tcMask,
+                                               stageOffsets, &colorOffset,
+                                               &coverageOffset, &edgeOffset);
+            GrAssert(2*sizeof(GrPoint) == size);
+            GrAssert(-1 == colorOffset);
+            GrAssert(-1 == coverageOffset);
+            GrAssert(-1 == edgeOffset);
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                GrAssert(sizeof(GrPoint) == stageOffsets[s]);
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+            }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool GrDrawState::StageUsesTexCoords(GrVertexLayout layout, int stageIdx) {
+    return SkToBool(layout & gStageTexCoordMasks[stageIdx]);
+}
+
+bool GrDrawState::srcAlphaWillBeOne(GrVertexLayout layout) const {
+
+    uint32_t validComponentFlags;
+    GrColor  color;
+    // Check if per-vertex or constant color may have partial alpha
+    if (layout & kColor_VertexLayoutBit) {
+        validComponentFlags = 0;
+    } else {
+        validComponentFlags = GrEffect::kAll_ValidComponentFlags;
+        color = this->getColor();
+    }
+
+    // Run through the color stages
+    int stageCnt = getFirstCoverageStage();
+    for (int s = 0; s < stageCnt; ++s) {
+        const GrEffectRef* effect = this->getStage(s).getEffect();
+        if (NULL != effect) {
+            (*effect)->getConstantColorComponents(&color, &validComponentFlags);
+        }
+    }
+
+    // Check if the color filter could introduce an alpha.
+    // We could skip the above work when this is true, but it is rare and the right fix is to make
+    // the color filter a GrEffect and implement getConstantColorComponents() for it.
+    if (SkXfermode::kDst_Mode != this->getColorFilterMode()) {
+        validComponentFlags = 0;
+    }
+
+    // Check whether coverage is treated as color. If so we run through the coverage computation.
+    if (this->isCoverageDrawing()) {
+        GrColor coverageColor = this->getCoverage();
+        GrColor oldColor = color;
+        color = 0;
+        for (int c = 0; c < 4; ++c) {
+            if (validComponentFlags & (1 << c)) {
+                U8CPU a = (oldColor >> (c * 8)) & 0xff;
+                U8CPU b = (coverageColor >> (c * 8)) & 0xff;
+                color |= (SkMulDiv255Round(a, b) << (c * 8));
+            }
+        }
+        for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
+            const GrEffectRef* effect = this->getStage(s).getEffect();
+            if (NULL != effect) {
+                (*effect)->getConstantColorComponents(&color, &validComponentFlags);
+            }
+        }
+    }
+    return (GrEffect::kA_ValidComponentFlag & validComponentFlags) && 0xff == GrColorUnpackA(color);
+}
+
+bool GrDrawState::hasSolidCoverage(GrVertexLayout layout) const {
+    // If we're drawing coverage directly then coverage is effectively treated as color.
+    if (this->isCoverageDrawing()) {
+        return true;
+    }
+
+    GrColor coverage;
+    uint32_t validComponentFlags;
+    // Initialize to an unknown starting coverage if per-vertex coverage is specified.
+    if (layout & kCoverage_VertexLayoutBit) {
+        validComponentFlags = 0;
+    } else {
+        coverage = fCommon.fCoverage;
+        validComponentFlags = GrEffect::kAll_ValidComponentFlags;
+    }
+
+    // Run through the coverage stages and see if the coverage will be all ones at the end.
+    for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
+        const GrEffectRef* effect = this->getStage(s).getEffect();
+        if (NULL != effect) {
+            (*effect)->getConstantColorComponents(&coverage, &validComponentFlags);
+        }
+    }
+    return (GrEffect::kAll_ValidComponentFlags == validComponentFlags)  && (0xffffffff == coverage);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 void GrDrawState::AutoViewMatrixRestore::restore() {
     if (NULL != fDrawState) {
         fDrawState->setViewMatrix(fViewMatrix);
         for (int s = 0; s < GrDrawState::kNumStages; ++s) {
             if (fRestoreMask & (1 << s)) {
-                fDrawState->stage(s)->restoreCoordChange(fSavedCoordChanges[s]);
+                fDrawState->fStages[s].restoreCoordChange(fSavedCoordChanges[s]);
             }
         }
     }
@@ -71,8 +586,8 @@
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
         if (!(explicitCoordStageMask & (1 << s)) && drawState->isStageEnabled(s)) {
             fRestoreMask |= (1 << s);
-            fDrawState->stage(s)->saveCoordChange(&fSavedCoordChanges[s]);
-            drawState->stage(s)->preConcatCoordChange(preconcatMatrix);
+            fDrawState->fStages[s].saveCoordChange(&fSavedCoordChanges[s]);
+            drawState->fStages[s].preConcatCoordChange(preconcatMatrix);
         }
     }
 }
@@ -84,7 +599,7 @@
         fDrawState->setViewMatrix(fViewMatrix);
         for (int s = 0; s < GrDrawState::kNumStages; ++s) {
             if (fRestoreMask & (1 << s)) {
-                fDrawState->stage(s)->restoreCoordChange(fSavedCoordChanges[s]);
+                fDrawState->fStages[s].restoreCoordChange(fSavedCoordChanges[s]);
             }
         }
     }
@@ -117,7 +632,7 @@
                 inverted = true;
             }
             fRestoreMask |= (1 << s);
-            GrEffectStage* stage = drawState->stage(s);
+            GrEffectStage* stage = drawState->fStages + s;
             stage->saveCoordChange(&fSavedCoordChanges[s]);
             stage->preConcatCoordChange(invVM);
         }
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 1208b77..0a0b0da 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -10,14 +10,15 @@
 
 #include "GrBackendEffectFactory.h"
 #include "GrColor.h"
-#include "SkMatrix.h"
-#include "GrRefCnt.h"
 #include "GrEffectStage.h"
-#include "GrStencil.h"
-#include "GrTexture.h"
+#include "GrRefCnt.h"
 #include "GrRenderTarget.h"
-#include "effects/GrSingleTextureEffect.h"
+#include "GrStencil.h"
+#include "GrTemplates.h"
+#include "GrTexture.h"
+#include "effects/GrSimpleTextureEffect.h"
 
+#include "SkMatrix.h"
 #include "SkXfermode.h"
 
 class GrPaint;
@@ -57,21 +58,19 @@
         kMaxTexCoords = kNumStages
     };
 
-    GrDrawState()
-        : fRenderTarget(NULL) {
-
+    GrDrawState() {
+#if GR_DEBUG
+        VertexLayoutUnitTest();
+#endif
         this->reset();
     }
 
-    GrDrawState(const GrDrawState& state)
-        : fRenderTarget(NULL) {
-
+    GrDrawState(const GrDrawState& state) {
         *this = state;
     }
 
     virtual ~GrDrawState() {
         this->disableStages();
-        GrSafeSetNull(fRenderTarget);
     }
 
     /**
@@ -82,20 +81,21 @@
 
         this->disableStages();
 
-        fColor = 0xffffffff;
-        fViewMatrix.reset();
-        GrSafeSetNull(fRenderTarget);
-        fSrcBlend = kOne_GrBlendCoeff;
-        fDstBlend = kZero_GrBlendCoeff;
-        fBlendConstant = 0x0;
-        fFlagBits = 0x0;
-        fVertexEdgeType = kHairLine_EdgeType;
-        fStencilSettings.setDisabled();
-        fFirstCoverageStage = kNumStages;
-        fCoverage = 0xffffffff;
-        fColorFilterMode = SkXfermode::kDst_Mode;
-        fColorFilterColor = 0x0;
-        fDrawFace = kBoth_DrawFace;
+        fRenderTarget.reset(NULL);
+
+        fCommon.fColor = 0xffffffff;
+        fCommon.fViewMatrix.reset();
+        fCommon.fSrcBlend = kOne_GrBlendCoeff;
+        fCommon.fDstBlend = kZero_GrBlendCoeff;
+        fCommon.fBlendConstant = 0x0;
+        fCommon.fFlagBits = 0x0;
+        fCommon.fVertexEdgeType = kHairLine_EdgeType;
+        fCommon.fStencilSettings.setDisabled();
+        fCommon.fFirstCoverageStage = kNumStages;
+        fCommon.fCoverage = 0xffffffff;
+        fCommon.fColorFilterMode = SkXfermode::kDst_Mode;
+        fCommon.fColorFilterColor = 0x0;
+        fCommon.fDrawFace = kBoth_DrawFace;
     }
 
     /**
@@ -107,6 +107,276 @@
     void setFromPaint(const GrPaint& paint);
 
     ///////////////////////////////////////////////////////////////////////////
+    /// @name Vertex Format
+    ////
+
+    /**
+     * The format of vertices is represented as a bitfield of flags.
+     * Flags that indicate the layout of vertex data. Vertices always contain
+     * positions and may also contain up to GrDrawState::kMaxTexCoords sets
+     * of 2D texture coordinates, per-vertex colors, and per-vertex coverage.
+     * Each stage can
+     * use any of the texture coordinates as its input texture coordinates or it
+     * may use the positions as texture coordinates.
+     *
+     * If no texture coordinates are specified for a stage then the stage is
+     * disabled.
+     *
+     * Only one type of texture coord can be specified per stage. For
+     * example StageTexCoordVertexLayoutBit(0, 2) and
+     * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified.
+     *
+     * The order in memory is always (position, texture coord 0, ..., color,
+     * coverage) with any unused fields omitted. Note that this means that if
+     * only texture coordinates 1 is referenced then there is no texture
+     * coordinates 0 and the order would be (position, texture coordinate 1
+     * [, color][, coverage]).
+     */
+
+    /**
+     * Generates a bit indicating that a texture stage uses texture coordinates
+     *
+     * @param stageIdx    the stage that will use texture coordinates.
+     * @param texCoordIdx the index of the texture coordinates to use
+     *
+     * @return the bit to add to a GrVertexLayout bitfield.
+     */
+    static int StageTexCoordVertexLayoutBit(int stageIdx, int texCoordIdx) {
+        GrAssert(stageIdx < kNumStages);
+        GrAssert(texCoordIdx < kMaxTexCoords);
+        return 1 << (stageIdx + (texCoordIdx * kNumStages));
+    }
+
+    static bool StageUsesTexCoords(GrVertexLayout layout, int stageIdx);
+
+private:
+    // non-stage bits start at this index.
+    static const int STAGE_BIT_CNT = kNumStages * kMaxTexCoords;
+public:
+
+    /**
+     * Additional Bits that can be specified in GrVertexLayout.
+     */
+    enum VertexLayoutBits {
+        /* vertices have colors (GrColor) */
+        kColor_VertexLayoutBit              = 1 << (STAGE_BIT_CNT + 0),
+        /* vertices have coverage (GrColor)
+         */
+        kCoverage_VertexLayoutBit           = 1 << (STAGE_BIT_CNT + 1),
+        /* Use text vertices. (Pos and tex coords may be a different type for
+         * text [GrGpuTextVertex vs GrPoint].)
+         */
+        kTextFormat_VertexLayoutBit         = 1 << (STAGE_BIT_CNT + 2),
+
+        /* Each vertex specificies an edge. Distance to the edge is used to
+         * compute a coverage. See GrDrawState::setVertexEdgeType().
+         */
+        kEdge_VertexLayoutBit               = 1 << (STAGE_BIT_CNT + 3),
+        // for below assert
+        kDummyVertexLayoutBit,
+        kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
+    };
+    // make sure we haven't exceeded the number of bits in GrVertexLayout.
+    GR_STATIC_ASSERT(kHighVertexLayoutBit < ((uint64_t)1 << 8*sizeof(GrVertexLayout)));
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Helpers for picking apart vertex layouts
+
+    /**
+     * Helper function to compute the size of a vertex from a vertex layout
+     * @return size of a single vertex.
+     */
+    static size_t VertexSize(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function for determining the index of texture coordinates that
+     * is input for a texture stage. Note that a stage may instead use positions
+     * as texture coordinates, in which case the result of the function is
+     * indistinguishable from the case when the stage is disabled.
+     *
+     * @param stageIdx      the stage to query
+     * @param vertexLayout  layout to query
+     *
+     * @return the texture coordinate index or -1 if the stage doesn't use
+     *         separate (non-position) texture coordinates.
+     */
+    static int VertexTexCoordsForStage(int stageIdx, GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of texture coordinates in a vertex
+     * @return offset of texture coordinates in vertex layout or -1 if the
+     *         layout has no texture coordinates. Will be 0 if positions are
+     *         used as texture coordinates for the stage.
+     */
+    static int VertexStageCoordOffset(int stageIdx, GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of the color in a vertex
+     * @return offset of color in vertex layout or -1 if the
+     *         layout has no color.
+     */
+    static int VertexColorOffset(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of the coverage in a vertex
+     * @return offset of coverage in vertex layout or -1 if the
+     *         layout has no coverage.
+     */
+    static int VertexCoverageOffset(GrVertexLayout vertexLayout);
+
+     /**
+      * Helper function to compute the offset of the edge pts in a vertex
+      * @return offset of edge in vertex layout or -1 if the
+      *         layout has no edge.
+      */
+     static int VertexEdgeOffset(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to determine if vertex layout contains explicit texture
+     * coordinates of some index.
+     *
+     * @param coordIndex    the tex coord index to query
+     * @param vertexLayout  layout to query
+     *
+     * @return true if vertex specifies texture coordinates for the index,
+     *              false otherwise.
+     */
+    static bool VertexUsesTexCoordIdx(int coordIndex,
+                                      GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the size of each vertex and the offsets of
+     * texture coordinates and color. Determines tex coord offsets by tex coord
+     * index rather than by stage. (Each stage can be mapped to any t.c. index
+     * by StageTexCoordVertexLayoutBit.)
+     *
+     * @param vertexLayout          the layout to query
+     * @param texCoordOffsetsByIdx  after return it is the offset of each
+     *                              tex coord index in the vertex or -1 if
+     *                              index isn't used. (optional)
+     * @param colorOffset           after return it is the offset of the
+     *                              color field in each vertex, or -1 if
+     *                              there aren't per-vertex colors. (optional)
+     * @param coverageOffset        after return it is the offset of the
+     *                              coverage field in each vertex, or -1 if
+     *                              there aren't per-vertex coeverages.
+     *                              (optional)
+     * @param edgeOffset            after return it is the offset of the
+     *                              edge eq field in each vertex, or -1 if
+     *                              there aren't per-vertex edge equations.
+     *                              (optional)
+     * @return size of a single vertex
+     */
+    static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
+                   int texCoordOffsetsByIdx[kMaxTexCoords],
+                   int *colorOffset,
+                   int *coverageOffset,
+                   int* edgeOffset);
+
+    /**
+     * Helper function to compute the size of each vertex and the offsets of
+     * texture coordinates and color. Determines tex coord offsets by stage
+     * rather than by index. (Each stage can be mapped to any t.c. index
+     * by StageTexCoordVertexLayoutBit.) If a stage uses positions for
+     * tex coords then that stage's offset will be 0 (positions are always at 0).
+     *
+     * @param vertexLayout              the layout to query
+     * @param texCoordOffsetsByStage    after return it is the offset of each
+     *                                  tex coord index in the vertex or -1 if
+     *                                  index isn't used. (optional)
+     * @param colorOffset               after return it is the offset of the
+     *                                  color field in each vertex, or -1 if
+     *                                  there aren't per-vertex colors.
+     *                                  (optional)
+     * @param coverageOffset            after return it is the offset of the
+     *                                  coverage field in each vertex, or -1 if
+     *                                  there aren't per-vertex coeverages.
+     *                                  (optional)
+     * @param edgeOffset                after return it is the offset of the
+     *                                  edge eq field in each vertex, or -1 if
+     *                                  there aren't per-vertex edge equations.
+     *                                  (optional)
+     * @return size of a single vertex
+     */
+    static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
+                   int texCoordOffsetsByStage[kNumStages],
+                   int* colorOffset,
+                   int* coverageOffset,
+                   int* edgeOffset);
+
+    /**
+     * Determines whether src alpha is guaranteed to be one for all src pixels
+     */
+    bool srcAlphaWillBeOne(GrVertexLayout) const;
+
+    /**
+     * Determines whether the output coverage is guaranteed to be one for all pixels hit by a draw.
+     */
+    bool hasSolidCoverage(GrVertexLayout) const;
+
+    /**
+     * Accessing positions, texture coords, or colors, of a vertex within an
+     * array is a hassle involving casts and simple math. These helpers exist
+     * to keep GrDrawTarget clients' code a bit nicer looking.
+     */
+
+    /**
+     * Gets a pointer to a GrPoint of a vertex's position or texture
+     * coordinate.
+     * @param vertices      the vetex array
+     * @param vertexIndex   the index of the vertex in the array
+     * @param vertexSize    the size of each vertex in the array
+     * @param offset        the offset in bytes of the vertex component.
+     *                      Defaults to zero (corresponding to vertex position)
+     * @return pointer to the vertex component as a GrPoint
+     */
+    static GrPoint* GetVertexPoint(void* vertices,
+                                   int vertexIndex,
+                                   int vertexSize,
+                                   int offset = 0) {
+        intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<GrPoint*>(start + offset +
+                                 vertexIndex * vertexSize);
+    }
+    static const GrPoint* GetVertexPoint(const void* vertices,
+                                         int vertexIndex,
+                                         int vertexSize,
+                                         int offset = 0) {
+        intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<const GrPoint*>(start + offset +
+                                       vertexIndex * vertexSize);
+    }
+
+    /**
+     * Gets a pointer to a GrColor inside a vertex within a vertex array.
+     * @param vertices      the vetex array
+     * @param vertexIndex   the index of the vertex in the array
+     * @param vertexSize    the size of each vertex in the array
+     * @param offset        the offset in bytes of the vertex color
+     * @return pointer to the vertex component as a GrColor
+     */
+    static GrColor* GetVertexColor(void* vertices,
+                                   int vertexIndex,
+                                   int vertexSize,
+                                   int offset) {
+        intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<GrColor*>(start + offset +
+                                 vertexIndex * vertexSize);
+    }
+    static const GrColor* GetVertexColor(const void* vertices,
+                                         int vertexIndex,
+                                         int vertexSize,
+                                         int offset) {
+        const intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<const GrColor*>(start + offset +
+                                       vertexIndex * vertexSize);
+    }
+
+    static void VertexLayoutUnitTest();
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
     /// @name Color
     ////
 
@@ -115,9 +385,9 @@
      *
      *  @param color    the color to set.
      */
-    void setColor(GrColor color) { fColor = color; }
+    void setColor(GrColor color) { fCommon.fColor = color; }
 
-    GrColor getColor() const { return fColor; }
+    GrColor getColor() const { return fCommon.fColor; }
 
     /**
      *  Sets the color to be used for the next draw to be
@@ -134,26 +404,40 @@
      * after color-computing texture stages.
      */
     void setColorFilter(GrColor c, SkXfermode::Mode mode) {
-        fColorFilterColor = c;
-        fColorFilterMode = mode;
+        fCommon.fColorFilterColor = c;
+        fCommon.fColorFilterMode = mode;
     }
 
-    GrColor getColorFilterColor() const { return fColorFilterColor; }
-    SkXfermode::Mode getColorFilterMode() const { return fColorFilterMode; }
+    GrColor getColorFilterColor() const { return fCommon.fColorFilterColor; }
+    SkXfermode::Mode getColorFilterMode() const { return fCommon.fColorFilterMode; }
 
     /**
      * Constructor sets the color to be 'color' which is undone by the destructor.
      */
     class AutoColorRestore : public ::GrNoncopyable {
     public:
+        AutoColorRestore() : fDrawState(NULL) {}
+
         AutoColorRestore(GrDrawState* drawState, GrColor color) {
+            fDrawState = NULL;
+            this->set(drawState, color);
+        }
+
+        void reset() {
+            if (NULL != fDrawState) {
+                fDrawState->setColor(fOldColor);
+                fDrawState = NULL;
+            }
+        }
+
+        void set(GrDrawState* drawState, GrColor color) {
+            this->reset();
             fDrawState = drawState;
             fOldColor = fDrawState->getColor();
             fDrawState->setColor(color);
         }
-        ~AutoColorRestore() {
-            fDrawState->setColor(fOldColor);
-        }
+
+        ~AutoColorRestore() { this->reset(); }
     private:
         GrDrawState*    fDrawState;
         GrColor         fOldColor;
@@ -171,7 +455,7 @@
      * coverage is ignored when per-vertex coverage is provided.
      */
     void setCoverage(uint8_t coverage) {
-        fCoverage = GrColorPackRGBA(coverage, coverage, coverage, coverage);
+        fCommon.fCoverage = GrColorPackRGBA(coverage, coverage, coverage, coverage);
     }
 
     /**
@@ -179,41 +463,41 @@
      * should be premultiplied.
      */
     void setCoverage4(GrColor coverage) {
-        fCoverage = coverage;
+        fCommon.fCoverage = coverage;
     }
 
     GrColor getCoverage() const {
-        return fCoverage;
+        return fCommon.fCoverage;
     }
 
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
-    /// @name Textures
+    /// @name Effect Stages
     ////
 
-    /**
-     * Creates a GrSingleTextureEffect.
-     */
-    void createTextureEffect(int stageIdx, GrTexture* texture) {
-        GrAssert(!this->getStage(stageIdx).getEffect());
-        this->stage(stageIdx)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture)))->unref();
+    const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect) {
+        fStages[stageIdx].setEffect(effect);
+        return effect;
     }
+
+    /**
+     * Creates a GrSimpleTextureEffect.
+     */
     void createTextureEffect(int stageIdx, GrTexture* texture, const SkMatrix& matrix) {
         GrAssert(!this->getStage(stageIdx).getEffect());
-        GrEffect* effect = SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix));
-        this->stage(stageIdx)->setEffect(effect)->unref();
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix);
+        this->setEffect(stageIdx, effect)->unref();
     }
     void createTextureEffect(int stageIdx,
                              GrTexture* texture,
                              const SkMatrix& matrix,
                              const GrTextureParams& params) {
         GrAssert(!this->getStage(stageIdx).getEffect());
-        GrEffect* effect = SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix, params));
-        this->stage(stageIdx)->setEffect(effect)->unref();
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
+        this->setEffect(stageIdx, effect)->unref();
     }
 
-
     bool stagesDisabled() {
         for (int i = 0; i < kNumStages; ++i) {
             if (NULL != fStages[i].getEffect()) {
@@ -223,9 +507,7 @@
         return true;
     }
 
-    void disableStage(int stageIdx) {
-        fStages[stageIdx].setEffect(NULL);
-    }
+    void disableStage(int stageIdx) { this->setEffect(stageIdx, NULL); }
 
     /**
      * Release all the GrEffects referred to by this draw state.
@@ -248,12 +530,6 @@
         GrDrawState* fDrawState;
     };
 
-    /// @}
-
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Stages
-    ////
-
     /**
      * Returns the current stage by index.
      */
@@ -263,20 +539,18 @@
     }
 
     /**
-     * Writable pointer to a stage.
-     */
-    GrEffectStage* stage(int stageIdx) {
-        GrAssert((unsigned)stageIdx < kNumStages);
-        return fStages + stageIdx;
-    }
-
-    /**
      * Called when the source coord system is changing. preConcat gives the transformation from the
      * old coord system to the new coord system.
      */
     void preConcatStageMatrices(const SkMatrix& preConcat) {
+        this->preConcatStageMatrices(~0U, preConcat);
+    }
+    /**
+     * Version of above that applies the update matrix selectively to stages via a mask.
+     */
+    void preConcatStageMatrices(uint32_t stageMask, const SkMatrix& preConcat) {
         for (int i = 0; i < kNumStages; ++i) {
-            if (this->isStageEnabled(i)) {
+            if (((1 << i) & stageMask) && this->isStageEnabled(i)) {
                 fStages[i].preConcatCoordChange(preConcat);
             }
         }
@@ -320,14 +594,14 @@
      */
     void setFirstCoverageStage(int firstCoverageStage) {
         GrAssert((unsigned)firstCoverageStage <= kNumStages);
-        fFirstCoverageStage = firstCoverageStage;
+        fCommon.fFirstCoverageStage = firstCoverageStage;
     }
 
     /**
      * Gets the index of the first coverage-computing stage.
      */
     int getFirstCoverageStage() const {
-        return fFirstCoverageStage;
+        return fCommon.fFirstCoverageStage;
     }
 
     ///@}
@@ -350,8 +624,8 @@
      * @param dstCoef coefficient applied to the dst color.
      */
     void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
-        fSrcBlend = srcCoeff;
-        fDstBlend = dstCoeff;
+        fCommon.fSrcBlend = srcCoeff;
+        fCommon.fDstBlend = dstCoeff;
     #if GR_DEBUG
         switch (dstCoeff) {
         case kDC_GrBlendCoeff:
@@ -378,13 +652,13 @@
     #endif
     }
 
-    GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlend; }
-    GrBlendCoeff getDstBlendCoeff() const { return fDstBlend; }
+    GrBlendCoeff getSrcBlendCoeff() const { return fCommon.fSrcBlend; }
+    GrBlendCoeff getDstBlendCoeff() const { return fCommon.fDstBlend; }
 
     void getDstBlendCoeff(GrBlendCoeff* srcBlendCoeff,
                           GrBlendCoeff* dstBlendCoeff) const {
-        *srcBlendCoeff = fSrcBlend;
-        *dstBlendCoeff = fDstBlend;
+        *srcBlendCoeff = fCommon.fSrcBlend;
+        *dstBlendCoeff = fCommon.fDstBlend;
     }
 
     /**
@@ -397,13 +671,13 @@
      *
      * @param constant the constant to set
      */
-    void setBlendConstant(GrColor constant) { fBlendConstant = constant; }
+    void setBlendConstant(GrColor constant) { fCommon.fBlendConstant = constant; }
 
     /**
      * Retrieves the last value set by setBlendConstant()
      * @return the blending constant value
      */
-    GrColor getBlendConstant() const { return fBlendConstant; }
+    GrColor getBlendConstant() const { return fCommon.fBlendConstant; }
 
     /// @}
 
@@ -416,14 +690,14 @@
      *
      * In the post-view-matrix space the rectangle [0,w]x[0,h]
      * fully covers the render target. (w and h are the width and height of the
-     * the rendertarget.)
+     * the render-target.)
      */
-    void setViewMatrix(const SkMatrix& m) { fViewMatrix = m; }
+    void setViewMatrix(const SkMatrix& m) { fCommon.fViewMatrix = m; }
 
     /**
      * Gets a writable pointer to the view matrix.
      */
-    SkMatrix* viewMatrix() { return &fViewMatrix; }
+    SkMatrix* viewMatrix() { return &fCommon.fViewMatrix; }
 
     /**
      *  Multiplies the current view matrix by a matrix
@@ -435,7 +709,7 @@
      *
      *  @param m the matrix used to modify the view matrix.
      */
-    void preConcatViewMatrix(const SkMatrix& m) { fViewMatrix.preConcat(m); }
+    void preConcatViewMatrix(const SkMatrix& m) { fCommon.fViewMatrix.preConcat(m); }
 
     /**
      *  Multiplies the current view matrix by a matrix
@@ -447,13 +721,13 @@
      *
      *  @param m the matrix used to modify the view matrix.
      */
-    void postConcatViewMatrix(const SkMatrix& m) { fViewMatrix.postConcat(m); }
+    void postConcatViewMatrix(const SkMatrix& m) { fCommon.fViewMatrix.postConcat(m); }
 
     /**
      * Retrieves the current view matrix
      * @return the current view matrix.
      */
-    const SkMatrix& getViewMatrix() const { return fViewMatrix; }
+    const SkMatrix& getViewMatrix() const { return fCommon.fViewMatrix; }
 
     /**
      *  Retrieves the inverse of the current view matrix.
@@ -468,7 +742,7 @@
         // TODO: determine whether we really need to leave matrix unmodified
         // at call sites when inversion fails.
         SkMatrix inverse;
-        if (fViewMatrix.invert(&inverse)) {
+        if (fCommon.fViewMatrix.invert(&inverse)) {
             if (matrix) {
                 *matrix = inverse;
             }
@@ -575,21 +849,21 @@
     ////
 
     /**
-     * Sets the rendertarget used at the next drawing call
+     * Sets the render-target used at the next drawing call
      *
      * @param target  The render target to set.
      */
     void setRenderTarget(GrRenderTarget* target) {
-        GrSafeAssign(fRenderTarget, target);
+        fRenderTarget.reset(SkSafeRef(target));
     }
 
     /**
-     * Retrieves the currently set rendertarget.
+     * Retrieves the currently set render-target.
      *
      * @return    The currently set render target.
      */
-    const GrRenderTarget* getRenderTarget() const { return fRenderTarget; }
-    GrRenderTarget* getRenderTarget() { return fRenderTarget; }
+    const GrRenderTarget* getRenderTarget() const { return fRenderTarget.get(); }
+    GrRenderTarget* getRenderTarget() { return fRenderTarget.get(); }
 
     class AutoRenderTargetRestore : public ::GrNoncopyable {
     public:
@@ -639,19 +913,19 @@
      * @param settings  the stencil settings to use.
      */
     void setStencil(const GrStencilSettings& settings) {
-        fStencilSettings = settings;
+        fCommon.fStencilSettings = settings;
     }
 
     /**
      * Shortcut to disable stencil testing and ops.
      */
     void disableStencil() {
-        fStencilSettings.setDisabled();
+        fCommon.fStencilSettings.setDisabled();
     }
 
-    const GrStencilSettings& getStencil() const { return fStencilSettings; }
+    const GrStencilSettings& getStencil() const { return fCommon.fStencilSettings; }
 
-    GrStencilSettings* stencil() { return &fStencilSettings; }
+    GrStencilSettings* stencil() { return &fCommon.fStencilSettings; }
 
     /// @}
 
@@ -686,6 +960,9 @@
         /* Circle specified as center_x, center_y, outer_radius, inner_radius
            all in window space (y-down). */
         kCircle_EdgeType,
+        /* Axis-aligned ellipse specified as center_x, center_y, x_radius, x_radius/y_radius
+           all in window space (y-down). */
+        kEllipse_EdgeType,
 
         kVertexEdgeTypeCnt
     };
@@ -697,10 +974,10 @@
      */
     void setVertexEdgeType(VertexEdgeType type) {
         GrAssert(type >=0 && type < kVertexEdgeTypeCnt);
-        fVertexEdgeType = type;
+        fCommon.fVertexEdgeType = type;
     }
 
-    VertexEdgeType getVertexEdgeType() const { return fVertexEdgeType; }
+    VertexEdgeType getVertexEdgeType() const { return fCommon.fVertexEdgeType; }
 
     /// @}
 
@@ -749,7 +1026,7 @@
     };
 
     void resetStateFlags() {
-        fFlagBits = 0;
+        fCommon.fFlagBits = 0;
     }
 
     /**
@@ -758,7 +1035,7 @@
      * @param stateBits bitfield of StateBits specifying the states to enable
      */
     void enableState(uint32_t stateBits) {
-        fFlagBits |= stateBits;
+        fCommon.fFlagBits |= stateBits;
     }
 
     /**
@@ -767,7 +1044,7 @@
      * @param stateBits bitfield of StateBits specifying the states to disable
      */
     void disableState(uint32_t stateBits) {
-        fFlagBits &= ~(stateBits);
+        fCommon.fFlagBits &= ~(stateBits);
     }
 
     /**
@@ -785,27 +1062,27 @@
     }
 
     bool isDitherState() const {
-        return 0 != (fFlagBits & kDither_StateBit);
+        return 0 != (fCommon.fFlagBits & kDither_StateBit);
     }
 
     bool isHWAntialiasState() const {
-        return 0 != (fFlagBits & kHWAntialias_StateBit);
+        return 0 != (fCommon.fFlagBits & kHWAntialias_StateBit);
     }
 
     bool isClipState() const {
-        return 0 != (fFlagBits & kClip_StateBit);
+        return 0 != (fCommon.fFlagBits & kClip_StateBit);
     }
 
     bool isColorWriteDisabled() const {
-        return 0 != (fFlagBits & kNoColorWrites_StateBit);
+        return 0 != (fCommon.fFlagBits & kNoColorWrites_StateBit);
     }
 
     bool isCoverageDrawing() const {
-        return 0 != (fFlagBits & kCoverageDrawing_StateBit);
+        return 0 != (fCommon.fFlagBits & kCoverageDrawing_StateBit);
     }
 
     bool isStateFlagEnabled(uint32_t stateBit) const {
-        return 0 != (stateBit & fFlagBits);
+        return 0 != (stateBit & fCommon.fFlagBits);
     }
 
     /// @}
@@ -828,7 +1105,7 @@
      */
     void setDrawFace(DrawFace face) {
         GrAssert(kInvalid_DrawFace != face);
-        fDrawFace = face;
+        fCommon.fDrawFace = face;
     }
 
     /**
@@ -836,7 +1113,7 @@
      * or both faces.
      * @return the current draw face(s).
      */
-    DrawFace getDrawFace() const { return fDrawFace; }
+    DrawFace getDrawFace() const { return fCommon.fDrawFace; }
 
     /// @}
 
@@ -850,20 +1127,7 @@
     // Most stages are usually not used, so conditionals here
     // reduce the expected number of bytes touched by 50%.
     bool operator ==(const GrDrawState& s) const {
-        if (fColor != s.fColor ||
-            !s.fViewMatrix.cheapEqualTo(fViewMatrix) ||
-            fRenderTarget != s.fRenderTarget ||
-            fSrcBlend != s.fSrcBlend ||
-            fDstBlend != s.fDstBlend ||
-            fBlendConstant != s.fBlendConstant ||
-            fFlagBits != s.fFlagBits ||
-            fVertexEdgeType != s.fVertexEdgeType ||
-            fStencilSettings != s.fStencilSettings ||
-            fFirstCoverageStage != s.fFirstCoverageStage ||
-            fCoverage != s.fCoverage ||
-            fColorFilterMode != s.fColorFilterMode ||
-            fColorFilterColor != s.fColorFilterColor ||
-            fDrawFace != s.fDrawFace) {
+        if (fRenderTarget.get() != s.fRenderTarget.get() || fCommon != s.fCommon) {
             return false;
         }
 
@@ -880,54 +1144,123 @@
     }
     bool operator !=(const GrDrawState& s) const { return !(*this == s); }
 
-    // Most stages are usually not used, so conditionals here
-    // reduce the expected number of bytes touched by 50%.
-    GrDrawState& operator =(const GrDrawState& s) {
-        fColor = s.fColor;
-        fViewMatrix = s.fViewMatrix;
-        SkRefCnt_SafeAssign(fRenderTarget, s.fRenderTarget);
-        fSrcBlend = s.fSrcBlend;
-        fDstBlend = s.fDstBlend;
-        fBlendConstant = s.fBlendConstant;
-        fFlagBits = s.fFlagBits;
-        fVertexEdgeType = s.fVertexEdgeType;
-        fStencilSettings = s.fStencilSettings;
-        fFirstCoverageStage = s.fFirstCoverageStage;
-        fCoverage = s.fCoverage;
-        fColorFilterMode = s.fColorFilterMode;
-        fColorFilterColor = s.fColorFilterColor;
-        fDrawFace = s.fDrawFace;
-
+    GrDrawState& operator= (const GrDrawState& s) {
+        this->setRenderTarget(s.fRenderTarget.get());
+        fCommon = s.fCommon;
         for (int i = 0; i < kNumStages; i++) {
             if (s.isStageEnabled(i)) {
                 this->fStages[i] = s.fStages[i];
             }
         }
-
         return *this;
     }
 
 private:
 
-    // These fields are roughly sorted by decreasing likelihood of being different in op==
-    GrColor             fColor;
-    SkMatrix            fViewMatrix;
-    GrRenderTarget*     fRenderTarget;
-    GrBlendCoeff        fSrcBlend;
-    GrBlendCoeff        fDstBlend;
-    GrColor             fBlendConstant;
-    uint32_t            fFlagBits;
-    VertexEdgeType      fVertexEdgeType;
-    GrStencilSettings   fStencilSettings;
-    int                 fFirstCoverageStage;
-    GrColor             fCoverage;
-    SkXfermode::Mode    fColorFilterMode;
-    GrColor             fColorFilterColor;
-    DrawFace            fDrawFace;
+    /** Fields that are identical in GrDrawState and GrDrawState::DeferredState. */
+    struct CommonState {
+        // These fields are roughly sorted by decreasing likelihood of being different in op==
+        GrColor                         fColor;
+        SkMatrix                        fViewMatrix;
+        GrBlendCoeff                    fSrcBlend;
+        GrBlendCoeff                    fDstBlend;
+        GrColor                         fBlendConstant;
+        uint32_t                        fFlagBits;
+        VertexEdgeType                  fVertexEdgeType;
+        GrStencilSettings               fStencilSettings;
+        int                             fFirstCoverageStage;
+        GrColor                         fCoverage;
+        SkXfermode::Mode                fColorFilterMode;
+        GrColor                         fColorFilterColor;
+        DrawFace                        fDrawFace;
+        bool operator== (const CommonState& other) const {
+            return fColor == other.fColor &&
+                   fViewMatrix.cheapEqualTo(other.fViewMatrix) &&
+                   fSrcBlend == other.fSrcBlend &&
+                   fDstBlend == other.fDstBlend &&
+                   fBlendConstant == other.fBlendConstant &&
+                   fFlagBits == other.fFlagBits &&
+                   fVertexEdgeType == other.fVertexEdgeType &&
+                   fStencilSettings == other.fStencilSettings &&
+                   fFirstCoverageStage == other.fFirstCoverageStage &&
+                   fCoverage == other.fCoverage &&
+                   fColorFilterMode == other.fColorFilterMode &&
+                   fColorFilterColor == other.fColorFilterColor &&
+                   fDrawFace == other.fDrawFace;
+        }
+        bool operator!= (const CommonState& other) const { return !(*this == other); }
+    };
 
-    // This field must be last; it will not be copied or compared
-    // if the corresponding fTexture[] is NULL.
-    GrEffectStage       fStages[kNumStages];
+    /** GrDrawState uses GrEffectStages to hold stage state which holds a ref on GrEffectRef.
+        DeferredState must directly reference GrEffects, however. */
+    struct SavedEffectStage {
+        SavedEffectStage() : fEffect(NULL) {}
+        const GrEffect*                    fEffect;
+        GrEffectStage::SavedCoordChange    fCoordChange;
+    };
+
+public:
+    /**
+     * DeferredState contains all of the data of a GrDrawState but does not hold refs on GrResource
+     * objects. Resources are allowed to hit zero ref count while in DeferredStates. Their internal
+     * dispose mechanism returns them to the cache. This allows recycling resources through the
+     * the cache while they are in a deferred draw queue.
+     */
+    class DeferredState {
+    public:
+        DeferredState() : fRenderTarget(NULL) {
+            GR_DEBUGCODE(fInitialized = false;)
+        }
+        // TODO: Remove this when DeferredState no longer holds a ref to the RT
+        ~DeferredState() { SkSafeUnref(fRenderTarget); }
+
+        void saveFrom(const GrDrawState& drawState) {
+            fCommon = drawState.fCommon;
+            // TODO: Here we will copy the GrRenderTarget pointer without taking a ref.
+            fRenderTarget = drawState.fRenderTarget.get();
+            SkSafeRef(fRenderTarget);
+            // Here we ref the effects directly rather than the effect-refs. TODO: When the effect-
+            // ref gets fully unref'ed it will cause the underlying effect to unref its resources
+            // and recycle them to the cache (if no one else is holding a ref to the resources).
+            for (int i = 0; i < kNumStages; ++i) {
+                fStages[i].saveFrom(drawState.fStages[i]);
+            }
+            GR_DEBUGCODE(fInitialized = true;)
+        }
+
+        void restoreTo(GrDrawState* drawState) {
+            GrAssert(fInitialized);
+            drawState->fCommon = fCommon;
+            drawState->setRenderTarget(fRenderTarget);
+            for (int i = 0; i < kNumStages; ++i) {
+                fStages[i].restoreTo(&drawState->fStages[i]);
+            }
+        }
+
+        bool isEqual(const GrDrawState& state) const {
+            if (fRenderTarget != state.fRenderTarget.get() || fCommon != state.fCommon) {
+                return false;
+            }
+            for (int i = 0; i < kNumStages; ++i) {
+                if (!fStages[i].isEqual(state.fStages[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+    private:
+        GrRenderTarget*                 fRenderTarget;
+        CommonState                     fCommon;
+        GrEffectStage::DeferredStage    fStages[kNumStages];
+
+        GR_DEBUGCODE(bool fInitialized;)
+    };
+
+private:
+    SkAutoTUnref<GrRenderTarget>    fRenderTarget;
+    CommonState                     fCommon;
+    GrEffectStage                   fStages[kNumStages];
 
     typedef GrRefCnt INHERITED;
 };
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index f80a441..2cbcf19 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -9,7 +9,6 @@
 
 
 #include "GrDrawTarget.h"
-#include "GrGpuVertex.h"
 #include "GrRenderTarget.h"
 #include "GrTexture.h"
 #include "GrVertexBuffer.h"
@@ -18,422 +17,64 @@
 
 SK_DEFINE_INST_COUNT(GrDrawTarget)
 
-namespace {
-
-/**
- * This function generates some masks that we like to have known at compile
- * time. When the number of stages or tex coords is bumped or the way bits
- * are defined in GrDrawTarget.h changes this function should be rerun to
- * generate the new masks. (We attempted to force the compiler to generate the
- * masks using recursive templates but always wound up with static initializers
- * under gcc, even if they were just a series of immediate->memory moves.)
- *
- */
-void gen_mask_arrays(GrVertexLayout* stageTexCoordMasks,
-                     GrVertexLayout* texCoordMasks) {
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        stageTexCoordMasks[s] = 0;
-        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-            stageTexCoordMasks[s] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
-        }
-    }
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        texCoordMasks[t] = 0;
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            texCoordMasks[t] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
-        }
-    }
-}
-
-/**
- * Run this function to generate the code that declares the global masks.
- */
-void gen_globals() {
-    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
-    GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
-    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
-
-    GrPrintf("const GrVertexLayout gStageTexCoordMasks[] = {\n");
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrPrintf("    0x%x,\n", stageTexCoordMasks[s]);
-    }
-    GrPrintf("};\n");
-    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));\n\n");
-    GrPrintf("const GrVertexLayout gTexCoordMasks[] = {\n");
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        GrPrintf("    0x%x,\n", texCoordMasks[t]);
-    }
-    GrPrintf("};\n");
-    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));\n");
-}
-
-/* These values were generated by the above function */
-
-const GrVertexLayout gStageTexCoordMasks[] = {
-    0x108421,
-    0x210842,
-    0x421084,
-    0x842108,
-    0x1084210,
-};
-GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));
-
-const GrVertexLayout gTexCoordMasks[] = {
-    0x1f,
-    0x3e0,
-    0x7c00,
-    0xf8000,
-    0x1f00000,
-};
-GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));
-
-bool check_layout(GrVertexLayout layout) {
-    // can only have 1 or 0 bits set for each stage.
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        int stageBits = layout & gStageTexCoordMasks[s];
-        if (stageBits && !GrIsPow2(stageBits)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-int num_tex_coords(GrVertexLayout layout) {
-    int cnt = 0;
-    // figure out how many tex coordinates are present
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        if (gTexCoordMasks[t] & layout) {
-            ++cnt;
-        }
-    }
-    return cnt;
-}
-
-} //unnamed namespace
-
-size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    size_t vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                        sizeof(GrGpuTextVertex) :
-                        sizeof(GrPoint);
-
-    size_t size = vecSize; // position
-    size += num_tex_coords(vertexLayout) * vecSize;
-    if (vertexLayout & kColor_VertexLayoutBit) {
-        size += sizeof(GrColor);
-    }
-    if (vertexLayout & kCoverage_VertexLayoutBit) {
-        size += sizeof(GrColor);
-    }
-    if (vertexLayout & kEdge_VertexLayoutBit) {
-        size += 4 * sizeof(SkScalar);
-    }
-    return size;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
-/**
- * Functions for computing offsets of various components from the layout
- * bitfield.
- *
- * Order of vertex components:
- * Position
- * Tex Coord 0
- * ...
- * Tex Coord GrDrawState::kMaxTexCoords-1
- * Color
- * Coverage
- */
+GrDrawTarget::DrawInfo& GrDrawTarget::DrawInfo::operator =(const DrawInfo& di) {
+    fPrimitiveType  = di.fPrimitiveType;
+    fStartVertex    = di.fStartVertex;
+    fStartIndex     = di.fStartIndex;
+    fVertexCount    = di.fVertexCount;
+    fIndexCount     = di.fIndexCount;
 
-int GrDrawTarget::VertexStageCoordOffset(int stageIdx, GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
+    fInstanceCount          = di.fInstanceCount;
+    fVerticesPerInstance    = di.fVerticesPerInstance;
+    fIndicesPerInstance     = di.fIndicesPerInstance;
 
-    if (!StageUsesTexCoords(vertexLayout, stageIdx)) {
-        return 0;
-    }
-    int tcIdx = VertexTexCoordsForStage(stageIdx, vertexLayout);
-    if (tcIdx >= 0) {
-
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-        int offset = vecSize; // position
-        // figure out how many tex coordinates are present and precede this one.
-        for (int t = 0; t < tcIdx; ++t) {
-            if (gTexCoordMasks[t] & vertexLayout) {
-                offset += vecSize;
-            }
-        }
-        return offset;
-    }
-
-    return -1;
-}
-
-int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    if (vertexLayout & kColor_VertexLayoutBit) {
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-        return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
-    }
-    return -1;
-}
-
-int GrDrawTarget::VertexCoverageOffset(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    if (vertexLayout & kCoverage_VertexLayoutBit) {
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-
-        int offset = vecSize * (num_tex_coords(vertexLayout) + 1);
-        if (vertexLayout & kColor_VertexLayoutBit) {
-            offset += sizeof(GrColor);
-        }
-        return offset;
-    }
-    return -1;
-}
-
-int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    // edge pts are after the pos, tex coords, and color
-    if (vertexLayout & kEdge_VertexLayoutBit) {
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-        int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
-        if (vertexLayout & kColor_VertexLayoutBit) {
-            offset += sizeof(GrColor);
-        }
-        if (vertexLayout & kCoverage_VertexLayoutBit) {
-            offset += sizeof(GrColor);
-        }
-        return offset;
-    }
-    return -1;
-}
-
-int GrDrawTarget::VertexSizeAndOffsetsByIdx(
-        GrVertexLayout vertexLayout,
-        int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords],
-        int* colorOffset,
-        int* coverageOffset,
-        int* edgeOffset) {
-    GrAssert(check_layout(vertexLayout));
-
-    int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                                    sizeof(GrGpuTextVertex) :
-                                                    sizeof(GrPoint);
-    int size = vecSize; // position
-
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        if (gTexCoordMasks[t] & vertexLayout) {
-            if (NULL != texCoordOffsetsByIdx) {
-                texCoordOffsetsByIdx[t] = size;
-            }
-            size += vecSize;
-        } else {
-            if (NULL != texCoordOffsetsByIdx) {
-                texCoordOffsetsByIdx[t] = -1;
-            }
-        }
-    }
-    if (kColor_VertexLayoutBit & vertexLayout) {
-        if (NULL != colorOffset) {
-            *colorOffset = size;
-        }
-        size += sizeof(GrColor);
+    if (NULL != di.fDevBounds) {
+        GrAssert(di.fDevBounds == &di.fDevBoundsStorage);
+        fDevBoundsStorage = di.fDevBoundsStorage;
+        fDevBounds = &fDevBoundsStorage;
     } else {
-        if (NULL != colorOffset) {
-            *colorOffset = -1;
-        }
+        fDevBounds = NULL;
     }
-    if (kCoverage_VertexLayoutBit & vertexLayout) {
-        if (NULL != coverageOffset) {
-            *coverageOffset = size;
-        }
-        size += sizeof(GrColor);
+    return *this;
+}
+
+#if GR_DEBUG
+bool GrDrawTarget::DrawInfo::isInstanced() const {
+    if (fInstanceCount > 0) {
+        GrAssert(0 == fIndexCount % fIndicesPerInstance);
+        GrAssert(0 == fVertexCount % fVerticesPerInstance);
+        GrAssert(fIndexCount / fIndicesPerInstance == fInstanceCount);
+        GrAssert(fVertexCount / fVerticesPerInstance == fInstanceCount);
+        // there is no way to specify a non-zero start index to drawIndexedInstances().
+        GrAssert(0 == fStartIndex);
+        return true;
     } else {
-        if (NULL != coverageOffset) {
-            *coverageOffset = -1;
-        }
+        GrAssert(!fVerticesPerInstance);
+        GrAssert(!fIndicesPerInstance);
+        return false;
     }
-    if (kEdge_VertexLayoutBit & vertexLayout) {
-        if (NULL != edgeOffset) {
-            *edgeOffset = size;
-        }
-        size += 4 * sizeof(SkScalar);
-    } else {
-        if (NULL != edgeOffset) {
-            *edgeOffset = -1;
-        }
-    }
-    return size;
+}
+#endif
+
+void GrDrawTarget::DrawInfo::adjustInstanceCount(int instanceOffset) {
+    GrAssert(this->isInstanced());
+    GrAssert(instanceOffset + fInstanceCount >= 0);
+    fInstanceCount += instanceOffset;
+    fVertexCount = fVerticesPerInstance * fInstanceCount;
+    fIndexCount = fIndicesPerInstance * fInstanceCount;
 }
 
-int GrDrawTarget::VertexSizeAndOffsetsByStage(
-        GrVertexLayout vertexLayout,
-        int texCoordOffsetsByStage[GrDrawState::kNumStages],
-        int* colorOffset,
-        int* coverageOffset,
-        int* edgeOffset) {
-    GrAssert(check_layout(vertexLayout));
-
-    int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords];
-    int size = VertexSizeAndOffsetsByIdx(vertexLayout,
-                                         (NULL == texCoordOffsetsByStage) ?
-                                               NULL :
-                                               texCoordOffsetsByIdx,
-                                         colorOffset,
-                                         coverageOffset,
-                                         edgeOffset);
-    if (NULL != texCoordOffsetsByStage) {
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            int tcIdx = VertexTexCoordsForStage(s, vertexLayout);
-            texCoordOffsetsByStage[s] =
-                tcIdx < 0 ? 0 : texCoordOffsetsByIdx[tcIdx];
-        }
-    }
-    return size;
+void GrDrawTarget::DrawInfo::adjustStartVertex(int vertexOffset) {
+    fStartVertex += vertexOffset;
+    GrAssert(fStartVertex >= 0);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-bool GrDrawTarget::VertexUsesTexCoordIdx(int coordIndex,
-                                         GrVertexLayout vertexLayout) {
-    GrAssert(coordIndex < GrDrawState::kMaxTexCoords);
-    GrAssert(check_layout(vertexLayout));
-    return !!(gTexCoordMasks[coordIndex] & vertexLayout);
-}
-
-int GrDrawTarget::VertexTexCoordsForStage(int stageIdx,
-                                          GrVertexLayout vertexLayout) {
-    GrAssert(stageIdx < GrDrawState::kNumStages);
-    GrAssert(check_layout(vertexLayout));
-    int bit = vertexLayout & gStageTexCoordMasks[stageIdx];
-    if (bit) {
-        // figure out which set of texture coordates is used
-        // bits are ordered T0S0, T0S1, T0S2, ..., T1S0, T1S1, ...
-        // and start at bit 0.
-        GR_STATIC_ASSERT(sizeof(GrVertexLayout) <= sizeof(uint32_t));
-        return (32 - SkCLZ(bit) - 1) / GrDrawState::kNumStages;
-    }
-    return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-void GrDrawTarget::VertexLayoutUnitTest() {
-    // Ensure that our globals mask arrays are correct
-    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
-    GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
-    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrAssert(stageTexCoordMasks[s] == gStageTexCoordMasks[s]);
-    }
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        GrAssert(texCoordMasks[t] == gTexCoordMasks[t]);
-    }
-
-    // not necessarily exhaustive
-    static bool run;
-    if (!run) {
-        run = true;
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-
-            GrVertexLayout stageMask = 0;
-            for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-                stageMask |= StageTexCoordVertexLayoutBit(s,t);
-            }
-            GrAssert(1 == GrDrawState::kMaxTexCoords ||
-                     !check_layout(stageMask));
-            GrAssert(gStageTexCoordMasks[s] == stageMask);
-            GrAssert(!check_layout(stageMask));
-        }
-        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-            GrVertexLayout tcMask = 0;
-            GrAssert(!VertexUsesTexCoordIdx(t, 0));
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                tcMask |= StageTexCoordVertexLayoutBit(s,t);
-                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
-                GrAssert(VertexUsesTexCoordIdx(t, tcMask));
-                GrAssert(2*sizeof(GrPoint) == VertexSize(tcMask));
-                GrAssert(t == VertexTexCoordsForStage(s, tcMask));
-                for (int s2 = s + 1; s2 < GrDrawState::kNumStages; ++s2) {
-                    GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
-
-                #if GR_DEBUG
-                    GrVertexLayout posAsTex = tcMask;
-                #endif
-                    GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
-                    GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
-                    GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
-                    GrAssert(-1 == VertexEdgeOffset(posAsTex));
-                }
-                GrAssert(-1 == VertexEdgeOffset(tcMask));
-                GrAssert(-1 == VertexColorOffset(tcMask));
-                GrAssert(-1 == VertexCoverageOffset(tcMask));
-            #if GR_DEBUG
-                GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
-            #endif
-                GrAssert(-1 == VertexCoverageOffset(withColor));
-                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
-            #if GR_DEBUG
-                GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
-            #endif
-                GrAssert(-1 == VertexColorOffset(withEdge));
-                GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
-                GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
-            #if GR_DEBUG
-                GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
-            #endif
-                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
-                GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
-            #if GR_DEBUG
-                GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit;
-            #endif
-                GrAssert(-1 == VertexColorOffset(withCoverage));
-                GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage));
-            #if GR_DEBUG
-                GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit |
-                                                      kColor_VertexLayoutBit;
-            #endif
-                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor));
-                GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor));
-            }
-            GrAssert(gTexCoordMasks[t] == tcMask);
-            GrAssert(check_layout(tcMask));
-
-            int stageOffsets[GrDrawState::kNumStages];
-            int colorOffset;
-            int edgeOffset;
-            int coverageOffset;
-            int size;
-            size = VertexSizeAndOffsetsByStage(tcMask,
-                                               stageOffsets, &colorOffset,
-                                               &coverageOffset, &edgeOffset);
-            GrAssert(2*sizeof(GrPoint) == size);
-            GrAssert(-1 == colorOffset);
-            GrAssert(-1 == coverageOffset);
-            GrAssert(-1 == edgeOffset);
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                GrAssert(sizeof(GrPoint) == stageOffsets[s]);
-                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
-            }
-        }
-    }
+void GrDrawTarget::DrawInfo::adjustStartIndex(int indexOffset) {
+    GrAssert(this->isIndexed());
+    fStartIndex += indexOffset;
+    GrAssert(fStartIndex >= 0);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -442,9 +83,6 @@
 #define DEBUG_INVAL_START_IDX -1
 
 GrDrawTarget::GrDrawTarget() : fClip(NULL) {
-#if GR_DEBUG
-    VertexLayoutUnitTest();
-#endif
     fDrawState = &fDefaultDrawState;
     // We assume that fDrawState always owns a ref to the object it points at.
     fDefaultDrawState.ref();
@@ -461,7 +99,7 @@
 
 GrDrawTarget::~GrDrawTarget() {
     GrAssert(1 == fGeoSrcStateStack.count());
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    SkDEBUGCODE(GeometrySrcState& geoSrc = fGeoSrcStateStack.back());
     GrAssert(kNone_GeometrySrcType == geoSrc.fIndexSrc);
     GrAssert(kNone_GeometrySrcType == geoSrc.fVertexSrc);
     fDrawState->unref();
@@ -508,7 +146,7 @@
         this->releasePreviousVertexSource();
         geoSrc.fVertexSrc = kNone_GeometrySrcType;
 
-        acquired = this->onReserveVertexSpace(vertexLayout,
+        acquired = this->onReserveVertexSpace(GrDrawState::VertexSize(vertexLayout),
                                               vertexCount,
                                               vertices);
     }
@@ -543,16 +181,12 @@
 
 }
 
-bool GrDrawTarget::StageUsesTexCoords(GrVertexLayout layout, int stageIdx) {
-    return SkToBool(layout & gStageTexCoordMasks[stageIdx]);
-}
-
 bool GrDrawTarget::reserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
                                               int vertexCount,
                                               int indexCount,
                                               void** vertices,
                                               void** indices) {
-    this->willReserveVertexAndIndexSpace(vertexLayout, vertexCount, indexCount);
+    this->willReserveVertexAndIndexSpace(GrDrawState::VertexSize(vertexLayout), vertexCount, indexCount);
     if (vertexCount) {
         if (!this->reserveVertexSpace(vertexLayout, vertexCount, vertices)) {
             if (indexCount) {
@@ -572,7 +206,7 @@
     return true;
 }
 
-bool GrDrawTarget::geometryHints(GrVertexLayout vertexLayout,
+bool GrDrawTarget::geometryHints(size_t vertexSize,
                                  int32_t* vertexCount,
                                  int32_t* indexCount) const {
     if (NULL != vertexCount) {
@@ -721,7 +355,7 @@
             maxValidVertex = geoSrc.fVertexCount;
             break;
         case kBuffer_GeometrySrcType:
-            maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() / VertexSize(geoSrc.fVertexLayout);
+            maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() / GrDrawState::VertexSize(geoSrc.fVertexLayout);
             break;
     }
     if (maxVertex > maxValidVertex) {
@@ -749,7 +383,7 @@
     GrAssert(NULL != drawState.getRenderTarget());
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
         if (drawState.isStageEnabled(s)) {
-            const GrEffect* effect = drawState.getStage(s).getEffect();
+            const GrEffectRef& effect = *drawState.getStage(s).getEffect();
             int numTextures = effect->numTextures();
             for (int t = 0; t < numTextures; ++t) {
                 GrTexture* texture = effect->texture(t);
@@ -764,23 +398,51 @@
     return true;
 }
 
-void GrDrawTarget::drawIndexed(GrPrimitiveType type, int startVertex,
-                               int startIndex, int vertexCount,
-                               int indexCount) {
-    if (indexCount > 0 &&
-        this->checkDraw(type, startVertex, startIndex,
-                        vertexCount, indexCount)) {
-        this->onDrawIndexed(type, startVertex, startIndex,
-                            vertexCount, indexCount);
+void GrDrawTarget::drawIndexed(GrPrimitiveType type,
+                               int startVertex,
+                               int startIndex,
+                               int vertexCount,
+                               int indexCount,
+                               const SkRect* devBounds) {
+    if (indexCount > 0 && this->checkDraw(type, startVertex, startIndex, vertexCount, indexCount)) {
+        DrawInfo info;
+        info.fPrimitiveType = type;
+        info.fStartVertex   = startVertex;
+        info.fStartIndex    = startIndex;
+        info.fVertexCount   = vertexCount;
+        info.fIndexCount    = indexCount;
+
+        info.fInstanceCount         = 0;
+        info.fVerticesPerInstance   = 0;
+        info.fIndicesPerInstance    = 0;
+
+        if (NULL != devBounds) {
+            info.setDevBounds(*devBounds);
+        }
+        this->onDraw(info);
     }
 }
 
 void GrDrawTarget::drawNonIndexed(GrPrimitiveType type,
                                   int startVertex,
-                                  int vertexCount) {
-    if (vertexCount > 0 &&
-        this->checkDraw(type, startVertex, -1, vertexCount, -1)) {
-        this->onDrawNonIndexed(type, startVertex, vertexCount);
+                                  int vertexCount,
+                                  const SkRect* devBounds) {
+    if (vertexCount > 0 && this->checkDraw(type, startVertex, -1, vertexCount, -1)) {
+        DrawInfo info;
+        info.fPrimitiveType = type;
+        info.fStartVertex   = startVertex;
+        info.fStartIndex    = 0;
+        info.fVertexCount   = vertexCount;
+        info.fIndexCount    = 0;
+
+        info.fInstanceCount         = 0;
+        info.fVerticesPerInstance   = 0;
+        info.fIndicesPerInstance    = 0;
+
+        if (NULL != devBounds) {
+            info.setDevBounds(*devBounds);
+        }
+        this->onDraw(info);
     }
 }
 
@@ -816,45 +478,6 @@
            this->getDrawState().isCoverageDrawing();
 }
 
-bool GrDrawTarget::srcAlphaWillBeOne(GrVertexLayout layout) const {
-    const GrDrawState& drawState = this->getDrawState();
-
-    // Check if per-vertex or constant color may have partial alpha
-    if ((layout & kColor_VertexLayoutBit) ||
-        0xff != GrColorUnpackA(drawState.getColor())) {
-        return false;
-    }
-    // Check if color filter could introduce an alpha
-    // (TODO: Consider being more aggressive with regards to detecting 0xff
-    // final alpha from color filter).
-    if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
-        return false;
-    }
-    int stageCnt;
-    // Check whether coverage is treated as color
-    if (drawState.isCoverageDrawing()) {
-        if (0xff != GrColorUnpackA(drawState.getCoverage())) {
-            return false;
-        }
-        stageCnt = GrDrawState::kNumStages;
-    } else {
-        stageCnt = drawState.getFirstCoverageStage();
-    }
-    // Check if a color stage could create a partial alpha
-    for (int s = 0; s < stageCnt; ++s) {
-        const GrEffect* effect = drawState.getStage(s).getEffect();
-        if (NULL != effect) {
-            // FIXME: The param indicates whether the texture is opaque or not. However, the effect
-            // already controls its textures. It really needs to know whether the incoming color
-            // (from a uni, per-vertex colors, or previous stage) is opaque or not.
-            if (!effect->isOpaque(true)) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
 namespace {
 GrVertexLayout default_blend_opts_vertex_layout() {
     GrVertexLayout layout = 0;
@@ -892,14 +515,14 @@
         *dstCoeff = kOne_GrBlendCoeff;
     }
 
-    bool srcAIsOne = this->srcAlphaWillBeOne(layout);
+    bool srcAIsOne = drawState.srcAlphaWillBeOne(layout);
     bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff ||
                          (kSA_GrBlendCoeff == *dstCoeff && srcAIsOne);
     bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff ||
                          (kISA_GrBlendCoeff == *dstCoeff && srcAIsOne);
 
     bool covIsZero = !drawState.isCoverageDrawing() &&
-                     !(layout & kCoverage_VertexLayoutBit) &&
+                     !(layout & GrDrawState::kCoverage_VertexLayoutBit) &&
                      0 == drawState.getCoverage();
     // When coeffs are (0,1) there is no reason to draw at all, unless
     // stenciling is enabled. Having color writes disabled is effectively
@@ -917,12 +540,12 @@
     // edge aa or coverage stage
     bool hasCoverage = forceCoverage ||
                        0xffffffff != drawState.getCoverage() ||
-                       (layout & kCoverage_VertexLayoutBit) ||
-                       (layout & kEdge_VertexLayoutBit);
+                       (layout & GrDrawState::kCoverage_VertexLayoutBit) ||
+                       (layout & GrDrawState::kEdge_VertexLayoutBit);
     for (int s = drawState.getFirstCoverageStage();
          !hasCoverage && s < GrDrawState::kNumStages;
          ++s) {
-        if (this->isStageEnabled(s)) {
+        if (drawState.isStageEnabled(s)) {
             hasCoverage = true;
         }
     }
@@ -1003,27 +626,43 @@
 void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type,
                                         int instanceCount,
                                         int verticesPerInstance,
-                                        int indicesPerInstance) {
+                                        int indicesPerInstance,
+                                        const SkRect* devBounds) {
     if (!verticesPerInstance || !indicesPerInstance) {
         return;
     }
 
-    int instancesPerDraw = this->indexCountInCurrentSource() /
-                           indicesPerInstance;
-    if (!instancesPerDraw) {
+    int maxInstancesPerDraw = this->indexCountInCurrentSource() / indicesPerInstance;
+    if (!maxInstancesPerDraw) {
         return;
     }
 
-    instancesPerDraw = GrMin(instanceCount, instancesPerDraw);
-    int startVertex = 0;
+    DrawInfo info;
+    info.fPrimitiveType = type;
+    info.fStartIndex = 0;
+    info.fStartVertex = 0;
+    info.fIndicesPerInstance = indicesPerInstance;
+    info.fVerticesPerInstance = verticesPerInstance;
+
+    // Set the same bounds for all the draws.
+    if (NULL != devBounds) {
+        info.setDevBounds(*devBounds);
+    }
+
     while (instanceCount) {
-        this->drawIndexed(type,
-                          startVertex,
-                          0,
-                          verticesPerInstance * instancesPerDraw,
-                          indicesPerInstance * instancesPerDraw);
-        startVertex += verticesPerInstance;
-        instanceCount -= instancesPerDraw;
+        info.fInstanceCount = GrMin(instanceCount, maxInstancesPerDraw);
+        info.fVertexCount = info.fInstanceCount * verticesPerInstance;
+        info.fIndexCount = info.fInstanceCount * indicesPerInstance;
+
+        if (this->checkDraw(type,
+                            info.fStartVertex,
+                            info.fStartIndex,
+                            info.fVertexCount,
+                            info.fIndexCount)) {
+            this->onDraw(info);
+        }
+        info.fStartVertex += info.fVertexCount;
+        instanceCount -= info.fInstanceCount;
     }
 }
 
@@ -1033,7 +672,24 @@
                             const SkMatrix* matrix,
                             const GrRect* srcRects[],
                             const SkMatrix* srcMatrices[]) {
-    GrVertexLayout layout = GetRectVertexLayout(srcRects);
+    GrVertexLayout layout = 0;
+    uint32_t explicitCoordMask = 0;
+
+    if (NULL != srcRects) {
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            int numTC = 0;
+            if (NULL != srcRects[s]) {
+                layout |= GrDrawState::StageTexCoordVertexLayoutBit(s, numTC);
+                explicitCoordMask |= (1 << s);
+                ++numTC;
+            }
+        }
+    }
+
+    GrDrawState::AutoViewMatrixRestore avmr;
+    if (NULL != matrix) {
+        avmr.set(this->drawState(), *matrix, explicitCoordMask);
+    }
 
     AutoReleaseGeometry geo(this, layout, 4, 0);
     if (!geo.succeeded()) {
@@ -1041,69 +697,14 @@
         return;
     }
 
-    SetRectVertices(rect, matrix, srcRects,
-                    srcMatrices, SK_ColorBLACK, layout, geo.vertices());
-
-    drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
-}
-
-GrVertexLayout GrDrawTarget::GetRectVertexLayout(const GrRect* srcRects[]) {
-    if (NULL == srcRects) {
-        return 0;
-    }
-
-    GrVertexLayout layout = 0;
-    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-        int numTC = 0;
-        if (NULL != srcRects[i]) {
-            layout |= StageTexCoordVertexLayoutBit(i, numTC);
-            ++numTC;
-        }
-    }
-    return layout;
-}
-
-// This method fills int the four vertices for drawing 'rect'.
-//      matrix - is applied to each vertex
-//      srcRects - provide the uvs for each vertex
-//      srcMatrices - are applied to the corresponding 'srcRect'
-//      color - vertex color (replicated in each vertex)
-//      layout - specifies which uvs and/or color are present
-//      vertices - storage for the resulting vertices
-// Note: the color parameter will only be used when kColor_VertexLayoutBit
-// is present in 'layout'
-void GrDrawTarget::SetRectVertices(const GrRect& rect,
-                                   const SkMatrix* matrix,
-                                   const GrRect* srcRects[],
-                                   const SkMatrix* srcMatrices[],
-                                   GrColor color,
-                                   GrVertexLayout layout,
-                                   void* vertices) {
-#if GR_DEBUG
-    // check that the layout and srcRects agree
-    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-        if (VertexTexCoordsForStage(i, layout) >= 0) {
-            GR_DEBUGASSERT(NULL != srcRects && NULL != srcRects[i]);
-        } else {
-            GR_DEBUGASSERT(NULL == srcRects || NULL == srcRects[i]);
-        }
-    }
-#endif
-
-    int stageOffsets[GrDrawState::kNumStages], colorOffset;
-    int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets,
-                                            &colorOffset, NULL, NULL);
-
-    GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
-                                            rect.fRight, rect.fBottom,
-                                            vsize);
-    if (NULL != matrix) {
-        matrix->mapPointsWithStride(GrTCast<GrPoint*>(vertices), vsize, 4);
-    }
+    int stageOffsets[GrDrawState::kNumStages];
+    int vsize = GrDrawState::VertexSizeAndOffsetsByStage(layout, stageOffsets,  NULL, NULL, NULL);
+    geo.positions()->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize);
 
     for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-        if (stageOffsets[i] > 0) {
-            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(vertices) +
+        if (explicitCoordMask & (1 << i)) {
+            GrAssert(0 != stageOffsets[i]);
+            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(geo.vertices()) +
                                                 stageOffsets[i]);
             coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop,
                                srcRects[i]->fRight, srcRects[i]->fBottom,
@@ -1111,18 +712,15 @@
             if (NULL != srcMatrices && NULL != srcMatrices[i]) {
                 srcMatrices[i]->mapPointsWithStride(coords, vsize, 4);
             }
+        } else {
+            GrAssert(0 == stageOffsets[i]);
         }
     }
 
-    if (layout & kColor_VertexLayoutBit) {
+    this->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
+}
 
-        GrColor* vertCol = GrTCast<GrColor*>(GrTCast<intptr_t>(vertices) + colorOffset);
-
-        for (int i = 0; i < 4; ++i) {
-            *vertCol = color;
-            vertCol = (GrColor*) ((intptr_t) vertCol + vsize);
-        }
-    }
+void GrDrawTarget::clipWillBeSet(const GrClipData* clipData) {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index a54dd0f..c1444b4 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -16,7 +16,6 @@
 #include "GrIndexBuffer.h"
 #include "SkMatrix.h"
 #include "GrRefCnt.h"
-#include "GrTemplates.h"
 
 #include "SkClipStack.h"
 #include "SkPath.h"
@@ -27,7 +26,6 @@
 class GrClipData;
 class GrPath;
 class GrVertexBuffer;
-
 class SkStrokeRec;
 
 class GrDrawTarget : public GrRefCnt {
@@ -51,6 +49,8 @@
         int fMaxTextureSize;
     };
 
+    class DrawInfo;
+
 public:
     SK_DECLARE_INST_COUNT(GrDrawTarget)
 
@@ -170,76 +170,6 @@
     bool willUseHWAALines() const;
 
     /**
-     * The format of vertices is represented as a bitfield of flags.
-     * Flags that indicate the layout of vertex data. Vertices always contain
-     * positions and may also contain up to GrDrawState::kMaxTexCoords sets
-     * of 2D texture coordinates, per-vertex colors, and per-vertex coverage.
-     * Each stage can
-     * use any of the texture coordinates as its input texture coordinates or it
-     * may use the positions as texture coordinates.
-     *
-     * If no texture coordinates are specified for a stage then the stage is
-     * disabled.
-     *
-     * Only one type of texture coord can be specified per stage. For
-     * example StageTexCoordVertexLayoutBit(0, 2) and
-     * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified.
-     *
-     * The order in memory is always (position, texture coord 0, ..., color,
-     * coverage) with any unused fields omitted. Note that this means that if
-     * only texture coordinates 1 is referenced then there is no texture
-     * coordinates 0 and the order would be (position, texture coordinate 1
-     * [, color][, coverage]).
-     */
-
-    /**
-     * Generates a bit indicating that a texture stage uses texture coordinates
-     *
-     * @param stageIdx    the stage that will use texture coordinates.
-     * @param texCoordIdx the index of the texture coordinates to use
-     *
-     * @return the bit to add to a GrVertexLayout bitfield.
-     */
-    static int StageTexCoordVertexLayoutBit(int stageIdx, int texCoordIdx) {
-        GrAssert(stageIdx < GrDrawState::kNumStages);
-        GrAssert(texCoordIdx < GrDrawState::kMaxTexCoords);
-        return 1 << (stageIdx + (texCoordIdx * GrDrawState::kNumStages));
-    }
-
-    static bool StageUsesTexCoords(GrVertexLayout layout, int stageIdx);
-
-private:
-    // non-stage bits start at this index.
-    static const int STAGE_BIT_CNT = GrDrawState::kNumStages *
-                                     GrDrawState::kMaxTexCoords;
-public:
-
-    /**
-     * Additional Bits that can be specified in GrVertexLayout.
-     */
-    enum VertexLayoutBits {
-        /* vertices have colors (GrColor) */
-        kColor_VertexLayoutBit              = 1 << (STAGE_BIT_CNT + 0),
-        /* vertices have coverage (GrColor)
-         */
-        kCoverage_VertexLayoutBit           = 1 << (STAGE_BIT_CNT + 1),
-        /* Use text vertices. (Pos and tex coords may be a different type for
-         * text [GrGpuTextVertex vs GrPoint].)
-         */
-        kTextFormat_VertexLayoutBit         = 1 << (STAGE_BIT_CNT + 2),
-
-        /* Each vertex specificies an edge. Distance to the edge is used to
-         * compute a coverage. See GrDrawState::setVertexEdgeType().
-         */
-        kEdge_VertexLayoutBit               = 1 << (STAGE_BIT_CNT + 3),
-        // for below assert
-        kDummyVertexLayoutBit,
-        kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
-    };
-    // make sure we haven't exceeded the number of bits in GrVertexLayout.
-    GR_STATIC_ASSERT(kHighVertexLayoutBit < ((uint64_t)1 << 8*sizeof(GrVertexLayout)));
-
-    /**
      * There are three types of "sources" of geometry (vertices and indices) for
      * draw calls made on the target. When performing an indexed draw, the
      * indices and vertices can use different source types. Once a source is
@@ -325,7 +255,7 @@
      * Also may hint whether the draw target should be flushed first. This is
      * useful for deferred targets.
      *
-     * @param vertexLayout layout of vertices caller would like to reserve
+     * @param vertexSize   size of vertices caller would like to reserve
      * @param vertexCount  in: hint about how many vertices the caller would
      *                     like to allocate.
      *                     out: a hint about the number of vertices that can be
@@ -339,7 +269,7 @@
      *
      * @return  true if target should be flushed based on the input values.
      */
-    virtual bool geometryHints(GrVertexLayout vertexLayout,
+    virtual bool geometryHints(size_t vertexSize,
                                int* vertexCount,
                                int* indexCount) const;
 
@@ -432,12 +362,15 @@
      * @param indexCount   the number of index elements to read. The index count
      *                     is effectively trimmed to the last completely
      *                     specified primitive.
+     * @param devBounds    optional bounds hint. This is a promise from the caller,
+     *                     not a request for clipping.
      */
     void drawIndexed(GrPrimitiveType type,
                      int startVertex,
                      int startIndex,
                      int vertexCount,
-                     int indexCount);
+                     int indexCount,
+                     const SkRect* devBounds = NULL);
 
     /**
      * Draws non-indexed geometry using the current state and current vertex
@@ -447,10 +380,13 @@
      * @param startVertex  the vertex in the vertex array/buffer corresponding
      *                     to index 0
      * @param vertexCount  one greater than the max index.
+     * @param devBounds    optional bounds hint. This is a promise from the caller,
+     *                     not a request for clipping.
      */
     void drawNonIndexed(GrPrimitiveType type,
                         int startVertex,
-                        int vertexCount);
+                        int vertexCount,
+                        const SkRect* devBounds = NULL);
 
     /**
      * Draws path into the stencil buffer. The fill must be either even/odd or
@@ -464,9 +400,13 @@
      * and vertex sources. After returning, the vertex and index sources may
      * have changed. They should be reestablished before the next drawIndexed
      * or drawNonIndexed. This cannot be called between reserving and releasing
-     * geometry. The GrDrawTarget subclass may be able to perform additional
-     * optimizations if drawRect is used rather than drawIndexed or
-     * drawNonIndexed.
+     * geometry.
+     *
+     * A subclass may override this to perform more optimal rect rendering. Its
+     * draws should be funneled through one of the public GrDrawTarget draw methods
+     * (e.g. drawNonIndexed, drawIndexedInstances, ...). The base class draws a two
+     * triangle fan using drawNonIndexed from reserved vertex space.
+     *
      * @param rect      the rect to draw
      * @param matrix    optional matrix applied to rect (before viewMatrix)
      * @param srcRects  specifies rects for stages enabled by stageEnableMask.
@@ -496,7 +436,6 @@
         this->drawRect(rect, matrix, NULL, NULL);
     }
 
-
     /**
      * This call is used to draw multiple instances of some geometry with a
      * given number of vertices (V) and indices (I) per-instance. The indices in
@@ -511,7 +450,7 @@
      * source. The size of the index buffer limits the number of instances that
      * can be drawn by the GPU in a single draw. However, the caller may specify
      * any (positive) number for instanceCount and if necessary multiple GPU
-     * draws will be issued. Morever, when drawIndexedInstances is called
+     * draws will be issued. Moreover, when drawIndexedInstances is called
      * multiple times it may be possible for GrDrawTarget to group them into a
      * single GPU draw.
      *
@@ -524,11 +463,14 @@
      *                              in the above description).
      * @param indicesPerInstance    The number of indices in each instance (I
      *                              in the above description).
+     * @param devBounds    optional bounds hint. This is a promise from the caller,
+     *                     not a request for clipping.
      */
-    virtual void drawIndexedInstances(GrPrimitiveType type,
-                                      int instanceCount,
-                                      int verticesPerInstance,
-                                      int indicesPerInstance);
+    void drawIndexedInstances(GrPrimitiveType type,
+                              int instanceCount,
+                              int verticesPerInstance,
+                              int indicesPerInstance,
+                              const SkRect* devBounds = NULL);
 
     /**
      * Clear the current render target if one isn't passed in. Ignores the
@@ -545,6 +487,11 @@
      */
     virtual void purgeResources() {};
 
+    /**
+     * For subclass internal use to invoke a call to onDraw(). See DrawInfo below.
+     */
+    void executeDraw(const DrawInfo& info) { this->onDraw(info); }
+
     ////////////////////////////////////////////////////////////////////////////
 
     /**
@@ -671,191 +618,6 @@
         GrDrawTarget* fTarget;
     };
 
-    ////////////////////////////////////////////////////////////////////////////
-    // Helpers for picking apart vertex layouts
-
-    /**
-     * Helper function to compute the size of a vertex from a vertex layout
-     * @return size of a single vertex.
-     */
-    static size_t VertexSize(GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function for determining the index of texture coordinates that
-     * is input for a texture stage. Note that a stage may instead use positions
-     * as texture coordinates, in which case the result of the function is
-     * indistinguishable from the case when the stage is disabled.
-     *
-     * @param stageIdx      the stage to query
-     * @param vertexLayout  layout to query
-     *
-     * @return the texture coordinate index or -1 if the stage doesn't use
-     *         separate (non-position) texture coordinates.
-     */
-    static int VertexTexCoordsForStage(int stageIdx, GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the offset of texture coordinates in a vertex
-     * @return offset of texture coordinates in vertex layout or -1 if the
-     *         layout has no texture coordinates. Will be 0 if positions are
-     *         used as texture coordinates for the stage.
-     */
-    static int VertexStageCoordOffset(int stageIdx, GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the offset of the color in a vertex
-     * @return offset of color in vertex layout or -1 if the
-     *         layout has no color.
-     */
-    static int VertexColorOffset(GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the offset of the coverage in a vertex
-     * @return offset of coverage in vertex layout or -1 if the
-     *         layout has no coverage.
-     */
-    static int VertexCoverageOffset(GrVertexLayout vertexLayout);
-
-     /**
-      * Helper function to compute the offset of the edge pts in a vertex
-      * @return offset of edge in vertex layout or -1 if the
-      *         layout has no edge.
-      */
-     static int VertexEdgeOffset(GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to determine if vertex layout contains explicit texture
-     * coordinates of some index.
-     *
-     * @param coordIndex    the tex coord index to query
-     * @param vertexLayout  layout to query
-     *
-     * @return true if vertex specifies texture coordinates for the index,
-     *              false otherwise.
-     */
-    static bool VertexUsesTexCoordIdx(int coordIndex,
-                                      GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the size of each vertex and the offsets of
-     * texture coordinates and color. Determines tex coord offsets by tex coord
-     * index rather than by stage. (Each stage can be mapped to any t.c. index
-     * by StageTexCoordVertexLayoutBit.)
-     *
-     * @param vertexLayout          the layout to query
-     * @param texCoordOffsetsByIdx  after return it is the offset of each
-     *                              tex coord index in the vertex or -1 if
-     *                              index isn't used. (optional)
-     * @param colorOffset           after return it is the offset of the
-     *                              color field in each vertex, or -1 if
-     *                              there aren't per-vertex colors. (optional)
-     * @param coverageOffset        after return it is the offset of the
-     *                              coverage field in each vertex, or -1 if
-     *                              there aren't per-vertex coeverages.
-     *                              (optional)
-     * @param edgeOffset            after return it is the offset of the
-     *                              edge eq field in each vertex, or -1 if
-     *                              there aren't per-vertex edge equations.
-     *                              (optional)
-     * @return size of a single vertex
-     */
-    static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
-                   int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords],
-                   int *colorOffset,
-                   int *coverageOffset,
-                   int* edgeOffset);
-
-    /**
-     * Helper function to compute the size of each vertex and the offsets of
-     * texture coordinates and color. Determines tex coord offsets by stage
-     * rather than by index. (Each stage can be mapped to any t.c. index
-     * by StageTexCoordVertexLayoutBit.) If a stage uses positions for
-     * tex coords then that stage's offset will be 0 (positions are always at 0).
-     *
-     * @param vertexLayout              the layout to query
-     * @param texCoordOffsetsByStage    after return it is the offset of each
-     *                                  tex coord index in the vertex or -1 if
-     *                                  index isn't used. (optional)
-     * @param colorOffset               after return it is the offset of the
-     *                                  color field in each vertex, or -1 if
-     *                                  there aren't per-vertex colors.
-     *                                  (optional)
-     * @param coverageOffset            after return it is the offset of the
-     *                                  coverage field in each vertex, or -1 if
-     *                                  there aren't per-vertex coeverages.
-     *                                  (optional)
-     * @param edgeOffset                after return it is the offset of the
-     *                                  edge eq field in each vertex, or -1 if
-     *                                  there aren't per-vertex edge equations.
-     *                                  (optional)
-     * @return size of a single vertex
-     */
-    static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
-                   int texCoordOffsetsByStage[GrDrawState::kNumStages],
-                   int* colorOffset,
-                   int* coverageOffset,
-                   int* edgeOffset);
-
-    /**
-     * Accessing positions, texture coords, or colors, of a vertex within an
-     * array is a hassle involving casts and simple math. These helpers exist
-     * to keep GrDrawTarget clients' code a bit nicer looking.
-     */
-
-    /**
-     * Gets a pointer to a GrPoint of a vertex's position or texture
-     * coordinate.
-     * @param vertices      the vetex array
-     * @param vertexIndex   the index of the vertex in the array
-     * @param vertexSize    the size of each vertex in the array
-     * @param offset        the offset in bytes of the vertex component.
-     *                      Defaults to zero (corresponding to vertex position)
-     * @return pointer to the vertex component as a GrPoint
-     */
-    static GrPoint* GetVertexPoint(void* vertices,
-                                   int vertexIndex,
-                                   int vertexSize,
-                                   int offset = 0) {
-        intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<GrPoint*>(start + offset +
-                                 vertexIndex * vertexSize);
-    }
-    static const GrPoint* GetVertexPoint(const void* vertices,
-                                         int vertexIndex,
-                                         int vertexSize,
-                                         int offset = 0) {
-        intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<const GrPoint*>(start + offset +
-                                       vertexIndex * vertexSize);
-    }
-
-    /**
-     * Gets a pointer to a GrColor inside a vertex within a vertex array.
-     * @param vertices      the vetex array
-     * @param vertexIndex   the index of the vertex in the array
-     * @param vertexSize    the size of each vertex in the array
-     * @param offset        the offset in bytes of the vertex color
-     * @return pointer to the vertex component as a GrColor
-     */
-    static GrColor* GetVertexColor(void* vertices,
-                                   int vertexIndex,
-                                   int vertexSize,
-                                   int offset) {
-        intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<GrColor*>(start + offset +
-                                 vertexIndex * vertexSize);
-    }
-    static const GrColor* GetVertexColor(const void* vertices,
-                                         int vertexIndex,
-                                         int vertexSize,
-                                         int offset) {
-        const intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<const GrColor*>(start + offset +
-                                       vertexIndex * vertexSize);
-    }
-
-    static void VertexLayoutUnitTest();
-
 protected:
 
     /**
@@ -906,9 +668,6 @@
                                GrBlendCoeff* srcCoeff = NULL,
                                GrBlendCoeff* dstCoeff = NULL) const;
 
-    // determine if src alpha is guaranteed to be one for all src pixels
-    bool srcAlphaWillBeOne(GrVertexLayout vertexLayout) const;
-
     enum GeometrySrcType {
         kNone_GeometrySrcType,     //<! src has not been specified
         kReserved_GeometrySrcType, //<! src was set using reserve*Space
@@ -952,93 +711,110 @@
         }
     }
 
-    bool isStageEnabled(int stageIdx) const {
-        return this->getDrawState().isStageEnabled(stageIdx);
-    }
-
-    // A sublcass can optionally overload this function to be notified before
-    // vertex and index space is reserved.
-    virtual void willReserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
-                                                int vertexCount,
-                                                int indexCount) {}
-
-
-    // implemented by subclass to allocate space for reserved geom
-    virtual bool onReserveVertexSpace(GrVertexLayout vertexLayout,
-                                      int vertexCount,
-                                      void** vertices) = 0;
-    virtual bool onReserveIndexSpace(int indexCount, void** indices) = 0;
-    // implemented by subclass to handle release of reserved geom space
-    virtual void releaseReservedVertexSpace() = 0;
-    virtual void releaseReservedIndexSpace() = 0;
-    // subclass must consume array contents when set
-    virtual void onSetVertexSourceToArray(const void* vertexArray,
-                                          int vertexCount) = 0;
-    virtual void onSetIndexSourceToArray(const void* indexArray,
-                                         int indexCount) = 0;
-    // subclass is notified that geom source will be set away from an array
-    virtual void releaseVertexArray() = 0;
-    virtual void releaseIndexArray() = 0;
-    // subclass overrides to be notified just before geo src state
-    // is pushed/popped.
-    virtual void geometrySourceWillPush() = 0;
-    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) = 0;
-    // subclass called to perform drawing
-    virtual void onDrawIndexed(GrPrimitiveType type,
-                               int startVertex,
-                               int startIndex,
-                               int vertexCount,
-                               int indexCount) = 0;
-    virtual void onDrawNonIndexed(GrPrimitiveType type,
-                                  int startVertex,
-                                  int vertexCount) = 0;
-    virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill) = 0;
-
-    // subclass overrides to be notified when clip is set. Must call
-    // INHERITED::clipwillBeSet
-    virtual void clipWillBeSet(const GrClipData* clipData) {}
-
-    // Helpers for drawRect, protected so subclasses that override drawRect
-    // can use them.
-    static GrVertexLayout GetRectVertexLayout(const GrRect* srcRects[]);
-
-    static void SetRectVertices(const GrRect& rect,
-                                const SkMatrix* matrix,
-                                const GrRect* srcRects[],
-                                const SkMatrix* srcMatrices[],
-                                GrColor color,
-                                GrVertexLayout layout,
-                                void* vertices);
-
-    // accessors for derived classes
-    const GeometrySrcState& getGeomSrc() const {
-        return fGeoSrcStateStack.back();
-    }
-    // it is prefereable to call this rather than getGeomSrc()->fVertexLayout
-    // because of the assert.
-    GrVertexLayout getVertexLayout() const {
-        // the vertex layout is only valid if a vertex source has been
-        // specified.
-        GrAssert(this->getGeomSrc().fVertexSrc != kNone_GeometrySrcType);
-        return this->getGeomSrc().fVertexLayout;
-    }
-
     // allows derived class to set the caps
     CapsInternals* capsInternals() { return &fCaps.fInternals; }
 
-    const GrClipData* fClip;
-
-    GrDrawState* fDrawState;
-    GrDrawState fDefaultDrawState;
-
-    Caps fCaps;
+    // A subclass may override this function if it wishes to be notified when the clip is changed.
+    // The override should call INHERITED::clipWillBeSet().
+    virtual void clipWillBeSet(const GrClipData* clipData);
 
     // subclasses must call this in their destructors to ensure all vertex
     // and index sources have been released (including those held by
     // pushGeometrySource())
     void releaseGeometry();
 
+    // accessors for derived classes
+    const GeometrySrcState& getGeomSrc() const { return fGeoSrcStateStack.back(); }
+    // it is preferable to call this rather than getGeomSrc()->fVertexLayout because of the assert.
+    GrVertexLayout getVertexLayout() const {
+        // the vertex layout is only valid if a vertex source has been specified.
+        GrAssert(this->getGeomSrc().fVertexSrc != kNone_GeometrySrcType);
+        return this->getGeomSrc().fVertexLayout;
+    }
+
+    Caps fCaps;
+
+    /**
+     * Used to communicate draws to subclass's onDraw function.
+     */
+    class DrawInfo {
+    public:
+        DrawInfo(const DrawInfo& di) { (*this) = di; }
+        DrawInfo& operator =(const DrawInfo& di);
+
+        GrPrimitiveType primitiveType() const { return fPrimitiveType; }
+        int startVertex() const { return fStartVertex; }
+        int startIndex() const { return fStartIndex; }
+        int vertexCount() const { return fVertexCount; }
+        int indexCount() const { return fIndexCount; }
+        int verticesPerInstance() const { return fVerticesPerInstance; }
+        int indicesPerInstance() const { return fIndicesPerInstance; }
+        int instanceCount() const { return fInstanceCount; }
+
+        bool isIndexed() const { return fIndexCount > 0; }
+#if GR_DEBUG
+        bool isInstanced() const; // this version is longer because of asserts
+#else
+        bool isInstanced() const { return fInstanceCount > 0; }
+#endif
+
+        // adds or remove instances
+        void adjustInstanceCount(int instanceOffset);
+        // shifts the start vertex
+        void adjustStartVertex(int vertexOffset);
+        // shifts the start index
+        void adjustStartIndex(int indexOffset);
+
+        void setDevBounds(const SkRect& bounds) {
+            fDevBoundsStorage = bounds;
+            fDevBounds = &fDevBoundsStorage;
+        }
+        const SkRect* getDevBounds() const { return fDevBounds; }
+
+    private:
+        DrawInfo() { fDevBounds = NULL; }
+
+        friend class GrDrawTarget;
+
+        GrPrimitiveType fPrimitiveType;
+
+        int             fStartVertex;
+        int             fStartIndex;
+        int             fVertexCount;
+        int             fIndexCount;
+
+        int             fInstanceCount;
+        int             fVerticesPerInstance;
+        int             fIndicesPerInstance;
+
+        SkRect          fDevBoundsStorage;
+        SkRect*         fDevBounds;
+    };
+
 private:
+    // A subclass can optionally overload this function to be notified before
+    // vertex and index space is reserved.
+    virtual void willReserveVertexAndIndexSpace(size_t vertexSize, int vertexCount, int indexCount) {}
+
+    // implemented by subclass to allocate space for reserved geom
+    virtual bool onReserveVertexSpace(size_t vertexSize, int vertexCount, void** vertices) = 0;
+    virtual bool onReserveIndexSpace(int indexCount, void** indices) = 0;
+    // implemented by subclass to handle release of reserved geom space
+    virtual void releaseReservedVertexSpace() = 0;
+    virtual void releaseReservedIndexSpace() = 0;
+    // subclass must consume array contents when set
+    virtual void onSetVertexSourceToArray(const void* vertexArray, int vertexCount) = 0;
+    virtual void onSetIndexSourceToArray(const void* indexArray, int indexCount) = 0;
+    // subclass is notified that geom source will be set away from an array
+    virtual void releaseVertexArray() = 0;
+    virtual void releaseIndexArray() = 0;
+    // subclass overrides to be notified just before geo src state is pushed/popped.
+    virtual void geometrySourceWillPush() = 0;
+    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) = 0;
+    // subclass called to perform drawing
+    virtual void onDraw(const DrawInfo&) = 0;
+    virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill) = 0;
+
     // helpers for reserving vertex and index space.
     bool reserveVertexSpace(GrVertexLayout vertexLayout,
                             int vertexCount,
@@ -1057,8 +833,10 @@
     enum {
         kPreallocGeoSrcStateStackCnt = 4,
     };
-    SkSTArray<kPreallocGeoSrcStateStackCnt,
-              GeometrySrcState, true>           fGeoSrcStateStack;
+    SkSTArray<kPreallocGeoSrcStateStackCnt, GeometrySrcState, true> fGeoSrcStateStack;
+    const GrClipData*                                               fClip;
+    GrDrawState*                                                    fDrawState;
+    GrDrawState                                                     fDefaultDrawState;
 
     typedef GrRefCnt INHERITED;
 };
diff --git a/src/gpu/GrEffect.cpp b/src/gpu/GrEffect.cpp
index d470c9c..40a519a 100644
--- a/src/gpu/GrEffect.cpp
+++ b/src/gpu/GrEffect.cpp
@@ -58,42 +58,39 @@
 
 int32_t GrBackendEffectFactory::fCurrEffectClassID = GrBackendEffectFactory::kIllegalEffectClassID;
 
-GrEffect::GrEffect(int numTextures)
-    : fNumTextures(numTextures) {
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_INST_COUNT(GrEffectRef)
+
+GrEffectRef::~GrEffectRef() {
+    GrAssert(1 == this->getRefCnt());
+    fEffect->EffectRefDestroyed();
+    fEffect->unref();
 }
 
+void* GrEffectRef::operator new(size_t size) {
+    return GrEffect_Globals::GetTLS()->allocate(size);
+}
+
+void GrEffectRef::operator delete(void* target) {
+    GrEffect_Globals::GetTLS()->release(target);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 GrEffect::~GrEffect() {
-
-}
-
-bool GrEffect::isOpaque(bool inputTextureIsOpaque) const {
-    return false;
+    GrAssert(NULL == fEffectRef);
 }
 
 const char* GrEffect::name() const {
     return this->getFactory().name();
 }
 
-
-bool GrEffect::isEqual(const GrEffect& s) const {
-    if (this->numTextures() != s.numTextures()) {
-        return false;
-    }
-    for (int i = 0; i < this->numTextures(); ++i) {
-        if (this->textureAccess(i) != s.textureAccess(i)) {
-            return false;
-        }
-    }
-    return true;
+void GrEffect::addTextureAccess(const GrTextureAccess* access) {
+    fTextureAccesses.push_back(access);
 }
 
-const GrTextureAccess& GrEffect::textureAccess(int index) const {
-    GrCrash("We shouldn't be calling this function on the base class.");
-    static GrTextureAccess kDummy;
-    return kDummy;
-}
-
-void * GrEffect::operator new(size_t size) {
+void* GrEffect::operator new(size_t size) {
     return GrEffect_Globals::GetTLS()->allocate(size);
 }
 
diff --git a/src/gpu/GrGeometryBuffer.h b/src/gpu/GrGeometryBuffer.h
index c156fa3..52318c1 100644
--- a/src/gpu/GrGeometryBuffer.h
+++ b/src/gpu/GrGeometryBuffer.h
@@ -76,8 +76,8 @@
     virtual size_t sizeInBytes() const { return fSizeInBytes; }
 
 protected:
-    GrGeometryBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
-        : INHERITED(gpu)
+    GrGeometryBuffer(GrGpu* gpu, bool isWrapped, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, isWrapped)
         , fSizeInBytes(sizeInBytes)
         , fDynamic(dynamic) {}
 
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index b84b0f1..3da8219 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -23,8 +23,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-extern void gr_run_unittests();
-
 #define DEBUG_INVAL_BUFFER    0xdeadcafe
 #define DEBUG_INVAL_START_IDX -1
 
@@ -35,16 +33,12 @@
     , fIndexPool(NULL)
     , fVertexPoolUseCnt(0)
     , fIndexPoolUseCnt(0)
-    , fQuadIndexBuffer(NULL)
     , fUnitSquareVertexBuffer(NULL)
+    , fQuadIndexBuffer(NULL)
     , fContextIsDirty(true) {
 
     fClipMaskManager.setGpu(this);
 
-#if GR_DEBUG
-    //gr_run_unittests();
-#endif
-
     fGeomPoolStateStack.push_back();
 #if GR_DEBUG
     GeometryPoolState& poolState = fGeomPoolStateStack.back();
@@ -337,7 +331,7 @@
 
 bool GrGpu::setupClipAndFlushState(DrawType type) {
 
-    if (!fClipMaskManager.setupClipping(fClip)) {
+    if (!fClipMaskManager.setupClipping(this->getClip())) {
         return false;
     }
 
@@ -366,6 +360,8 @@
     newState.fPoolStartVertex = DEBUG_INVAL_START_IDX;
     newState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
     newState.fPoolStartIndex = DEBUG_INVAL_START_IDX;
+#else
+    (void) newState; // silence compiler warning
 #endif
 }
 
@@ -375,45 +371,18 @@
     fGeomPoolStateStack.pop_back();
 }
 
-void GrGpu::onDrawIndexed(GrPrimitiveType type,
-                          int startVertex,
-                          int startIndex,
-                          int vertexCount,
-                          int indexCount) {
-
+void GrGpu::onDraw(const DrawInfo& info) {
     this->handleDirtyContext();
-
-    if (!this->setupClipAndFlushState(PrimTypeToDrawType(type))) {
+    if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()))) {
         return;
     }
-
-    int sVertex = startVertex;
-    int sIndex = startIndex;
-    setupGeometry(&sVertex, &sIndex, vertexCount, indexCount);
-
-    this->onGpuDrawIndexed(type, sVertex, sIndex,
-                           vertexCount, indexCount);
-}
-
-void GrGpu::onDrawNonIndexed(GrPrimitiveType type,
-                             int startVertex,
-                             int vertexCount) {
-    this->handleDirtyContext();
-
-    if (!this->setupClipAndFlushState(PrimTypeToDrawType(type))) {
-        return;
-    }
-
-    int sVertex = startVertex;
-    setupGeometry(&sVertex, NULL, vertexCount, 0);
-
-    this->onGpuDrawNonIndexed(type, sVertex, vertexCount);
+    this->onGpuDraw(info);
 }
 
 void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillType fill) {
     this->handleDirtyContext();
 
-    // TODO: make this more effecient (don't copy and copy back)
+    // TODO: make this more efficient (don't copy and copy back)
     GrAutoTRestore<GrStencilSettings> asr(this->drawState()->stencil());
 
     this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
@@ -460,7 +429,7 @@
     }
 }
 
-bool GrGpu::onReserveVertexSpace(GrVertexLayout vertexLayout,
+bool GrGpu::onReserveVertexSpace(size_t vertexSize,
                                  int vertexCount,
                                  void** vertices) {
     GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
@@ -470,7 +439,7 @@
 
     this->prepareVertexPool();
 
-    *vertices = fVertexPool->makeSpace(vertexLayout,
+    *vertices = fVertexPool->makeSpace(vertexSize,
                                        vertexCount,
                                        &geomPoolState.fPoolVertexBuffer,
                                        &geomPoolState.fPoolStartVertex);
@@ -502,7 +471,7 @@
 void GrGpu::releaseReservedVertexSpace() {
     const GeometrySrcState& geoSrc = this->getGeomSrc();
     GrAssert(kReserved_GeometrySrcType == geoSrc.fVertexSrc);
-    size_t bytes = geoSrc.fVertexCount * VertexSize(geoSrc.fVertexLayout);
+    size_t bytes = geoSrc.fVertexCount * GrDrawState::VertexSize(geoSrc.fVertexLayout);
     fVertexPool->putBack(bytes);
     --fVertexPoolUseCnt;
 }
@@ -521,7 +490,7 @@
 #if GR_DEBUG
     bool success =
 #endif
-    fVertexPool->appendVertices(this->getVertexLayout(),
+    fVertexPool->appendVertices(GrDrawState::VertexSize(this->getVertexLayout()),
                                 vertexCount,
                                 vertexArray,
                                 &geomPoolState.fPoolVertexBuffer,
@@ -548,7 +517,7 @@
     // if vertex source was array, we stowed data in the pool
     const GeometrySrcState& geoSrc = this->getGeomSrc();
     GrAssert(kArray_GeometrySrcType == geoSrc.fVertexSrc);
-    size_t bytes = geoSrc.fVertexCount * VertexSize(geoSrc.fVertexLayout);
+    size_t bytes = geoSrc.fVertexCount * GrDrawState::VertexSize(geoSrc.fVertexLayout);
     fVertexPool->putBack(bytes);
     --fVertexPoolUseCnt;
 }
@@ -561,4 +530,3 @@
     fIndexPool->putBack(bytes);
     --fIndexPoolUseCnt;
 }
-
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 4bc4c25..bcda257 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -317,7 +317,7 @@
 
     /**
      * These methods are called by the clip manager's setupClipping function
-     * which (called as part of GrGpu's implementation of onDraw* and
+     * which (called as part of GrGpu's implementation of onDraw and
      * onStencilPath member functions.) The GrGpu subclass should flush the
      * stencil state to the 3D API in its implementation of flushGraphicsState.
      */
@@ -417,43 +417,36 @@
     // constructors
     bool    fConfigRenderSupport[kGrPixelConfigCount];
 
-    // GrDrawTarget overrides
-    virtual bool onReserveVertexSpace(GrVertexLayout vertexLayout,
-                                      int vertexCount,
-                                      void** vertices) SK_OVERRIDE;
-    virtual bool onReserveIndexSpace(int indexCount,
-                                     void** indices) SK_OVERRIDE;
-    virtual void releaseReservedVertexSpace() SK_OVERRIDE;
-    virtual void releaseReservedIndexSpace() SK_OVERRIDE;
-    virtual void onSetVertexSourceToArray(const void* vertexArray,
-                                          int vertexCount) SK_OVERRIDE;
-    virtual void onSetIndexSourceToArray(const void* indexArray,
-                                         int indexCount) SK_OVERRIDE;
-    virtual void releaseVertexArray() SK_OVERRIDE;
-    virtual void releaseIndexArray() SK_OVERRIDE;
-    virtual void geometrySourceWillPush() SK_OVERRIDE;
-    virtual void geometrySourceWillPop(
-        const GeometrySrcState& restoredState) SK_OVERRIDE;
-
     // Helpers for setting up geometry state
     void finalizeReservedVertices();
     void finalizeReservedIndices();
 
+private:
+    // GrDrawTarget overrides
+    virtual bool onReserveVertexSpace(size_t vSize, int vertexCount, void** vertices) SK_OVERRIDE;
+    virtual bool onReserveIndexSpace(int indexCount, void** indices) SK_OVERRIDE;
+    virtual void releaseReservedVertexSpace() SK_OVERRIDE;
+    virtual void releaseReservedIndexSpace() SK_OVERRIDE;
+    virtual void onSetVertexSourceToArray(const void* vertexArray, int vertexCount) SK_OVERRIDE;
+    virtual void onSetIndexSourceToArray(const void* indexArray, int indexCount) SK_OVERRIDE;
+    virtual void releaseVertexArray() SK_OVERRIDE;
+    virtual void releaseIndexArray() SK_OVERRIDE;
+    virtual void geometrySourceWillPush() SK_OVERRIDE;
+    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) SK_OVERRIDE;
+
+
     // called when the 3D context state is unknown. Subclass should emit any
     // assumed 3D context state and dirty any state cache.
     virtual void onResetContext() = 0;
 
-
     // overridden by backend-specific derived class to create objects.
     virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
                                        const void* srcData,
                                        size_t rowBytes) = 0;
     virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) = 0;
     virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) = 0;
-    virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
-                                                 bool dynamic) = 0;
-    virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
-                                               bool dynamic) = 0;
+    virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size, bool dynamic) = 0;
+    virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size, bool dynamic) = 0;
     virtual GrPath* onCreatePath(const SkPath& path) = 0;
 
     // overridden by backend-specific derived class to perform the clear and
@@ -461,15 +454,7 @@
     virtual void onClear(const GrIRect* rect, GrColor color) = 0;
 
     // overridden by backend-specific derived class to perform the draw call.
-    virtual void onGpuDrawIndexed(GrPrimitiveType type,
-                                  uint32_t startVertex,
-                                  uint32_t startIndex,
-                                  uint32_t vertexCount,
-                                  uint32_t indexCount) = 0;
-
-    virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
-                                     uint32_t vertexCount,
-                                     uint32_t numVertices) = 0;
+    virtual void onGpuDraw(const DrawInfo&) = 0;
     // when GrDrawTarget::stencilPath is called the draw state's current stencil
     // settings are ignored. Instead the GrGpu decides the stencil rules
     // necessary to stencil the path. These are still subject to filtering by
@@ -500,24 +485,13 @@
     // overridden by backend-specific derived class to perform the resolve
     virtual void onResolveRenderTarget(GrRenderTarget* target) = 0;
 
-    // called to program the vertex data, indexCount will be 0 if drawing non-
-    // indexed geometry. The subclass may adjust the startVertex and/or
-    // startIndex since it may have already accounted for these in the setup.
-    virtual void setupGeometry(int* startVertex,
-                               int* startIndex,
-                               int vertexCount,
-                               int indexCount) = 0;
-
     // width and height may be larger than rt (if underlying API allows it).
     // Should attach the SB to the RT. Returns false if compatible sb could
     // not be created.
-    virtual bool createStencilBufferForRenderTarget(GrRenderTarget* rt,
-                                                    int width,
-                                                    int height) = 0;
+    virtual bool createStencilBufferForRenderTarget(GrRenderTarget*, int width, int height) = 0;
 
     // attaches an existing SB to an existing RT.
-    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
-                                                   GrRenderTarget* rt) = 0;
+    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer*, GrRenderTarget*) = 0;
 
     // The GrGpu typically records the clients requested state and then flushes
     // deltas from previous state at draw time. This function does the
@@ -528,48 +502,11 @@
     // clears the entire stencil buffer to 0
     virtual void clearStencil() = 0;
 
-private:
-    GrContext*                  fContext; // not reffed (context refs gpu)
-
-    ResetTimestamp              fResetTimestamp;
-
-    GrVertexBufferAllocPool*    fVertexPool;
-
-    GrIndexBufferAllocPool*     fIndexPool;
-
-    // counts number of uses of vertex/index pool in the geometry stack
-    int                         fVertexPoolUseCnt;
-    int                         fIndexPoolUseCnt;
-
-    enum {
-        kPreallocGeomPoolStateStackCnt = 4,
-    };
-    SkSTArray<kPreallocGeomPoolStateStackCnt,
-              GeometryPoolState, true>              fGeomPoolStateStack;
-
-    mutable GrIndexBuffer*      fQuadIndexBuffer; // mutable so it can be
-                                                  // created on-demand
-
-    mutable GrVertexBuffer*     fUnitSquareVertexBuffer; // mutable so it can be
-                                                         // created on-demand
-
-    bool                        fContextIsDirty;
-
-    typedef SkTInternalLList<GrResource> ResourceList;
-    ResourceList                fResourceList;
-
     // Given a rt, find or create a stencil buffer and attach it
     bool attachStencilBufferToRenderTarget(GrRenderTarget* target);
 
     // GrDrawTarget overrides
-    virtual void onDrawIndexed(GrPrimitiveType type,
-                               int startVertex,
-                               int startIndex,
-                               int vertexCount,
-                               int indexCount) SK_OVERRIDE;
-    virtual void onDrawNonIndexed(GrPrimitiveType type,
-                                  int startVertex,
-                                  int vertexCount) SK_OVERRIDE;
+    virtual void onDraw(const DrawInfo&) SK_OVERRIDE;
     virtual void onStencilPath(const GrPath* path, const SkStrokeRec& stroke,
                                SkPath::FillType) SK_OVERRIDE;
 
@@ -593,6 +530,24 @@
         }
     }
 
+    enum {
+        kPreallocGeomPoolStateStackCnt = 4,
+    };
+    typedef SkTInternalLList<GrResource> ResourceList;
+    SkSTArray<kPreallocGeomPoolStateStackCnt, GeometryPoolState, true>  fGeomPoolStateStack;
+    GrContext*                                                          fContext; // not reffed
+    ResetTimestamp                                                      fResetTimestamp;
+    GrVertexBufferAllocPool*                                            fVertexPool;
+    GrIndexBufferAllocPool*                                             fIndexPool;
+    // counts number of uses of vertex/index pool in the geometry stack
+    int                                                                 fVertexPoolUseCnt;
+    int                                                                 fIndexPoolUseCnt;
+    // these are mutable so they can be created on-demand
+    mutable GrVertexBuffer*                                             fUnitSquareVertexBuffer;
+    mutable GrIndexBuffer*                                              fQuadIndexBuffer;
+    bool                                                                fContextIsDirty;
+    ResourceList                                                        fResourceList;
+
     typedef GrDrawTarget INHERITED;
 };
 
diff --git a/src/gpu/GrGpuVertex.h b/src/gpu/GrGpuVertex.h
index a5e39e8..19a0b01 100644
--- a/src/gpu/GrGpuVertex.h
+++ b/src/gpu/GrGpuVertex.h
@@ -94,4 +94,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp
index d51dca4..bd54967 100644
--- a/src/gpu/GrInOrderDrawBuffer.cpp
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -21,13 +21,12 @@
                                          GrIndexBufferAllocPool* indexPool)
     : fAutoFlushTarget(NULL)
     , fClipSet(true)
+    , fClipProxyState(kUnknown_ClipProxyState)
     , fVertexPool(*vertexPool)
     , fIndexPool(*indexPool)
-    , fLastRectVertexLayout(0)
-    , fQuadIndexBuffer(NULL)
-    , fMaxQuads(0)
     , fFlushing(false) {
 
+    fGpu.reset(SkRef(gpu));
     fCaps = gpu->getCaps();
 
     GrAssert(NULL != vertexPool);
@@ -49,29 +48,26 @@
     this->reset();
     // This must be called by before the GrDrawTarget destructor
     this->releaseGeometry();
-    GrSafeUnref(fQuadIndexBuffer);
     GrSafeUnref(fAutoFlushTarget);
 }
 
-void GrInOrderDrawBuffer::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) {
-    bool newIdxBuffer = fQuadIndexBuffer != indexBuffer;
-    if (newIdxBuffer) {
-        GrSafeUnref(fQuadIndexBuffer);
-        fQuadIndexBuffer = indexBuffer;
-        GrSafeRef(fQuadIndexBuffer);
-        fCurrQuad = 0;
-        fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads();
-    } else {
-        GrAssert((NULL == indexBuffer && 0 == fMaxQuads) ||
-                 (indexBuffer->maxQuads() == fMaxQuads));
-    }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrInOrderDrawBuffer::resetDrawTracking() {
-    fCurrQuad = 0;
-    fInstancedDrawTracker.reset();
+namespace {
+void get_vertex_bounds(const void* vertices,
+                       size_t vertexSize,
+                       int vertexCount,
+                       SkRect* bounds) {
+    GrAssert(vertexSize >= sizeof(GrPoint));
+    GrAssert(vertexCount > 0);
+    const GrPoint* point = static_cast<const GrPoint*>(vertices);
+    bounds->fLeft = bounds->fRight = point->fX;
+    bounds->fTop = bounds->fBottom = point->fY;
+    for (int i = 1; i < vertexCount; ++i) {
+        point = reinterpret_cast<GrPoint*>(reinterpret_cast<intptr_t>(point) + vertexSize);
+        bounds->growToInclude(point->fX, point->fY);
+    }
+}
 }
 
 void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
@@ -79,314 +75,223 @@
                                    const GrRect* srcRects[],
                                    const SkMatrix* srcMatrices[]) {
 
-    GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
-    GrAssert(!(fDraws.empty() && fCurrQuad));
-    GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));
+    GrVertexLayout layout = 0;
+    GrDrawState::AutoColorRestore acr;
+    GrColor color = this->drawState()->getColor();
 
-    GrDrawState* drawState = this->drawState();
-
-    // if we have a quad IB then either append to the previous run of
-    // rects or start a new run
-    if (fMaxQuads) {
-
-        bool appendToPreviousDraw = false;
-        GrVertexLayout layout = GetRectVertexLayout(srcRects);
-
-        // Batching across colors means we move the draw color into the
-        // rect's vertex colors to allow greater batching (a lot of rects
-        // in a row differing only in color is a common occurence in tables).
-        bool batchAcrossColors = true;
-        if (!this->getCaps().dualSourceBlendingSupport()) {
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                if (this->getDrawState().isStageEnabled(s)) {
-                    // We disable batching across colors when there is a texture
-                    // present because (by pushing the the color to the vertices)
-                    // Ganesh loses track of the rect's opacity. This, in turn, can
-                    // cause some of the blending optimizations to be disabled. This
-                    // becomes a huge problem on some of the smaller devices where
-                    // shader derivatives and dual source blending aren't supported.
-                    // In those cases paths are often drawn to a texture and then
-                    // drawn as a texture (using this method). Because dual source
-                    // blending is disabled (and the blend optimizations are short
-                    // circuited) some of the more esoteric blend modes can no longer
-                    // be supported.
-                    // TODO: add tracking of batchAcrossColors's opacity
-                    batchAcrossColors = false;
-                    break;
-                }
-            }
-        }
-
-        if (batchAcrossColors) {
-            layout |= kColor_VertexLayoutBit;
-        }
-
-        AutoReleaseGeometry geo(this, layout, 4, 0);
-        if (!geo.succeeded()) {
-            GrPrintf("Failed to get space for vertices!\n");
-            return;
-        }
-        SkMatrix combinedMatrix = drawState->getViewMatrix();
-        // We go to device space so that matrix changes allow us to concat
-        // rect draws. When the caller has provided explicit source rects
-        // then we don't want to modify the stages' matrices. Otherwise
-        // we have to account for the view matrix change in the stage
-        // matrices.
-        uint32_t explicitCoordMask = 0;
-        if (srcRects) {
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                if (srcRects[s]) {
-                    explicitCoordMask |= (1 << s);
-                }
-            }
-        }
-        GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask);
-        if (!adcd.succeeded()) {
-            return;
-        }
-        if (NULL != matrix) {
-            combinedMatrix.preConcat(*matrix);
-        }
-
-        SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices,
-                        this->getDrawState().getColor(), layout, geo.vertices());
-
-        // Now that the paint's color is stored in the vertices set it to
-        // white so that the following code can batch all the rects regardless
-        // of paint color
-        GrDrawState::AutoColorRestore acr(this->drawState(),
-                                          batchAcrossColors ? SK_ColorWHITE
-                                                            : this->getDrawState().getColor());
-
-        // we don't want to miss an opportunity to batch rects together
-        // simply because the clip has changed if the clip doesn't affect
-        // the rect.
-        bool disabledClip = false;
-
-        if (drawState->isClipState()) {
-
-            GrRect devClipRect;
-            bool isIntersectionOfRects = false;
-
-            fClip->fClipStack->getConservativeBounds(-fClip->fOrigin.fX,
-                                                     -fClip->fOrigin.fY,
-                                                     drawState->getRenderTarget()->width(),
-                                                     drawState->getRenderTarget()->height(),
-                                                     &devClipRect,
-                                                     &isIntersectionOfRects);
-
-            if (isIntersectionOfRects) {
-                // If the clip rect touches the edge of the viewport, extended it
-                // out (close) to infinity to avoid bogus intersections.
-                // We might consider a more exact clip to viewport if this
-                // conservative test fails.
-                const GrRenderTarget* target = drawState->getRenderTarget();
-                if (0 >= devClipRect.fLeft) {
-                    devClipRect.fLeft = SK_ScalarMin;
-                }
-                if (target->width() <= devClipRect.fRight) {
-                    devClipRect.fRight = SK_ScalarMax;
-                }
-                if (0 >= devClipRect.top()) {
-                    devClipRect.fTop = SK_ScalarMin;
-                }
-                if (target->height() <= devClipRect.fBottom) {
-                    devClipRect.fBottom = SK_ScalarMax;
-                }
-                int stride = VertexSize(layout);
-                bool insideClip = true;
-                for (int v = 0; v < 4; ++v) {
-                    const GrPoint& p = *GetVertexPoint(geo.vertices(), v, stride);
-                    if (!devClipRect.contains(p)) {
-                        insideClip = false;
-                        break;
-                    }
-                }
-                if (insideClip) {
-                    drawState->disableState(GrDrawState::kClip_StateBit);
-                    disabledClip = true;
-                }
-            }
-        }
-
-        if (!this->needsNewClip() &&
-            !this->needsNewState() &&
-            fCurrQuad > 0 &&
-            fCurrQuad < fMaxQuads &&
-            layout == fLastRectVertexLayout) {
-
-            int vsize = VertexSize(layout);
-
-            Draw& lastDraw = fDraws.back();
-
-            GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
-            GrAssert(kTriangles_GrPrimitiveType == lastDraw.fPrimitiveType);
-            GrAssert(0 == lastDraw.fVertexCount % 4);
-            GrAssert(0 == lastDraw.fIndexCount % 6);
-            GrAssert(0 == lastDraw.fStartIndex);
-
-            GeometryPoolState& poolState = fGeoPoolStateStack.back();
-
-            appendToPreviousDraw =
-                kDraw_Cmd == fCmds.back() &&
-                lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer &&
-                (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex;
-
-            if (appendToPreviousDraw) {
-                lastDraw.fVertexCount += 4;
-                lastDraw.fIndexCount += 6;
-                fCurrQuad += 1;
-                // we reserved above, so we should be the first
-                // use of this vertex reservation.
-                GrAssert(0 == poolState.fUsedPoolVertexBytes);
-                poolState.fUsedPoolVertexBytes = 4 * vsize;
-            }
-        }
-        if (!appendToPreviousDraw) {
-            this->setIndexSourceToBuffer(fQuadIndexBuffer);
-            this->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
-            fCurrQuad = 1;
-            fLastRectVertexLayout = layout;
-        }
-        if (disabledClip) {
-            drawState->enableState(GrDrawState::kClip_StateBit);
-        }
-        fInstancedDrawTracker.reset();
-    } else {
-        INHERITED::drawRect(rect, matrix, srcRects, srcMatrices);
+    // Using per-vertex colors allows batching across colors. (A lot of rects in a row differing
+    // only in color is a common occurrence in tables). However, having per-vertex colors disables
+    // blending optimizations because we don't know if the color will be solid or not. These
+    // optimizations help determine whether coverage and color can be blended correctly when
+    // dual-source blending isn't available. This comes into play when there is coverage. If colors
+    // were a stage it could take a hint that every vertex's color will be opaque.
+    if (this->getCaps().dualSourceBlendingSupport() ||
+        this->getDrawState().hasSolidCoverage(this->getGeomSrc().fVertexLayout)) {
+        layout |= GrDrawState::kColor_VertexLayoutBit;;
+        // We set the draw state's color to white here. This is done so that any batching performed
+        // in our subclass's onDraw() won't get a false from GrDrawState::op== due to a color
+        // mismatch. TODO: Once vertex layout is owned by GrDrawState it should skip comparing the
+        // constant color in its op== when the kColor layout bit is set and then we can remove this.
+        acr.set(this->drawState(), 0xFFFFFFFF);
     }
-}
 
-void GrInOrderDrawBuffer::drawIndexedInstances(GrPrimitiveType type,
-                                               int instanceCount,
-                                               int verticesPerInstance,
-                                               int indicesPerInstance) {
-    if (!verticesPerInstance || !indicesPerInstance) {
+    uint32_t explicitCoordMask = 0;
+    if (NULL != srcRects) {
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            int numTC = 0;
+            if (NULL != srcRects[s]) {
+                layout |= GrDrawState::StageTexCoordVertexLayoutBit(s, numTC);
+                ++numTC;
+                explicitCoordMask |= (1 << s);
+            }
+        }
+    }
+
+    AutoReleaseGeometry geo(this, layout, 4, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
         return;
     }
 
+    // Go to device coords to allow batching across matrix changes
+    SkMatrix combinedMatrix;
+    if (NULL != matrix) {
+        combinedMatrix = *matrix;
+    } else {
+        combinedMatrix.reset();
+    }
+    combinedMatrix.postConcat(this->drawState()->getViewMatrix());
+    // When the caller has provided an explicit source rects for a stage then we don't want to
+    // modify that stage's matrix. Otherwise if the effect is generating its source rect from
+    // the vertex positions then we have to account for the view matrix change.
+    GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask);
+    if (!adcd.succeeded()) {
+        return;
+    }
+
+    int stageOffsets[GrDrawState::kNumStages], colorOffset;
+    int vsize = GrDrawState::VertexSizeAndOffsetsByStage(layout, stageOffsets,
+                                                         &colorOffset, NULL, NULL);
+
+    geo.positions()->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize);
+    combinedMatrix.mapPointsWithStride(geo.positions(), vsize, 4);
+
+    SkRect devBounds;
+    // since we already computed the dev verts, set the bounds hint. This will help us avoid
+    // unnecessary clipping in our onDraw().
+    get_vertex_bounds(geo.vertices(), vsize, 4, &devBounds);
+
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        if (explicitCoordMask & (1 << i)) {
+            GrAssert(0 != stageOffsets[i]);
+            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(geo.vertices()) +
+                                                stageOffsets[i]);
+            coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop,
+                               srcRects[i]->fRight, srcRects[i]->fBottom,
+                               vsize);
+            if (NULL != srcMatrices && NULL != srcMatrices[i]) {
+                srcMatrices[i]->mapPointsWithStride(coords, vsize, 4);
+            }
+        } else {
+            GrAssert(0 == stageOffsets[i]);
+        }
+    }
+
+    if (colorOffset >= 0) {
+        GrColor* vertColor = GrTCast<GrColor*>(GrTCast<intptr_t>(geo.vertices()) + colorOffset);
+        for (int i = 0; i < 4; ++i) {
+            *vertColor = color;
+            vertColor = (GrColor*) ((intptr_t) vertColor + vsize);
+        }
+    }
+
+    this->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
+    this->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &devBounds);
+}
+
+bool GrInOrderDrawBuffer::quickInsideClip(const SkRect& devBounds) {
+    if (!this->getDrawState().isClipState()) {
+        return true;
+    }
+    if (kUnknown_ClipProxyState == fClipProxyState) {
+        SkIRect rect;
+        bool iior;
+        this->getClip()->getConservativeBounds(this->getDrawState().getRenderTarget(), &rect, &iior);
+        if (iior) {
+            // The clip is a rect. We will remember that in fProxyClip. It is common for an edge (or
+            // all edges) of the clip to be at the edge of the RT. However, we get that clipping for
+            // free via the viewport. We don't want to think that clipping must be enabled in this
+            // case. So we extend the clip outward from the edge to avoid these false negatives.
+            fClipProxyState = kValid_ClipProxyState;
+            fClipProxy = SkRect::MakeFromIRect(rect);
+
+            if (fClipProxy.fLeft <= 0) {
+                fClipProxy.fLeft = SK_ScalarMin;
+            }
+            if (fClipProxy.fTop <= 0) {
+                fClipProxy.fTop = SK_ScalarMin;
+            }
+            if (fClipProxy.fRight >= this->getDrawState().getRenderTarget()->width()) {
+                fClipProxy.fRight = SK_ScalarMax;
+            }
+            if (fClipProxy.fBottom >= this->getDrawState().getRenderTarget()->height()) {
+                fClipProxy.fBottom = SK_ScalarMax;
+            }
+        } else {
+            fClipProxyState = kInvalid_ClipProxyState;
+        }
+    }
+    if (kValid_ClipProxyState == fClipProxyState) {
+        return fClipProxy.contains(devBounds);
+    }
+    SkPoint originOffset = {SkIntToScalar(this->getClip()->fOrigin.fX),
+                            SkIntToScalar(this->getClip()->fOrigin.fY)};
+    SkRect clipSpaceBounds = devBounds;
+    clipSpaceBounds.offset(originOffset);
+    return this->getClip()->fClipStack->quickContains(clipSpaceBounds);
+}
+
+int GrInOrderDrawBuffer::concatInstancedDraw(const DrawInfo& info) {
+    GrAssert(info.isInstanced());
+
     const GeometrySrcState& geomSrc = this->getGeomSrc();
 
-    // we only attempt to concat the case when reserved verts are used with
-    // an index buffer.
-    if (kReserved_GeometrySrcType == geomSrc.fVertexSrc &&
-        kBuffer_GeometrySrcType == geomSrc.fIndexSrc) {
-
-        if (this->needsNewClip()) {
-            this->recordClip();
-        }
-        if (this->needsNewState()) {
-            this->recordState();
-        }
-
-        Draw* draw = NULL;
-        // if the last draw used the same indices/vertices per shape then we
-        // may be able to append to it.
-        if (kDraw_Cmd == fCmds.back() &&
-            verticesPerInstance == fInstancedDrawTracker.fVerticesPerInstance &&
-            indicesPerInstance == fInstancedDrawTracker.fIndicesPerInstance) {
-            GrAssert(fDraws.count());
-            draw = &fDraws.back();
-        }
-
-        GeometryPoolState& poolState = fGeoPoolStateStack.back();
-        const GrVertexBuffer* vertexBuffer = poolState.fPoolVertexBuffer;
-
-        // Check whether the draw is compatible with this draw in order to
-        // append
-        if (NULL == draw ||
-            draw->fIndexBuffer != geomSrc.fIndexBuffer ||
-            draw->fPrimitiveType != type ||
-            draw->fVertexBuffer != vertexBuffer) {
-
-            draw = this->recordDraw();
-            draw->fPrimitiveType = type;
-            draw->fStartVertex = poolState.fPoolStartVertex;
-            draw->fStartIndex = 0;
-            draw->fVertexCount = 0;
-            draw->fIndexCount = 0;
-            draw->fVertexLayout = geomSrc.fVertexLayout;
-            draw->fVertexBuffer = vertexBuffer;
-            vertexBuffer->ref();
-            draw->fIndexBuffer = geomSrc.fIndexBuffer;
-            geomSrc.fIndexBuffer->ref();
-        } else {
-            GrAssert(!(draw->fIndexCount % indicesPerInstance));
-            GrAssert(!(draw->fVertexCount % verticesPerInstance));
-            GrAssert(poolState.fPoolStartVertex == draw->fStartVertex +
-                                                   draw->fVertexCount);
-        }
-
-        // how many instances can be in a single draw
-        int maxInstancesPerDraw = this->indexCountInCurrentSource() /
-                                  indicesPerInstance;
-        if (!maxInstancesPerDraw) {
-            return;
-        }
-        // how many instances should be concat'ed onto draw
-        int instancesToConcat = maxInstancesPerDraw - draw->fVertexCount /
-                                                      verticesPerInstance;
-        if (maxInstancesPerDraw > instanceCount) {
-            maxInstancesPerDraw = instanceCount;
-            if (instancesToConcat > instanceCount) {
-                instancesToConcat = instanceCount;
-            }
-        }
-
-        // update the amount of reserved data actually referenced in draws
-        size_t vertexBytes = instanceCount * verticesPerInstance *
-                             VertexSize(draw->fVertexLayout);
-        poolState.fUsedPoolVertexBytes =
-                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
-
-        while (instanceCount) {
-            if (!instancesToConcat) {
-                int startVertex = draw->fStartVertex + draw->fVertexCount;
-                draw = this->recordDraw();
-                draw->fPrimitiveType = type;
-                draw->fStartVertex = startVertex;
-                draw->fStartIndex = 0;
-                draw->fVertexCount = 0;
-                draw->fIndexCount = 0;
-                draw->fVertexLayout = geomSrc.fVertexLayout;
-                draw->fVertexBuffer = vertexBuffer;
-                vertexBuffer->ref();
-                draw->fIndexBuffer = geomSrc.fIndexBuffer;
-                geomSrc.fIndexBuffer->ref();
-                instancesToConcat = maxInstancesPerDraw;
-            }
-            draw->fVertexCount += instancesToConcat * verticesPerInstance;
-            draw->fIndexCount += instancesToConcat * indicesPerInstance;
-            instanceCount -= instancesToConcat;
-            instancesToConcat = 0;
-        }
-
-        // update draw tracking for next draw
-        fCurrQuad = 0;
-        fInstancedDrawTracker.fVerticesPerInstance = verticesPerInstance;
-        fInstancedDrawTracker.fIndicesPerInstance = indicesPerInstance;
-    } else {
-        this->INHERITED::drawIndexedInstances(type,
-                                              instanceCount,
-                                              verticesPerInstance,
-                                              indicesPerInstance);
+    // we only attempt to concat the case when reserved verts are used with a client-specified index
+    // buffer. To make this work with client-specified VBs we'd need to know if the VB was updated
+    // between draws.
+    if (kReserved_GeometrySrcType != geomSrc.fVertexSrc ||
+        kBuffer_GeometrySrcType != geomSrc.fIndexSrc) {
+        return 0;
+    }
+    // Check if there is a draw info that is compatible that uses the same VB from the pool and
+    // the same IB
+    if (kDraw_Cmd != fCmds.back()) {
+        return 0;
     }
 
+    DrawRecord* draw = &fDraws.back();
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    const GrVertexBuffer* vertexBuffer = poolState.fPoolVertexBuffer;
+
+    if (!draw->isInstanced() ||
+        draw->verticesPerInstance() != info.verticesPerInstance() ||
+        draw->indicesPerInstance() != info.indicesPerInstance() ||
+        draw->fVertexBuffer != vertexBuffer ||
+        draw->fIndexBuffer != geomSrc.fIndexBuffer ||
+        draw->fVertexLayout != geomSrc.fVertexLayout) {
+        return 0;
+    }
+    // info does not yet account for the offset from the start of the pool's VB while the previous
+    // draw record does.
+    int adjustedStartVertex = poolState.fPoolStartVertex + info.startVertex();
+    if (draw->startVertex() + draw->vertexCount() != adjustedStartVertex) {
+        return 0;
+    }
+
+    GrAssert(poolState.fPoolStartVertex == draw->startVertex() + draw->vertexCount());
+
+    // how many instances can be concat'ed onto draw given the size of the index buffer
+    int instancesToConcat = this->indexCountInCurrentSource() / info.indicesPerInstance();
+    instancesToConcat -= draw->instanceCount();
+    instancesToConcat = GrMin(instancesToConcat, info.instanceCount());
+
+    // update the amount of reserved vertex data actually referenced in draws
+    size_t vertexBytes = instancesToConcat * info.verticesPerInstance() *
+                         GrDrawState::VertexSize(draw->fVertexLayout);
+    poolState.fUsedPoolVertexBytes = GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+
+    draw->adjustInstanceCount(instancesToConcat);
+    return instancesToConcat;
 }
 
-void GrInOrderDrawBuffer::onDrawIndexed(GrPrimitiveType primitiveType,
-                                        int startVertex,
-                                        int startIndex,
-                                        int vertexCount,
-                                        int indexCount) {
-
-    if (!vertexCount || !indexCount) {
-        return;
+class AutoClipReenable {
+public:
+    AutoClipReenable() : fDrawState(NULL) {}
+    ~AutoClipReenable() {
+        if (NULL != fDrawState) {
+            fDrawState->enableState(GrDrawState::kClip_StateBit);
+        }
     }
+    void set(GrDrawState* drawState) {
+        if (drawState->isClipState()) {
+            fDrawState = drawState;
+            drawState->disableState(GrDrawState::kClip_StateBit);
+        }
+    }
+private:
+    GrDrawState*    fDrawState;
+};
 
-    this->resetDrawTracking();
+void GrInOrderDrawBuffer::onDraw(const DrawInfo& info) {
 
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    AutoClipReenable acr;
+
+    if (this->getDrawState().isClipState() &&
+        NULL != info.getDevBounds() &&
+        this->quickInsideClip(*info.getDevBounds())) {
+        acr.set(this->drawState());
+    }
 
     if (this->needsNewClip()) {
        this->recordClip();
@@ -395,97 +300,58 @@
         this->recordState();
     }
 
-    Draw* draw = this->recordDraw();
-
-    draw->fPrimitiveType = primitiveType;
-    draw->fStartVertex   = startVertex;
-    draw->fStartIndex    = startIndex;
-    draw->fVertexCount   = vertexCount;
-    draw->fIndexCount    = indexCount;
-
-    draw->fVertexLayout = this->getVertexLayout();
-    switch (this->getGeomSrc().fVertexSrc) {
-    case kBuffer_GeometrySrcType:
-        draw->fVertexBuffer = this->getGeomSrc().fVertexBuffer;
-        break;
-    case kReserved_GeometrySrcType: // fallthrough
-    case kArray_GeometrySrcType: {
-        size_t vertexBytes = (vertexCount + startVertex) *
-                             VertexSize(draw->fVertexLayout);
-        poolState.fUsedPoolVertexBytes =
-                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
-        draw->fVertexBuffer = poolState.fPoolVertexBuffer;
-        draw->fStartVertex += poolState.fPoolStartVertex;
-        break;
+    DrawRecord* draw;
+    if (info.isInstanced()) {
+        int instancesConcated = this->concatInstancedDraw(info);
+        if (info.instanceCount() > instancesConcated) {
+            draw = this->recordDraw(info);
+            draw->adjustInstanceCount(-instancesConcated);
+        } else {
+            return;
+        }
+    } else {
+        draw = this->recordDraw(info);
     }
-    default:
-        GrCrash("unknown geom src type");
+    draw->fVertexLayout = this->getVertexLayout();
+
+    switch (this->getGeomSrc().fVertexSrc) {
+        case kBuffer_GeometrySrcType:
+            draw->fVertexBuffer = this->getGeomSrc().fVertexBuffer;
+            break;
+        case kReserved_GeometrySrcType: // fallthrough
+        case kArray_GeometrySrcType: {
+            size_t vertexBytes = (info.vertexCount() + info.startVertex()) *
+                                 GrDrawState::VertexSize(draw->fVertexLayout);
+            poolState.fUsedPoolVertexBytes = GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+            draw->fVertexBuffer = poolState.fPoolVertexBuffer;
+            draw->adjustStartVertex(poolState.fPoolStartVertex);
+            break;
+        }
+        default:
+            GrCrash("unknown geom src type");
     }
     draw->fVertexBuffer->ref();
 
-    switch (this->getGeomSrc().fIndexSrc) {
-    case kBuffer_GeometrySrcType:
-        draw->fIndexBuffer = this->getGeomSrc().fIndexBuffer;
-        break;
-    case kReserved_GeometrySrcType: // fallthrough
-    case kArray_GeometrySrcType: {
-        size_t indexBytes = (indexCount + startIndex) * sizeof(uint16_t);
-        poolState.fUsedPoolIndexBytes =
-                            GrMax(poolState.fUsedPoolIndexBytes, indexBytes);
-        draw->fIndexBuffer = poolState.fPoolIndexBuffer;
-        draw->fStartIndex += poolState.fPoolStartIndex;
-        break;
+    if (info.isIndexed()) {
+        switch (this->getGeomSrc().fIndexSrc) {
+            case kBuffer_GeometrySrcType:
+                draw->fIndexBuffer = this->getGeomSrc().fIndexBuffer;
+                break;
+            case kReserved_GeometrySrcType: // fallthrough
+            case kArray_GeometrySrcType: {
+                size_t indexBytes = (info.indexCount() + info.startIndex()) * sizeof(uint16_t);
+                poolState.fUsedPoolIndexBytes = GrMax(poolState.fUsedPoolIndexBytes, indexBytes);
+                draw->fIndexBuffer = poolState.fPoolIndexBuffer;
+                draw->adjustStartIndex(poolState.fPoolStartIndex);
+                break;
+            }
+            default:
+                GrCrash("unknown geom src type");
+        }
+        draw->fIndexBuffer->ref();
+    } else {
+        draw->fIndexBuffer = NULL;
     }
-    default:
-        GrCrash("unknown geom src type");
-    }
-    draw->fIndexBuffer->ref();
-}
-
-void GrInOrderDrawBuffer::onDrawNonIndexed(GrPrimitiveType primitiveType,
-                                           int startVertex,
-                                           int vertexCount) {
-    if (!vertexCount) {
-        return;
-    }
-
-    this->resetDrawTracking();
-
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    if (this->needsNewClip()) {
-        this->recordClip();
-    }
-    if (this->needsNewState()) {
-        this->recordState();
-    }
-
-    Draw* draw = this->recordDraw();
-    draw->fPrimitiveType = primitiveType;
-    draw->fStartVertex   = startVertex;
-    draw->fStartIndex    = 0;
-    draw->fVertexCount   = vertexCount;
-    draw->fIndexCount    = 0;
-
-    draw->fVertexLayout = this->getVertexLayout();
-    switch (this->getGeomSrc().fVertexSrc) {
-    case kBuffer_GeometrySrcType:
-        draw->fVertexBuffer = this->getGeomSrc().fVertexBuffer;
-        break;
-    case kReserved_GeometrySrcType: // fallthrough
-    case kArray_GeometrySrcType: {
-        size_t vertexBytes = (vertexCount + startVertex) *
-                             VertexSize(draw->fVertexLayout);
-        poolState.fUsedPoolVertexBytes =
-                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
-        draw->fVertexBuffer = poolState.fPoolVertexBuffer;
-        draw->fStartVertex += poolState.fPoolStartVertex;
-        break;
-    }
-    default:
-        GrCrash("unknown geom src type");
-    }
-    draw->fVertexBuffer->ref();
-    draw->fIndexBuffer = NULL;
 }
 
 GrInOrderDrawBuffer::StencilPath::StencilPath() : fStroke(SkStrokeRec::kFill_InitStyle) {}
@@ -506,9 +372,7 @@
     sp->fStroke = stroke;
 }
 
-void GrInOrderDrawBuffer::clear(const GrIRect* rect,
-                                GrColor color,
-                                GrRenderTarget* renderTarget) {
+void GrInOrderDrawBuffer::clear(const GrIRect* rect, GrColor color, GrRenderTarget* renderTarget) {
     GrIRect r;
     if (NULL == renderTarget) {
         renderTarget = this->drawState()->getRenderTarget();
@@ -549,17 +413,9 @@
     fClips.reset();
     fClipOrigins.reset();
     fClipSet = true;
-
-    this->resetDrawTracking();
-
-    // we start off with a default clip and state so that we don't have
-    // to do count checks on fClips, fStates, or fCmds before checking their
-    // last entry.
-    this->recordDefaultState();
-    this->recordDefaultClip();
 }
 
-bool GrInOrderDrawBuffer::playback(GrDrawTarget* target) {
+bool GrInOrderDrawBuffer::flushTo(GrDrawTarget* target) {
     GrAssert(kReserved_GeometrySrcType != this->getGeomSrc().fVertexSrc);
     GrAssert(kReserved_GeometrySrcType != this->getGeomSrc().fIndexSrc);
 
@@ -567,10 +423,7 @@
     GrAssert(target != this); // not considered and why?
 
     int numCmds = fCmds.count();
-    GrAssert(numCmds >= 2);
-    if (2 == numCmds) {
-        GrAssert(kSetState_Cmd == fCmds[0]);
-        GrAssert(kSetClip_Cmd  == fCmds[1]);
+    if (0 == numCmds) {
         return false;
     }
 
@@ -579,8 +432,11 @@
 
     GrDrawTarget::AutoClipRestore acr(target);
     AutoGeometryPush agp(target);
+
+    GrDrawState playbackState;
     GrDrawState* prevDrawState = target->drawState();
     prevDrawState->ref();
+    target->setDrawState(&playbackState);
 
     GrClipData clipData;
 
@@ -590,26 +446,17 @@
     int currDraw        = 0;
     int currStencilPath = 0;
 
+
     for (int c = 0; c < numCmds; ++c) {
         switch (fCmds[c]) {
             case kDraw_Cmd: {
-                const Draw& draw = fDraws[currDraw];
+                const DrawRecord& draw = fDraws[currDraw];
                 target->setVertexSourceToBuffer(draw.fVertexLayout, draw.fVertexBuffer);
-                if (draw.fIndexCount) {
+                if (draw.isIndexed()) {
                     target->setIndexSourceToBuffer(draw.fIndexBuffer);
                 }
+                target->executeDraw(draw);
 
-                if (draw.fIndexCount) {
-                    target->drawIndexed(draw.fPrimitiveType,
-                                        draw.fStartVertex,
-                                        draw.fStartIndex,
-                                        draw.fVertexCount,
-                                        draw.fIndexCount);
-                } else {
-                    target->drawNonIndexed(draw.fPrimitiveType,
-                                           draw.fStartVertex,
-                                           draw.fVertexCount);
-                }
                 ++currDraw;
                 break;
             }
@@ -620,7 +467,7 @@
                 break;
             }
             case kSetState_Cmd:
-                target->setDrawState(&fStates[currState]);
+                fStates[currState].restoreTo(&playbackState);
                 ++currState;
                 break;
             case kSetClip_Cmd:
@@ -646,6 +493,7 @@
 
     target->setDrawState(prevDrawState);
     prevDrawState->unref();
+    this->reset();
     return true;
 }
 
@@ -654,7 +502,7 @@
 }
 
 void GrInOrderDrawBuffer::willReserveVertexAndIndexSpace(
-                                GrVertexLayout vertexLayout,
+                                size_t vertexSize,
                                 int vertexCount,
                                 int indexCount) {
     if (NULL != fAutoFlushTarget) {
@@ -686,14 +534,14 @@
             !unreleasedVertexSpace &&
             !unreleasedIndexSpace &&
             !targetHasReservedGeom &&
-            this->geometryHints(vertexLayout, &vcount, &icount)) {
+            this->geometryHints(vertexSize, &vcount, &icount)) {
 
             this->flushTo(fAutoFlushTarget);
         }
     }
 }
 
-bool GrInOrderDrawBuffer::geometryHints(GrVertexLayout vertexLayout,
+bool GrInOrderDrawBuffer::geometryHints(size_t vertexSize,
                                         int* vertexCount,
                                         int* indexCount) const {
     // we will recommend a flush if the data could fit in a single
@@ -711,10 +559,10 @@
         *indexCount = currIndices;
     }
     if (NULL != vertexCount) {
-        int32_t currVertices = fVertexPool.currentBufferVertices(vertexLayout);
+        int32_t currVertices = fVertexPool.currentBufferVertices(vertexSize);
         if (*vertexCount > currVertices &&
             (!fVertexPool.preallocatedBuffersRemaining() &&
-             *vertexCount <= fVertexPool.preallocatedBufferVertices(vertexLayout))) {
+             *vertexCount <= fVertexPool.preallocatedBufferVertices(vertexSize))) {
 
             flush = true;
         }
@@ -723,7 +571,7 @@
     return flush;
 }
 
-bool GrInOrderDrawBuffer::onReserveVertexSpace(GrVertexLayout vertexLayout,
+bool GrInOrderDrawBuffer::onReserveVertexSpace(size_t vertexSize,
                                                int vertexCount,
                                                void** vertices) {
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
@@ -731,7 +579,7 @@
     GrAssert(NULL != vertices);
     GrAssert(0 == poolState.fUsedPoolVertexBytes);
 
-    *vertices = fVertexPool.makeSpace(vertexLayout,
+    *vertices = fVertexPool.makeSpace(vertexSize,
                                       vertexCount,
                                       &poolState.fPoolVertexBuffer,
                                       &poolState.fPoolStartVertex);
@@ -763,7 +611,7 @@
     // provided by the vertex buffer pool. At each draw we tracked the largest
     // offset into the pool's pointer that was referenced. Now we return to the
     // pool any portion at the tail of the allocation that no draw referenced.
-    size_t reservedVertexBytes = VertexSize(geoSrc.fVertexLayout) *
+    size_t reservedVertexBytes = GrDrawState::VertexSize(geoSrc.fVertexLayout) *
                                  geoSrc.fVertexCount;
     fVertexPool.putBack(reservedVertexBytes -
                         poolState.fUsedPoolVertexBytes);
@@ -798,7 +646,7 @@
 #if GR_DEBUG
     bool success =
 #endif
-    fVertexPool.appendVertices(this->getVertexLayout(),
+    fVertexPool.appendVertices(GrDrawState::VertexSize(this->getVertexLayout()),
                                vertexCount,
                                vertexArray,
                                &poolState.fPoolVertexBuffer,
@@ -836,7 +684,6 @@
     GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
     poolState.fUsedPoolVertexBytes = 0;
     poolState.fUsedPoolIndexBytes = 0;
-    this->resetDrawTracking();
 #if GR_DEBUG
     poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
     poolState.fPoolStartVertex = ~0;
@@ -856,7 +703,7 @@
     if (kReserved_GeometrySrcType == restoredState.fVertexSrc ||
         kArray_GeometrySrcType == restoredState.fVertexSrc) {
         poolState.fUsedPoolVertexBytes =
-            VertexSize(restoredState.fVertexLayout) *
+            GrDrawState::VertexSize(restoredState.fVertexLayout) *
             restoredState.fVertexCount;
     }
     if (kReserved_GeometrySrcType == restoredState.fIndexSrc ||
@@ -864,20 +711,19 @@
         poolState.fUsedPoolIndexBytes = sizeof(uint16_t) *
                                          restoredState.fIndexCount;
     }
-    this->resetDrawTracking();
 }
 
 bool GrInOrderDrawBuffer::needsNewState() const {
-    // we should have recorded a default state in reset()
-    GrAssert(!fStates.empty());
-    return fStates.back() != this->getDrawState();
+    return fStates.empty() || !fStates.back().isEqual(this->getDrawState());
 }
 
 bool GrInOrderDrawBuffer::needsNewClip() const {
-   if (this->getDrawState().isClipState()) {
+    GrAssert(fClips.count() == fClipOrigins.count());
+    if (this->getDrawState().isClipState()) {
        if (fClipSet &&
-           (fClips.back() != *fClip->fClipStack ||
-            fClipOrigins.back() != fClip->fOrigin)) {
+           (fClips.empty() ||
+            fClips.back() != *this->getClip()->fClipStack ||
+            fClipOrigins.back() != this->getClip()->fOrigin)) {
            return true;
        }
     }
@@ -885,31 +731,20 @@
 }
 
 void GrInOrderDrawBuffer::recordClip() {
-    fClips.push_back() = *fClip->fClipStack;
-    fClipOrigins.push_back() = fClip->fOrigin;
+    fClips.push_back() = *this->getClip()->fClipStack;
+    fClipOrigins.push_back() = this->getClip()->fOrigin;
     fClipSet = false;
     fCmds.push_back(kSetClip_Cmd);
 }
 
-void GrInOrderDrawBuffer::recordDefaultClip() {
-    fClips.push_back() = SkClipStack();
-    fClipOrigins.push_back() = SkIPoint::Make(0, 0);
-    fCmds.push_back(kSetClip_Cmd);
-}
-
 void GrInOrderDrawBuffer::recordState() {
-    fStates.push_back(this->getDrawState());
+    fStates.push_back().saveFrom(this->getDrawState());
     fCmds.push_back(kSetState_Cmd);
 }
 
-void GrInOrderDrawBuffer::recordDefaultState() {
-    fStates.push_back(GrDrawState());
-    fCmds.push_back(kSetState_Cmd);
-}
-
-GrInOrderDrawBuffer::Draw* GrInOrderDrawBuffer::recordDraw() {
+GrInOrderDrawBuffer::DrawRecord* GrInOrderDrawBuffer::recordDraw(const DrawInfo& info) {
     fCmds.push_back(kDraw_Cmd);
-    return &fDraws.push_back();
+    return &fDraws.push_back(info);
 }
 
 GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() {
@@ -925,4 +760,5 @@
 void GrInOrderDrawBuffer::clipWillBeSet(const GrClipData* newClipData) {
     INHERITED::clipWillBeSet(newClipData);
     fClipSet = true;
+    fClipProxyState = kUnknown_ClipProxyState;
 }
diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h
index 17271f8..daa5d06 100644
--- a/src/gpu/GrInOrderDrawBuffer.h
+++ b/src/gpu/GrInOrderDrawBuffer.h
@@ -25,17 +25,15 @@
 class GrVertexBufferAllocPool;
 
 /**
- * GrInOrderDrawBuffer is an implementation of GrDrawTarget that queues up
- * draws for eventual playback into a GrGpu. In theory one draw buffer could
- * playback into another. When index or vertex buffers are used as geometry
- * sources it is the callers the draw buffer only holds references to the
- * buffers. It is the callers responsibility to ensure that the data is still
- * valid when the draw buffer is played back into a GrGpu. Similarly, it is the
- * caller's responsibility to ensure that all referenced textures, buffers,
- * and rendertargets are associated in the GrGpu object that the buffer is
- * played back into. The buffer requires VB and IB pools to store geometry.
+ * GrInOrderDrawBuffer is an implementation of GrDrawTarget that queues up draws for eventual
+ * playback into a GrGpu. In theory one draw buffer could playback into another. When index or
+ * vertex buffers are used as geometry sources it is the callers the draw buffer only holds
+ * references to the buffers. It is the callers responsibility to ensure that the data is still
+ * valid when the draw buffer is played back into a GrGpu. Similarly, it is the caller's
+ * responsibility to ensure that all referenced textures, buffers, and render-targets are associated
+ * in the GrGpu object that the buffer is played back into. The buffer requires VB and IB pools to
+ * store geometry.
  */
-
 class GrInOrderDrawBuffer : public GrDrawTarget {
 public:
 
@@ -57,86 +55,47 @@
     virtual ~GrInOrderDrawBuffer();
 
     /**
-     * Provides the buffer with an index buffer that can be used for quad rendering.
-     * The buffer may be able to batch consecutive drawRects if this is provided.
-     * @param indexBuffer   index buffer with quad indices.
-     */
-    void setQuadIndexBuffer(const GrIndexBuffer* indexBuffer);
-
-    /**
-     * Empties the draw buffer of any queued up draws. This must not be called
-     * while inside an unbalanced pushGeometrySource().
+     * Empties the draw buffer of any queued up draws. This must not be called while inside an
+     * unbalanced pushGeometrySource(). The current draw state and clip are preserved.
      */
     void reset();
 
     /**
-     * plays the queued up draws to another target. Does not empty this buffer
-     * so that it can be played back multiple times. This buffer must not have
-     * an active reserved vertex or index source. Any reserved geometry on
-     * the target will be finalized because it's geometry source will be pushed
-     * before playback and popped afterwards.
+     * This plays the queued up draws to another target. It also resets this object (i.e. flushing
+     * is destructive). This buffer must not have an active reserved vertex or index source. Any
+     * reserved geometry on the target will be finalized because it's geometry source will be pushed
+     * before flushing and popped afterwards.
      *
-     * @return false if the playback trivially drew nothing because nothing was
-     *         recorded.
+     * @return false if the playback trivially drew nothing because nothing was recorded.
      *
      * @param target    the target to receive the playback
      */
-    bool playback(GrDrawTarget* target);
+    bool flushTo(GrDrawTarget* target);
 
     /**
-     * A convenience method to do a playback followed by a reset. All the
-     * constraints and side-effects or playback() and reset apply().
-     */
-    void flushTo(GrDrawTarget* target) {
-        if (fFlushing) {
-            // When creating SW-only clip masks, the GrClipMaskManager can
-            // cause a GrContext::flush (when copying the mask results back
-            // to the GPU). Without a guard this results in a recursive call
-            // to this method.
-            return;
-        }
-
-        fFlushing = true;
-        if (this->playback(target)) {
-            this->reset();
-        }
-        fFlushing = false;
-    }
-
-    /**
-     * This function allows the draw buffer to automatically flush itself to
-     * another target. This means the buffer may internally call
-     * this->flushTo(target) when it is safe to do so.
+     * This function allows the draw buffer to automatically flush itself to another target. This
+     * means the buffer may internally call this->flushTo(target) when it is safe to do so.
      *
-     * When the auto flush target is set to NULL (as it initially is) the draw
-     * buffer will never automatically flush itself.
+     * When the auto flush target is set to NULL (as it initially is) the draw buffer will never
+     * automatically flush itself.
      */
     void setAutoFlushTarget(GrDrawTarget* target);
 
     // overrides from GrDrawTarget
-    virtual void drawRect(const GrRect& rect,
-                          const SkMatrix* matrix = NULL,
-                          const GrRect* srcRects[] = NULL,
-                          const SkMatrix* srcMatrices[] = NULL) SK_OVERRIDE;
-
-    virtual void drawIndexedInstances(GrPrimitiveType type,
-                                      int instanceCount,
-                                      int verticesPerInstance,
-                                      int indicesPerInstance)
-                                      SK_OVERRIDE;
-
-    virtual bool geometryHints(GrVertexLayout vertexLayout,
+    virtual bool geometryHints(size_t vertexSize,
                                int* vertexCount,
                                int* indexCount) const SK_OVERRIDE;
-
     virtual void clear(const GrIRect* rect,
                        GrColor color,
                        GrRenderTarget* renderTarget = NULL) SK_OVERRIDE;
+    virtual void drawRect(const GrRect& rect,
+                          const SkMatrix* matrix,
+                          const GrRect* srcRects[],
+                          const SkMatrix* srcMatrices[]) SK_OVERRIDE;
 
 protected:
-    virtual void willReserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
-                                                int vertexCount,
-                                                int indexCount) SK_OVERRIDE;
+    virtual void clipWillBeSet(const GrClipData* newClip) SK_OVERRIDE;
+
 private:
     enum Cmd {
         kDraw_Cmd           = 1,
@@ -146,12 +105,9 @@
         kClear_Cmd          = 5,
     };
 
-    struct Draw {
-        GrPrimitiveType         fPrimitiveType;
-        int                     fStartVertex;
-        int                     fStartIndex;
-        int                     fVertexCount;
-        int                     fIndexCount;
+    class DrawRecord : public DrawInfo {
+    public:
+        DrawRecord(const DrawInfo& info) : DrawInfo(info) {}
         GrVertexLayout          fVertexLayout;
         const GrVertexBuffer*   fVertexBuffer;
         const GrIndexBuffer*    fIndexBuffer;
@@ -175,16 +131,9 @@
     };
 
     // overrides from GrDrawTarget
-    virtual void onDrawIndexed(GrPrimitiveType primitiveType,
-                               int startVertex,
-                               int startIndex,
-                               int vertexCount,
-                               int indexCount) SK_OVERRIDE;
-    virtual void onDrawNonIndexed(GrPrimitiveType primitiveType,
-                                  int startVertex,
-                                  int vertexCount) SK_OVERRIDE;
+    virtual void onDraw(const DrawInfo&) SK_OVERRIDE;
     virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType) SK_OVERRIDE;
-    virtual bool onReserveVertexSpace(GrVertexLayout layout,
+    virtual bool onReserveVertexSpace(size_t vertexSize,
                                       int vertexCount,
                                       void** vertices) SK_OVERRIDE;
     virtual bool onReserveIndexSpace(int indexCount,
@@ -198,28 +147,28 @@
     virtual void releaseVertexArray() SK_OVERRIDE;
     virtual void releaseIndexArray() SK_OVERRIDE;
     virtual void geometrySourceWillPush() SK_OVERRIDE;
-    virtual void geometrySourceWillPop(
-        const GeometrySrcState& restoredState) SK_OVERRIDE;
-    virtual void clipWillBeSet(const GrClipData* newClip) SK_OVERRIDE;
+    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) SK_OVERRIDE;
+    virtual void willReserveVertexAndIndexSpace(size_t vertexSize,
+                                                int vertexCount,
+                                                int indexCount) SK_OVERRIDE;
+    bool quickInsideClip(const SkRect& devBounds);
 
-    // we lazily record state and clip changes in order to skip clips and states
-    // that have no effect.
+    // Attempts to concat instances from info onto the previous draw. info must represent an
+    // instanced draw. The caller must have already recorded a new draw state and clip if necessary.
+    int concatInstancedDraw(const DrawInfo& info);
+
+    // we lazily record state and clip changes in order to skip clips and states that have no
+    // effect.
     bool needsNewState() const;
     bool needsNewClip() const;
 
     // these functions record a command
     void            recordState();
-    void            recordDefaultState();
     void            recordClip();
-    void            recordDefaultClip();
-    Draw*           recordDraw();
+    DrawRecord*     recordDraw(const DrawInfo&);
     StencilPath*    recordStencilPath();
     Clear*          recordClear();
 
-    // call this to invalidate the tracking data that is used to concatenate
-    // multiple draws into a single draw.
-    void resetDrawTracking();
-
     enum {
         kCmdPreallocCnt          = 32,
         kDrawPreallocCnt         = 8,
@@ -230,11 +179,13 @@
         kGeoPoolStatePreAllocCnt = 4,
     };
 
-    SkSTArray<kCmdPreallocCnt, uint8_t, true>           fCmds;
-    GrSTAllocator<kDrawPreallocCnt, Draw>               fDraws;
-    GrSTAllocator<kStatePreallocCnt, StencilPath>       fStencilPaths;
-    GrSTAllocator<kStatePreallocCnt, GrDrawState>       fStates;
-    GrSTAllocator<kClearPreallocCnt, Clear>             fClears;
+    SkAutoTUnref<const GrGpu> fGpu;
+
+    SkSTArray<kCmdPreallocCnt, uint8_t, true>                          fCmds;
+    GrSTAllocator<kDrawPreallocCnt, DrawRecord>                        fDraws;
+    GrSTAllocator<kStatePreallocCnt, StencilPath>                      fStencilPaths;
+    GrSTAllocator<kStatePreallocCnt, GrDrawState::DeferredState>       fStates;
+    GrSTAllocator<kClearPreallocCnt, Clear>                            fClears;
 
     GrSTAllocator<kClipPreallocCnt, SkClipStack>        fClips;
     GrSTAllocator<kClipPreallocCnt, SkIPoint>           fClipOrigins;
@@ -243,26 +194,18 @@
 
     bool                            fClipSet;
 
+    enum ClipProxyState {
+        kUnknown_ClipProxyState,
+        kValid_ClipProxyState,
+        kInvalid_ClipProxyState
+    };
+    ClipProxyState                  fClipProxyState;
+    SkRect                          fClipProxy;
+
     GrVertexBufferAllocPool&        fVertexPool;
 
     GrIndexBufferAllocPool&         fIndexPool;
 
-    // these are used to attempt to concatenate drawRect calls
-    GrVertexLayout                  fLastRectVertexLayout;
-    const GrIndexBuffer*            fQuadIndexBuffer;
-    int                             fMaxQuads;
-    int                             fCurrQuad;
-
-    // bookkeeping to attempt to concantenate drawIndexedInstances calls
-    struct {
-        int            fVerticesPerInstance;
-        int            fIndicesPerInstance;
-        void reset() {
-            fVerticesPerInstance = 0;
-            fIndicesPerInstance = 0;
-        }
-    } fInstancedDrawTracker;
-
     struct GeometryPoolState {
         const GrVertexBuffer*           fPoolVertexBuffer;
         int                             fPoolStartVertex;
diff --git a/src/gpu/GrIndexBuffer.h b/src/gpu/GrIndexBuffer.h
index a7e7a57..6e556d2 100644
--- a/src/gpu/GrIndexBuffer.h
+++ b/src/gpu/GrIndexBuffer.h
@@ -15,17 +15,17 @@
 
 class GrIndexBuffer : public GrGeometryBuffer {
 public:
-        /**
-         * Retrieves the maximum number of quads that could be rendered
-         * from the index buffer (using kTriangles_GrPrimitiveType).
-         * @return the maximum number of quads using full size of index buffer.
-         */
-        int maxQuads() const {
-            return this->sizeInBytes() / (sizeof(uint16_t) * 6);
-        }
+    /**
+     * Retrieves the maximum number of quads that could be rendered
+     * from the index buffer (using kTriangles_GrPrimitiveType).
+     * @return the maximum number of quads using full size of index buffer.
+     */
+    int maxQuads() const {
+        return this->sizeInBytes() / (sizeof(uint16_t) * 6);
+    }
 protected:
-    GrIndexBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
-        : INHERITED(gpu, sizeInBytes, dynamic) {}
+    GrIndexBuffer(GrGpu* gpu, bool isWrapped, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, isWrapped, sizeInBytes, dynamic) {}
 private:
     typedef GrGeometryBuffer INHERITED;
 };
diff --git a/src/gpu/GrMemory.cpp b/src/gpu/GrMemory.cpp
index f6d3c7f..bf96b0b 100644
--- a/src/gpu/GrMemory.cpp
+++ b/src/gpu/GrMemory.cpp
@@ -24,5 +24,3 @@
         ::free(ptr);
     }
 }
-
-
diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp
index 17d70ab..fc777e0 100644
--- a/src/gpu/GrMemoryPool.cpp
+++ b/src/gpu/GrMemoryPool.cpp
@@ -119,6 +119,7 @@
 }
 
 void GrMemoryPool::validate() {
+#ifdef SK_DEBUG
     BlockHeader* block = fHead;
     BlockHeader* prev = NULL;
     GrAssert(block);
@@ -156,5 +157,5 @@
     } while ((block = block->fNext));
     GrAssert(allocCount == fAllocationCnt);
     GrAssert(prev == fTail);
+#endif
 }
-
diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h
index e8f0d58..ad3a5c3 100644
--- a/src/gpu/GrPath.h
+++ b/src/gpu/GrPath.h
@@ -16,7 +16,7 @@
 public:
     SK_DECLARE_INST_COUNT(GrPath);
 
-    GrPath(GrGpu* gpu) : INHERITED(gpu) {}
+    GrPath(GrGpu* gpu, bool isWrapped) : INHERITED(gpu, isWrapped) {}
 
     const GrRect& getBounds() const { return fBounds; }
 
diff --git a/src/gpu/GrPathRenderer.cpp b/src/gpu/GrPathRenderer.cpp
index 1daa6c9..e0d2682 100644
--- a/src/gpu/GrPathRenderer.cpp
+++ b/src/gpu/GrPathRenderer.cpp
@@ -12,4 +12,3 @@
 
 GrPathRenderer::GrPathRenderer() {
 }
-
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index fa2112c..ebd464f 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -179,4 +179,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index 4092959..d3d784c 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -251,7 +251,7 @@
         m.postConcat(UVpts);
 
         // The matrix should not have perspective.
-        static const SkScalar gTOL = SkFloatToScalar(1.f / 100.f);
+        SkDEBUGCODE(static const SkScalar gTOL = SkFloatToScalar(1.f / 100.f));
         GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp0)) < gTOL);
         GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp1)) < gTOL);
 
diff --git a/src/gpu/GrPlotMgr.h b/src/gpu/GrPlotMgr.h
index 6dd7114..4f79a21 100644
--- a/src/gpu/GrPlotMgr.h
+++ b/src/gpu/GrPlotMgr.h
@@ -74,4 +74,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrRectanizer.cpp b/src/gpu/GrRectanizer.cpp
index 652cc29..75ff92c 100644
--- a/src/gpu/GrRectanizer.cpp
+++ b/src/gpu/GrRectanizer.cpp
@@ -119,5 +119,3 @@
 GrRectanizer* GrRectanizer::Factory(int width, int height) {
     return SkNEW_ARGS(GrRectanizerPow2, (width, height));
 }
-
-
diff --git a/src/gpu/GrRectanizer.h b/src/gpu/GrRectanizer.h
index d1f5033..44d14a1 100644
--- a/src/gpu/GrRectanizer.h
+++ b/src/gpu/GrRectanizer.h
@@ -12,7 +12,6 @@
 #define GrRectanizer_DEFINED
 
 #include "GrRect.h"
-#include "GrTDArray.h"
 
 class GrRectanizerPurgeListener {
 public:
@@ -53,5 +52,3 @@
 };
 
 #endif
-
-
diff --git a/src/gpu/GrRectanizer_fifo.cpp b/src/gpu/GrRectanizer_fifo.cpp
index aab012d..b6ef1d9 100644
--- a/src/gpu/GrRectanizer_fifo.cpp
+++ b/src/gpu/GrRectanizer_fifo.cpp
@@ -119,5 +119,3 @@
 GrRectanizer* GrRectanizer::Factory(int width, int height) {
     return SkNEW_ARGS(GrRectanizerFIFO, (width, height));
 }
-
-
diff --git a/src/gpu/GrRedBlackTree.h b/src/gpu/GrRedBlackTree.h
index 5038bb0..2ccf77f 100644
--- a/src/gpu/GrRedBlackTree.h
+++ b/src/gpu/GrRedBlackTree.h
@@ -956,7 +956,7 @@
     // add 10K ints
     for (int i = 0; i < 10000; ++i) {
         int x = r.nextU()%100;
-        Iter xi = tree.insert(x);
+        SkDEBUGCODE(Iter xi = ) tree.insert(x);
         GrAssert(*xi == x);
         ++count[x];
     }
diff --git a/src/gpu/GrResource.cpp b/src/gpu/GrResource.cpp
index 2ff7df6..8fb21e8 100644
--- a/src/gpu/GrResource.cpp
+++ b/src/gpu/GrResource.cpp
@@ -12,14 +12,21 @@
 
 SK_DEFINE_INST_COUNT(GrResource)
 
-GrResource::GrResource(GrGpu* gpu) {
-    fGpu        = gpu;
-    fCacheEntry = NULL;
+GrResource::GrResource(GrGpu* gpu, bool isWrapped) {
+    fGpu              = gpu;
+    fCacheEntry       = NULL;
+    fDeferredRefCount = 0;
+    if (isWrapped) {
+        fFlags = kWrapped_Flag;
+    } else {
+        fFlags = 0;
+    }
     fGpu->insertResource(this);
 }
 
 GrResource::~GrResource() {
     // subclass should have released this.
+    GrAssert(0 == fDeferredRefCount);
     GrAssert(!this->isValid());
 }
 
@@ -54,4 +61,3 @@
         return NULL;
     }
 }
-
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index a1f1d79..45b999f 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -11,6 +11,20 @@
 #include "GrResourceCache.h"
 #include "GrResource.h"
 
+
+GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
+    static int32_t gNextType = 0;
+
+    int32_t type = sk_atomic_inc(&gNextType);
+    if (type >= (1 << 8 * sizeof(ResourceType))) {
+        GrCrash("Too many Resource Types");
+    }
+
+    return static_cast<ResourceType>(type);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource)
         : fKey(key), fResource(resource) {
     // we assume ownership of the resource, and will unref it when we die
@@ -33,36 +47,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class GrResourceCache::Key {
-    typedef GrResourceEntry T;
-
-    const GrResourceKey& fKey;
-public:
-    Key(const GrResourceKey& key) : fKey(key) {}
-
-    uint32_t getHash() const { return fKey.hashIndex(); }
-
-    static bool LT(const T& entry, const Key& key) {
-        return entry.key() < key.fKey;
-    }
-    static bool EQ(const T& entry, const Key& key) {
-        return entry.key() == key.fKey;
-    }
-#if GR_DEBUG
-    static uint32_t GetHash(const T& entry) {
-        return entry.key().hashIndex();
-    }
-    static bool LT(const T& a, const T& b) {
-        return a.key() < b.key();
-    }
-    static bool EQ(const T& a, const T& b) {
-        return a.key() == b.key();
-    }
-#endif
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
 GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) :
         fMaxCount(maxCount),
         fMaxBytes(maxBytes) {
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index e1207a2..dd8ba31 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -14,27 +14,12 @@
 #include "GrConfig.h"
 #include "GrTypes.h"
 #include "GrTHashCache.h"
+#include "GrBinHashKey.h"
 #include "SkTInternalLList.h"
 
 class GrResource;
+class GrResourceEntry;
 
-// return true if a<b, or false if b<a
-//
-#define RET_IF_LT_OR_GT(a, b)   \
-    do {                        \
-        if ((a) < (b)) {        \
-            return true;        \
-        }                       \
-        if ((b) < (a)) {        \
-            return false;       \
-        }                       \
-    } while (0)
-
-/**
- *  Helper class for GrResourceCache, the Key is used to identify src data for
- *  a resource. It is identified by 2 32bit data fields which can hold any
- *  data (uninterpreted by the cache) and a width/height.
- */
 class GrResourceKey {
 public:
     enum {
@@ -43,82 +28,119 @@
         kHashMask   = kHashCount - 1
     };
 
-    GrResourceKey(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) {
-        fP[0] = p0;
-        fP[1] = p1;
-        fP[2] = p2;
-        fP[3] = p3;
-        this->computeHashIndex();
+    static GrCacheID::Domain ScratchDomain() {
+        static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain();
+        return gDomain;
     }
 
-    GrResourceKey(uint32_t v[4]) {
-        memcpy(fP, v, 4 * sizeof(uint32_t));
-        this->computeHashIndex();
-    }
+    /** Uniquely identifies the GrResource subclass in the key to avoid collisions
+        across resource types. */
+    typedef uint8_t ResourceType;
+
+    /** Flags set by the GrResource subclass. */
+    typedef uint8_t ResourceFlags;
+
+    /** Generate a unique ResourceType */
+    static ResourceType GenerateResourceType();
+
+    /** Creates a key for resource */
+    GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
+        this->init(id.getDomain(), id.getKey(), type, flags);
+    };
 
     GrResourceKey(const GrResourceKey& src) {
-        memcpy(fP, src.fP, 4 * sizeof(uint32_t));
-#if GR_DEBUG
-        this->computeHashIndex();
-        GrAssert(fHashIndex == src.fHashIndex);
-#endif
-        fHashIndex = src.fHashIndex;
+        fKey = src.fKey;
+    }
+
+    GrResourceKey() {
+        fKey.fHashedKey.reset();
+    }
+
+    void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
+        this->init(id.getDomain(), id.getKey(), type, flags);
     }
 
     //!< returns hash value [0..kHashMask] for the key
-    int hashIndex() const { return fHashIndex; }
-
-    friend bool operator==(const GrResourceKey& a, const GrResourceKey& b) {
-        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
-        return 0 == memcmp(a.fP, b.fP, 4 * sizeof(uint32_t));
+    int getHash() const {
+        return fKey.fHashedKey.getHash() & kHashMask;
     }
 
-    friend bool operator!=(const GrResourceKey& a, const GrResourceKey& b) {
-        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
-        return !(a == b);
+    bool isScratch() const {
+        return ScratchDomain() ==
+            *reinterpret_cast<const GrCacheID::Domain*>(fKey.fHashedKey.getData() +
+                                                        kCacheIDDomainOffset);
     }
 
-    friend bool operator<(const GrResourceKey& a, const GrResourceKey& b) {
-        RET_IF_LT_OR_GT(a.fP[0], b.fP[0]);
-        RET_IF_LT_OR_GT(a.fP[1], b.fP[1]);
-        RET_IF_LT_OR_GT(a.fP[2], b.fP[2]);
-        return a.fP[3] < b.fP[3];
+    ResourceType getResourceType() const {
+        return *reinterpret_cast<const ResourceType*>(fKey.fHashedKey.getData() +
+                                                      kResourceTypeOffset);
     }
 
-    uint32_t getValue32(int i) const {
-        GrAssert(i >=0 && i < 4);
-        return fP[i];
+    ResourceFlags getResourceFlags() const {
+        return *reinterpret_cast<const ResourceFlags*>(fKey.fHashedKey.getData() +
+                                                       kResourceFlagsOffset);
     }
+
+    int compare(const GrResourceKey& other) const {
+        return fKey.fHashedKey.compare(other.fKey.fHashedKey);
+    }
+
+    static bool LT(const GrResourceKey& a, const GrResourceKey& b) {
+        return a.compare(b) < 0;
+    }
+
+    static bool EQ(const GrResourceKey& a, const GrResourceKey& b) {
+        return 0 == a.compare(b);
+    }
+
+    inline static bool LT(const GrResourceEntry& entry, const GrResourceKey& key);
+    inline static bool EQ(const GrResourceEntry& entry, const GrResourceKey& key);
+    inline static bool LT(const GrResourceEntry& a, const GrResourceEntry& b);
+    inline static bool EQ(const GrResourceEntry& a, const GrResourceEntry& b);
+
 private:
+    enum {
+        kCacheIDKeyOffset = 0,
+        kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key),
+        kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain),
+        kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType),
+        kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags),
+        kKeySize = SkAlign4(kPadOffset),
+        kPadSize = kKeySize - kPadOffset
+    };
 
-    static uint32_t rol(uint32_t x) {
-        return (x >> 24) | (x << 8);
-    }
-    static uint32_t ror(uint32_t x) {
-        return (x >> 8) | (x << 24);
-    }
-    static uint32_t rohalf(uint32_t x) {
-        return (x >> 16) | (x << 16);
+    void init(const GrCacheID::Domain domain,
+              const GrCacheID::Key& key,
+              ResourceType type,
+              ResourceFlags flags) {
+        union {
+            uint8_t  fKey8[kKeySize];
+            uint32_t fKey32[kKeySize / 4];
+        } keyData;
+
+        uint8_t* k = keyData.fKey8;
+        memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key));
+        memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain));
+        memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType));
+        memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags));
+        memset(k + kPadOffset, 0, kPadSize);
+        fKey.fHashedKey.setKeyData(keyData.fKey32);
     }
 
-    void computeHashIndex() {
-        uint32_t hash = fP[0] ^ rol(fP[1]) ^ ror(fP[2]) ^ rohalf(fP[3]);
-        // this way to mix and reduce hash to its index may have to change
-        // depending on how many bits we allocate to the index
-        hash ^= hash >> 16;
-        hash ^= hash >> 8;
-        fHashIndex = hash & kHashMask;
-    }
+    struct Key;
+    typedef GrTBinHashKey<Key, kKeySize> HashedKey;
 
-    uint32_t    fP[4];
+    struct Key {
+        int compare(const HashedKey& hashedKey) const {
+            return fHashedKey.compare(fHashedKey);
+        }
 
-    // this is computed from the fP... fields
-    int         fHashIndex;
+        HashedKey fHashedKey;
+    };
 
-    friend class GrContext;
+    Key fKey;
 };
 
-
 ///////////////////////////////////////////////////////////////////////////////
 
 class GrResourceEntry {
@@ -146,6 +168,22 @@
     friend class GrDLinkedList;
 };
 
+bool GrResourceKey::LT(const GrResourceEntry& entry, const GrResourceKey& key) {
+    return LT(entry.key(), key);
+}
+
+bool GrResourceKey::EQ(const GrResourceEntry& entry, const GrResourceKey& key) {
+    return EQ(entry.key(), key);
+}
+
+bool GrResourceKey::LT(const GrResourceEntry& a, const GrResourceEntry& b) {
+    return LT(a.key(), b.key());
+}
+
+bool GrResourceKey::EQ(const GrResourceEntry& a, const GrResourceEntry& b) {
+    return EQ(a.key(), b.key());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "GrTHashCache.h"
@@ -289,8 +327,7 @@
 
     void removeInvalidResource(GrResourceEntry* entry);
 
-    class Key;
-    GrTHashTable<GrResourceEntry, Key, 8> fCache;
+    GrTHashTable<GrResourceEntry, GrResourceKey, 8> fCache;
 
     // We're an internal doubly linked list
     typedef SkTInternalLList<GrResourceEntry> EntryList;
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 32a945b..c052091 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -22,7 +22,7 @@
 
     static const SkXfermode::Mode modeMap[] = {
         SkXfermode::kDstOut_Mode,   // kDifference_Op
-        SkXfermode::kMultiply_Mode, // kIntersect_Op
+        SkXfermode::kModulate_Mode, // kIntersect_Op
         SkXfermode::kSrcOver_Mode,  // kUnion_Op
         SkXfermode::kXor_Mode,      // kXOR_Op
         SkXfermode::kClear_Mode,    // kReverseDifference_Op
@@ -197,8 +197,7 @@
         kPathMaskStage = GrPaint::kTotalStages,
     };
     GrAssert(!drawState->isStageEnabled(kPathMaskStage));
-    drawState->stage(kPathMaskStage)->reset();
-    drawState->createTextureEffect(kPathMaskStage, texture);
+    drawState->createTextureEffect(kPathMaskStage, texture, SkMatrix::I());
     SkScalar w = SkIntToScalar(rect.width());
     SkScalar h = SkIntToScalar(rect.height());
     GrRect maskRect = GrRect::MakeWH(w / texture->width(),
@@ -214,4 +213,3 @@
     target->drawRect(dstRect, NULL, srcRects, NULL);
     drawState->disableStage(kPathMaskStage);
 }
-
diff --git a/src/gpu/GrStencilBuffer.cpp b/src/gpu/GrStencilBuffer.cpp
index 180912e..865961a 100644
--- a/src/gpu/GrStencilBuffer.cpp
+++ b/src/gpu/GrStencilBuffer.cpp
@@ -13,7 +13,6 @@
 #include "GrResourceCache.h"
 
 SK_DEFINE_INST_COUNT(GrStencilBuffer)
-GR_DEFINE_RESOURCE_CACHE_TYPE(GrStencilBuffer)
 
 void GrStencilBuffer::transferToCache() {
     GrAssert(NULL == this->getCacheEntry());
@@ -22,30 +21,29 @@
 }
 
 namespace {
-// we should never have more than one stencil buffer with same combo of
-// (width,height,samplecount)
-void gen_stencil_key_values(int width,
-                            int height,
-                            int sampleCnt,
-                            GrCacheID* cacheID) {
-    cacheID->fPublicID = GrCacheID::kDefaultPublicCacheID;
-    cacheID->fResourceSpecific32 = width | (height << 16);
-    cacheID->fDomain = GrCacheData::kScratch_ResourceDomain;
-
-    GrAssert(sampleCnt >= 0 && sampleCnt < 256);
-    cacheID->fResourceSpecific16 = sampleCnt << 8;
-
-    // last 8 bits of 'fResourceSpecific16' is free for flags
+// we should never have more than one stencil buffer with same combo of (width,height,samplecount)
+void gen_cache_id(int width, int height, int sampleCnt, GrCacheID* cacheID) {
+    static const GrCacheID::Domain gStencilBufferDomain = GrCacheID::GenerateDomain();
+    GrCacheID::Key key;
+    uint32_t* keyData = key.fData32;
+    keyData[0] = width;
+    keyData[1] = height;
+    keyData[2] = sampleCnt;
+    memset(keyData + 3, 0, sizeof(key) - 3 * sizeof(uint32_t));
+    GR_STATIC_ASSERT(sizeof(key) >= 3 * sizeof(uint32_t));
+    cacheID->reset(gStencilBufferDomain, key);
 }
 }
 
 GrResourceKey GrStencilBuffer::ComputeKey(int width,
                                           int height,
                                           int sampleCnt) {
-    GrCacheID id(GrStencilBuffer::GetResourceType());
-    gen_stencil_key_values(width, height, sampleCnt, &id);
+    // All SBs are created internally to attach to RTs so they all use the same domain.
+    static const GrResourceKey::ResourceType gStencilBufferResourceType =
+        GrResourceKey::GenerateResourceType();
+    GrCacheID id;
+    gen_cache_id(width, height, sampleCnt, &id);
 
-    uint32_t v[4];
-    id.toRaw(v);
-    return GrResourceKey(v);
+    // we don't use any flags for SBs currently.
+    return GrResourceKey(id, gStencilBufferResourceType, 0);
 }
diff --git a/src/gpu/GrStencilBuffer.h b/src/gpu/GrStencilBuffer.h
index e4e5190..3765a4c 100644
--- a/src/gpu/GrStencilBuffer.h
+++ b/src/gpu/GrStencilBuffer.h
@@ -12,7 +12,6 @@
 
 #include "GrClipData.h"
 #include "GrResource.h"
-#include "GrCacheID.h"
 
 class GrRenderTarget;
 class GrResourceEntry;
@@ -21,7 +20,6 @@
 class GrStencilBuffer : public GrResource {
 public:
     SK_DECLARE_INST_COUNT(GrStencilBuffer);
-    GR_DECLARE_RESOURCE_CACHE_TYPE()
 
     virtual ~GrStencilBuffer() {
         // TODO: allow SB to be purged and detach itself from rts
@@ -57,8 +55,8 @@
     static GrResourceKey ComputeKey(int width, int height, int sampleCnt);
 
 protected:
-    GrStencilBuffer(GrGpu* gpu, int width, int height, int bits, int sampleCnt)
-        : GrResource(gpu)
+    GrStencilBuffer(GrGpu* gpu, bool isWrapped, int width, int height, int bits, int sampleCnt)
+        : GrResource(gpu, isWrapped)
         , fWidth(width)
         , fHeight(height)
         , fBits(bits)
diff --git a/src/gpu/GrSurface.cpp b/src/gpu/GrSurface.cpp
index 3fd90d9..47d9959 100644
--- a/src/gpu/GrSurface.cpp
+++ b/src/gpu/GrSurface.cpp
@@ -8,4 +8,3 @@
 #include "GrSurface.h"
 
 SK_DEFINE_INST_COUNT(GrSurface)
-
diff --git a/src/gpu/GrTBSearch.h b/src/gpu/GrTBSearch.h
index 1d77c95..cee4f2a 100644
--- a/src/gpu/GrTBSearch.h
+++ b/src/gpu/GrTBSearch.h
@@ -43,4 +43,3 @@
 }
 
 #endif
-
diff --git a/src/gpu/GrTDArray.h b/src/gpu/GrTDArray.h
deleted file mode 100644
index 0e5b6bb..0000000
--- a/src/gpu/GrTDArray.h
+++ /dev/null
@@ -1,216 +0,0 @@
-
-/*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-
-#ifndef GrTDArray_DEFINED
-#define GrTDArray_DEFINED
-
-#include "GrTypes.h"
-#include "GrRefCnt.h"
-
-static int GrInitialArrayAllocationCount() {
-    return 4;
-}
-
-static int GrNextArrayAllocationCount(int count) {
-    return count + ((count + 1) >> 1);
-}
-
-template <typename T> class GrTDArray {
-public:
-    GrTDArray() : fArray(NULL), fAllocated(0), fCount(0) {}
-    GrTDArray(const GrTDArray& src) {
-        fCount = fAllocated = src.fCount;
-        fArray = (T*)GrMalloc(fAllocated * sizeof(T));
-        memcpy(fArray, src.fArray, fCount * sizeof(T));
-    }
-    ~GrTDArray() {
-        if (fArray) {
-            GrFree(fArray);
-        }
-    }
-
-    bool isEmpty() const { return 0 == fCount; }
-    int count() const { return fCount; }
-
-    const T& at(int index) const {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        return fArray[index];
-    }
-    T& at(int index) {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        return fArray[index];
-    }
-
-    const T& operator[](int index) const { return this->at(index); }
-    T& operator[](int index) { return this->at(index); }
-
-    GrTDArray& operator=(const GrTDArray& src) {
-        if (fAllocated < src.fCount) {
-            fAllocated = src.fCount;
-            GrFree(fArray);
-            fArray = (T*)GrMalloc(fAllocated * sizeof(T));
-        }
-        fCount = src.fCount;
-        memcpy(fArray, src.fArray, fCount * sizeof(T));
-        return *this;
-    }
-
-    void reset() {
-        if (fArray) {
-            GrFree(fArray);
-            fArray = NULL;
-        }
-        fAllocated = fCount = 0;
-    }
-
-    T* begin() const { return fArray; }
-    T* end() const { return fArray + fCount; }
-    T* back() const { GrAssert(fCount); return fArray + (fCount - 1); }
-
-    T* prepend() {
-        this->growAt(0);
-        return fArray;
-    }
-
-    T* append() {
-        this->growAt(fCount);
-        return fArray + fCount - 1;
-    }
-
-    /**
-     *  index may be [0..count], so that you can insert at the end (like append)
-     */
-    T* insert(int index) {
-        GrAssert((unsigned)index <= (unsigned)fCount);
-        this->growAt(index);
-        return fArray + index;
-    }
-
-    void remove(int index) {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        fCount -= 1;
-        if (index < fCount) {
-            int remaining = fCount - index;
-            memmove(fArray + index, fArray + index + 1, remaining * sizeof(T));
-        }
-    }
-
-    void removeShuffle(int index) {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        fCount -= 1;
-        if (index < fCount) {
-            memmove(fArray + index, fArray + fCount, sizeof(T));
-        }
-    }
-
-    // Utility iterators
-
-    /**
-     *  Calls GrFree() on each element. Assumes each is NULL or was allocated
-     *  with GrMalloc().
-     */
-    void freeAll() {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            GrFree(*curr);
-        }
-        this->reset();
-    }
-
-    /**
-     *  Calls delete on each element. Assumes each is NULL or was allocated
-     *  with new.
-     */
-    void deleteAll() {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            delete *curr;
-        }
-        this->reset();
-    }
-
-    /**
-     *  Calls GrSafeUnref() on each element. Assumes each is NULL or is a
-     *  subclass of GrRefCnt.
-     */
-    void unrefAll() {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            GrSafeUnref(*curr);
-        }
-        this->reset();
-    }
-
-    void visit(void visitor(T&)) const {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            if (*curr) {
-                visitor(*curr);
-            }
-        }
-    }
-
-    int find(const T& elem) const {
-        int count = this->count();
-        T* curr = this->begin();
-        for (int i = 0; i < count; i++) {
-            if (elem == curr[i]) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    friend bool operator==(const GrTDArray<T>& a, const GrTDArray<T>& b) {
-        return a.count() == b.count() &&
-               (0 == a.count() ||
-                0 == memcmp(a.begin(), b.begin(), a.count() * sizeof(T)));
-    }
-    friend bool operator!=(const GrTDArray<T>& a, const GrTDArray<T>& b) {
-        return !(a == b);
-    }
-
-private:
-    T*  fArray;
-    int fAllocated, fCount;
-
-    // growAt will increment fCount, reallocate fArray (as needed), and slide
-    // the contents of fArray to make a hole for new data at index.
-    void growAt(int index) {
-        GrAssert(fCount <= fAllocated);
-        if (0 == fAllocated) {
-            fAllocated = GrInitialArrayAllocationCount();
-            fArray = (T*)GrMalloc(fAllocated * sizeof(T));
-        } else if (fCount == fAllocated) {
-            fAllocated = GrNextArrayAllocationCount(fAllocated);
-            T* newArray = (T*)GrMalloc(fAllocated * sizeof(T));
-            memcpy(newArray, fArray, index * sizeof(T));
-            memcpy(newArray + index + 1, fArray + index,
-                   (fCount - index) * sizeof(T));
-            GrFree(fArray);
-            fArray = newArray;
-        } else {
-            // check that we're not just appending
-            if (index < fCount) {
-                memmove(fArray + index + 1, fArray + index,
-                        (fCount - index) * sizeof(T));
-            }
-        }
-        GrAssert(fCount < fAllocated);
-        fCount += 1;
-    }
-};
-
-extern void* GrTDArray_growAt(void*, int* allocated, int& count, int index,
-                              size_t);
-
-
-#endif
-
diff --git a/src/gpu/GrTHashCache.h b/src/gpu/GrTHashCache.h
index 4494f9f..b73b574 100644
--- a/src/gpu/GrTHashCache.h
+++ b/src/gpu/GrTHashCache.h
@@ -11,7 +11,8 @@
 #ifndef GrTHashCache_DEFINED
 #define GrTHashCache_DEFINED
 
-#include "GrTDArray.h"
+#include "GrTypes.h"
+#include "SkTDArray.h"
 
 // GrTDefaultFindFunctor implements the default find behavior for
 // GrTHashTable (i.e., return the first resource that matches the
@@ -58,7 +59,7 @@
 #endif
 
     // testing
-    const GrTDArray<T*>& getArray() const { return fSorted; }
+    const SkTDArray<T*>& getArray() const { return fSorted; }
 private:
     enum {
         kHashCount = 1 << kHashBits,
@@ -73,7 +74,7 @@
     }
 
     mutable T* fHash[kHashCount];
-    GrTDArray<T*> fSorted;
+    SkTDArray<T*> fSorted;
 
     // search fSorted, and return the found index, or ~index of where it
     // should be inserted
@@ -226,13 +227,6 @@
 #if GR_DEBUG
 template <typename T, typename Key, size_t kHashBits>
 void GrTHashTable<T, Key, kHashBits>::validate() const {
-    for (size_t i = 0; i < GR_ARRAY_COUNT(fHash); i++) {
-        if (fHash[i]) {
-            unsigned hashIndex = hash2Index(Key::GetHash(*fHash[i]));
-            GrAssert(hashIndex == i);
-        }
-    }
-
     int count = fSorted.count();
     for (int i = 1; i < count; i++) {
         GrAssert(Key::LT(*fSorted[i - 1], *fSorted[i]) ||
@@ -249,4 +243,3 @@
 #endif
 
 #endif
-
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
index fd7a689..97e92fa 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -30,8 +30,6 @@
     GrDrawState* drawState = fDrawTarget->drawState();
     if (fCurrVertex > 0) {
         // setup our sampler state for our text texture/atlas
-        drawState->stage(kGlyphMaskStage)->reset();
-
         GrAssert(GrIsALIGN4(fCurrVertex));
         GrAssert(fCurrTexture);
         GrTextureParams params(SkShader::kRepeat_TileMode, false);
@@ -97,8 +95,8 @@
     fMaxVertices = 0;
 
     fVertexLayout =
-        GrDrawTarget::kTextFormat_VertexLayoutBit |
-        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
+        GrDrawState::kTextFormat_VertexLayoutBit |
+        GrDrawState::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
 }
 
 GrTextContext::~GrTextContext() {
@@ -206,7 +204,7 @@
         // a number of verts to reserve and whether to perform a flush.
         fMaxVertices = kMinRequestedVerts;
         bool flush = (NULL != fDrawTarget) &&
-                     fDrawTarget->geometryHints(fVertexLayout,
+                     fDrawTarget->geometryHints(GrDrawState::VertexSize(fVertexLayout),
                                                 &fMaxVertices,
                                                 NULL);
         if (flush) {
@@ -216,7 +214,7 @@
         fDrawTarget = fContext->getTextTarget(fPaint);
         fMaxVertices = kDefaultRequestedVerts;
         // ignore return, no point in flushing again.
-        fDrawTarget->geometryHints(fVertexLayout,
+        fDrawTarget->geometryHints(GrDrawState::VertexSize(fVertexLayout),
                                    &fMaxVertices,
                                    NULL);
 
@@ -263,4 +261,3 @@
 #endif
     fCurrVertex += 4;
 }
-
diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp
index aebaaf8..4b4298a 100644
--- a/src/gpu/GrTextStrike.cpp
+++ b/src/gpu/GrTextStrike.cpp
@@ -149,7 +149,7 @@
 GrTextStrike::~GrTextStrike() {
     GrAtlas::FreeLList(fAtlas);
     fFontScalerKey->unref();
-    fCache.getArray().visit(FreeGlyph);
+    fCache.getArray().visitAll(FreeGlyph);
 
 #if GR_DEBUG
     gCounter -= 1;
@@ -207,5 +207,3 @@
     glyph->fAtlas = fAtlas = atlas;
     return true;
 }
-
-
diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h
index 701acea..25ced6d 100644
--- a/src/gpu/GrTextStrike.h
+++ b/src/gpu/GrTextStrike.h
@@ -112,4 +112,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrTextStrike_impl.h b/src/gpu/GrTextStrike_impl.h
index a3f37b3..48323bc 100644
--- a/src/gpu/GrTextStrike_impl.h
+++ b/src/gpu/GrTextStrike_impl.h
@@ -103,4 +103,3 @@
 }
 
 #endif
-
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index c31d774..614d771 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -15,7 +15,6 @@
 #include "GrResourceCache.h"
 
 SK_DEFINE_INST_COUNT(GrTexture)
-GR_DEFINE_RESOURCE_CACHE_TYPE(GrTexture)
 
 /**
  * This method allows us to interrupt the normal deletion process and place
@@ -116,97 +115,73 @@
     }
 }
 
-// These flags need to fit in <= 8 bits so they can be folded into the texture
+// These flags need to fit in a GrResourceKey::ResourceFlags so they can be folded into the texture
 // key
-enum TextureBits {
-    /*
-     * The kNPOT bit is set when the texture is NPOT and is being repeated
-     * but the hardware doesn't support that feature.
+enum TextureFlags {
+    /**
+     * The kStretchToPOT bit is set when the texture is NPOT and is being repeated but the
+     * hardware doesn't support that feature.
      */
-    kNPOT_TextureBit            = 0x1,
-    /*
-     * The kFilter bit can only be set when the kNPOT flag is set and indicates
-     * whether the resizing of the texture should use filtering. This is
-     * to handle cases where the original texture is indexed to disable
-     * filtering.
+    kStretchToPOT_TextureFlag = 0x1,
+    /**
+     * The kFilter bit can only be set when the kStretchToPOT flag is set and indicates whether the
+     * stretched texture should be bilerp filtered or point sampled.
      */
-    kFilter_TextureBit          = 0x2,
-    /*
-     * The kScratch bit is set if the texture is being used as a scratch
-     * texture.
-     */
-    kScratch_TextureBit         = 0x4,
+    kFilter_TextureFlag       = 0x2,
 };
 
 namespace {
-void gen_texture_key_values(const GrGpu* gpu,
-                            const GrTextureParams* params,
-                            const GrTextureDesc& desc,
-                            const GrCacheData& cacheData,
-                            bool scratch,
-                            GrCacheID* cacheID) {
-
-    uint64_t clientKey = cacheData.fClientCacheID;
-
-    if (scratch) {
-        // Instead of a client-provided key of the texture contents
-        // we create a key from the descriptor.
-        GrAssert(GrCacheData::kScratch_CacheID == clientKey);
-        clientKey = (desc.fFlags << 8) | ((uint64_t) desc.fConfig << 32);
-    }
-
-    cacheID->fPublicID = clientKey;
-    cacheID->fDomain = cacheData.fResourceDomain;
-
-    // we assume we only need 16 bits of width and height
-    // assert that texture creation will fail anyway if this assumption
-    // would cause key collisions.
-    GrAssert(gpu->getCaps().maxTextureSize() <= SK_MaxU16);
-    cacheID->fResourceSpecific32 = desc.fWidth | (desc.fHeight << 16);
-
-    GrAssert(desc.fSampleCnt >= 0 && desc.fSampleCnt < 256);
-    cacheID->fResourceSpecific16 = desc.fSampleCnt << 8;
-
-    if (!gpu->getCaps().npotTextureTileSupport()) {
-        bool isPow2 = GrIsPow2(desc.fWidth) && GrIsPow2(desc.fHeight);
-
-        bool tiled = NULL != params && params->isTiled();
-
-        if (tiled && !isPow2) {
-            cacheID->fResourceSpecific16 |= kNPOT_TextureBit;
+GrResourceKey::ResourceFlags get_texture_flags(const GrGpu* gpu,
+                                               const GrTextureParams* params,
+                                               const GrTextureDesc& desc) {
+    GrResourceKey::ResourceFlags flags = 0;
+    bool tiled = NULL != params && params->isTiled();
+    if (tiled && !gpu->getCaps().npotTextureTileSupport()) {
+        if (!GrIsPow2(desc.fWidth) || !GrIsPow2(desc.fHeight)) {
+            flags |= kStretchToPOT_TextureFlag;
             if (params->isBilerp()) {
-                cacheID->fResourceSpecific16 |= kFilter_TextureBit;
+                flags |= kFilter_TextureFlag;
             }
         }
     }
+    return flags;
+}
 
-    if (scratch) {
-        cacheID->fResourceSpecific16 |= kScratch_TextureBit;
-    }
+GrResourceKey::ResourceType texture_resource_type() {
+    static const GrResourceKey::ResourceType gType = GrResourceKey::GenerateResourceType();
+    return gType;
 }
 }
 
 GrResourceKey GrTexture::ComputeKey(const GrGpu* gpu,
                                     const GrTextureParams* params,
                                     const GrTextureDesc& desc,
-                                    const GrCacheData& cacheData,
-                                    bool scratch) {
-    GrCacheID id(GrTexture::GetResourceType());
-    gen_texture_key_values(gpu, params, desc, cacheData, scratch, &id);
+                                    const GrCacheID& cacheID) {
+    GrResourceKey::ResourceFlags flags = get_texture_flags(gpu, params, desc);
+    return GrResourceKey(cacheID, texture_resource_type(), flags);
+}
 
-    uint32_t v[4];
-    id.toRaw(v);
-    return GrResourceKey(v);
+GrResourceKey GrTexture::ComputeScratchKey(const GrTextureDesc& desc) {
+    GrCacheID::Key idKey;
+    // Instead of a client-provided key of the texture contents we create a key from the
+    // descriptor.
+    GR_STATIC_ASSERT(sizeof(idKey) >= 12);
+    GrAssert(desc.fHeight < (1 << 16));
+    GrAssert(desc.fWidth < (1 << 16));
+    idKey.fData32[0] = (desc.fWidth) | (desc.fHeight << 16);
+    idKey.fData32[1] = desc.fConfig | desc.fSampleCnt << 16;
+    idKey.fData32[2] = desc.fFlags;
+    static const int kPadSize = sizeof(idKey) - 12;
+    memset(idKey.fData8 + 12, 0, kPadSize);
+
+    GrCacheID cacheID(GrResourceKey::ScratchDomain(), idKey);
+    return GrResourceKey(cacheID, texture_resource_type(), 0);
 }
 
 bool GrTexture::NeedsResizing(const GrResourceKey& key) {
-    return 0 != (key.getValue32(3) & kNPOT_TextureBit);
-}
-
-bool GrTexture::IsScratchTexture(const GrResourceKey& key) {
-    return 0 != (key.getValue32(3) & kScratch_TextureBit);
+    return SkToBool(key.getResourceFlags() & kStretchToPOT_TextureFlag);
 }
 
 bool GrTexture::NeedsFiltering(const GrResourceKey& key) {
-    return 0 != (key.getValue32(3) & kFilter_TextureBit);
+    return SkToBool(key.getResourceFlags() & kFilter_TextureFlag);
 }
diff --git a/src/gpu/GrVertexBuffer.h b/src/gpu/GrVertexBuffer.h
index bda235c..b53cbf0 100644
--- a/src/gpu/GrVertexBuffer.h
+++ b/src/gpu/GrVertexBuffer.h
@@ -15,8 +15,8 @@
 
 class GrVertexBuffer : public GrGeometryBuffer {
 protected:
-    GrVertexBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
-        : INHERITED(gpu, sizeInBytes, dynamic) {}
+    GrVertexBuffer(GrGpu* gpu, bool isWrapped, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, isWrapped, sizeInBytes, dynamic) {}
 private:
     typedef GrGeometryBuffer INHERITED;
 };
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 7b06d60..18f66b5 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -8,6 +8,7 @@
 #include "SkGpuDevice.h"
 
 #include "effects/GrTextureDomainEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
 
 #include "GrContext.h"
 #include "GrTextContext.h"
@@ -95,7 +96,7 @@
 
     ~SkAutoCachedTexture() {
         if (NULL != fTexture) {
-            GrUnlockCachedBitmapTexture(fTexture);
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
         }
     }
 
@@ -103,14 +104,14 @@
                    const SkBitmap& bitmap,
                    const GrTextureParams* params) {
         if (NULL != fTexture) {
-            GrUnlockCachedBitmapTexture(fTexture);
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
             fTexture = NULL;
         }
         fDevice = device;
         GrTexture* result = (GrTexture*)bitmap.getTexture();
         if (NULL == result) {
             // Cannot return the native texture so look it up in our cache
-            fTexture = GrLockCachedBitmapTexture(device->context(), bitmap, params);
+            fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
             result = fTexture;
         }
         return result;
@@ -347,8 +348,7 @@
 }
 
 namespace {
-void purgeClipCB(int genID, void* data) {
-    GrContext* context = (GrContext*) data;
+void purgeClipCB(int genID, void* ) {
 
     if (SkClipStack::kInvalidGenID == genID ||
         SkClipStack::kEmptyGenID == genID ||
@@ -444,7 +444,7 @@
     GrTexture* texture = fRenderTarget->asTexture();
     if (NULL != texture) {
         paint->colorStage(kBitmapTextureIdx)->setEffect(
-            SkNEW_ARGS(GrSingleTextureEffect, (texture)))->unref();
+            GrSimpleTextureEffect::Create(texture, SkMatrix::I()))->unref();
         return true;
     }
     return false;
@@ -475,7 +475,6 @@
                                     const SkPaint& skPaint,
                                     bool justAlpha,
                                     bool constantColor,
-                                    SkGpuDevice::SkAutoCachedTexture* act,
                                     GrPaint* grPaint) {
 
     grPaint->setDither(skPaint.isDither());
@@ -514,7 +513,7 @@
             SkColor filtered = colorFilter->filterColor(skPaint.getColor());
             grPaint->setColor(SkColor2GrColor(filtered));
         } else {
-            SkAutoTUnref<GrEffect> effect(colorFilter->asNewEffect(dev->context()));
+            SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));
             if (NULL != effect.get()) {
                 grPaint->colorStage(kColorFilterTextureIdx)->setEffect(effect);
             } else {
@@ -538,87 +537,43 @@
 inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
                                   const SkPaint& skPaint,
                                   bool constantColor,
-                                  SkGpuDevice::SkAutoCachedTexture textures[GrPaint::kMaxColorStages],
                                   GrPaint* grPaint) {
     SkShader* shader = skPaint.getShader();
     if (NULL == shader) {
-        return skPaint2GrPaintNoShader(dev,
-                                       skPaint,
-                                       false,
-                                       constantColor,
-                                       &textures[kColorFilterTextureIdx],
-                                       grPaint);
-    } else if (!skPaint2GrPaintNoShader(dev, skPaint, true, false,
-                                        &textures[kColorFilterTextureIdx], grPaint)) {
+        return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);
+    } else if (!skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint)) {
         return false;
     }
 
-    GrEffectStage* stage = grPaint->colorStage(kShaderTextureIdx);
-    if (shader->asNewEffect(dev->context(), stage)) {
+    SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));
+    if (NULL != effect.get()) {
+        grPaint->colorStage(kShaderTextureIdx)->setEffect(effect);
         return true;
     }
 
-    SkBitmap bitmap;
-    SkMatrix matrix;
-    SkShader::TileMode tileModes[2];
-    SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, &matrix, tileModes);
+    // We still don't have SkColorShader::asNewEffect() implemented.
+    SkShader::GradientInfo info;
+    SkColor                color;
 
-    if (SkShader::kNone_BitmapType == bmptype) {
-        SkShader::GradientInfo info;
-        SkColor                color;
-
-        info.fColors = &color;
-        info.fColorOffsets = NULL;
-        info.fColorCount = 1;
-        if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
-            SkPaint copy(skPaint);
-            copy.setShader(NULL);
-            // modulate the paint alpha by the shader's solid color alpha
-            U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
-            copy.setColor(SkColorSetA(color, newA));
-            return skPaint2GrPaintNoShader(dev,
-                                           copy,
-                                           false,
-                                           constantColor,
-                                           &textures[kColorFilterTextureIdx],
-                                           grPaint);
-        }
-        return false;
+    info.fColors = &color;
+    info.fColorOffsets = NULL;
+    info.fColorCount = 1;
+    if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
+        SkPaint copy(skPaint);
+        copy.setShader(NULL);
+        // modulate the paint alpha by the shader's solid color alpha
+        U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
+        copy.setColor(SkColorSetA(color, newA));
+        return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);
     }
-
-    // since our texture coords will be in local space, we whack the texture
-    // matrix to map them back into 0...1 before we load it
-    if (shader->hasLocalMatrix()) {
-        SkMatrix inverse;
-        if (!shader->getLocalMatrix().invert(&inverse)) {
-            return false;
-        }
-        matrix.preConcat(inverse);
-    }
-
-    // Must set wrap and filter on the sampler before requesting a texture.
-    GrTextureParams params(tileModes, skPaint.isFilterBitmap());
-    GrTexture* texture = textures[kShaderTextureIdx].set(dev, bitmap, &params);
-
-    if (NULL == texture) {
-        SkDebugf("Couldn't convert bitmap to texture.\n");
-        return false;
-    }
-
-    if (SkShader::kDefault_BitmapType == bmptype) {
-        SkScalar sx = SkFloatToScalar(1.f / bitmap.width());
-        SkScalar sy = SkFloatToScalar(1.f / bitmap.height());
-        matrix.postScale(sx, sy);
-    }
-    stage->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix, params)))->unref();
-
-    return true;
+    return false;
 }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 void SkGpuDevice::clear(SkColor color) {
-    fContext->clear(NULL, SkColor2GrColor(color), fRenderTarget);
+    SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
+    fContext->clear(&rect, SkColor2GrColor(color), fRenderTarget);
     fNeedClear = false;
 }
 
@@ -626,12 +581,7 @@
     CHECK_SHOULD_DRAW(draw, false);
 
     GrPaint grPaint;
-    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
-    if (!skPaint2GrPaintShader(this,
-                               paint,
-                               true,
-                               textures,
-                               &grPaint)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
 
@@ -662,12 +612,7 @@
     }
 
     GrPaint grPaint;
-    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
-    if (!skPaint2GrPaintShader(this,
-                               paint,
-                               true,
-                               textures,
-                               &grPaint)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
 
@@ -724,17 +669,41 @@
     }
 
     GrPaint grPaint;
-    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
-    if (!skPaint2GrPaintShader(this,
-                               paint,
-                               true,
-                               textures,
-                               &grPaint)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
     fContext->drawRect(grPaint, rect, doStroke ? width : -1);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
+                           const SkPaint& paint) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    bool usePath = false;
+    // some basic reasons we might need to call drawPath...
+    if (paint.getMaskFilter() || paint.getPathEffect()) {
+        usePath = true;
+    }
+
+    if (usePath) {
+        SkPath path;
+        path.addOval(oval);
+        this->drawPath(draw, path, paint, NULL, true);
+        return;
+    }
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+    SkStrokeRec stroke(paint);
+
+    fContext->drawOval(grPaint, oval, stroke);
+}
+
 #include "SkMaskFilter.h"
 #include "SkBounder.h"
 
@@ -858,7 +827,8 @@
             matrix.setIDiv(pathTexture->width(), pathTexture->height());
             // Blend pathTexture over blurTexture.
             context->setRenderTarget(blurTexture->asRenderTarget());
-            paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (pathTexture, matrix)))->unref();
+            paint.colorStage(0)->setEffect(
+                GrSimpleTextureEffect::Create(pathTexture, matrix))->unref();
             if (SkMaskFilter::kInner_BlurType == blurType) {
                 // inner:  dst = dst * src
                 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
@@ -889,7 +859,8 @@
     matrix.postIDiv(blurTexture->width(), blurTexture->height());
 
     grp->coverageStage(MASK_IDX)->reset();
-    grp->coverageStage(MASK_IDX)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (blurTexture, matrix)))->unref();
+    grp->coverageStage(MASK_IDX)->setEffect(
+        GrSimpleTextureEffect::Create(blurTexture, matrix))->unref();
     context->drawRect(*grp, finalRect);
     return true;
 }
@@ -945,7 +916,7 @@
     m.setTranslate(-dstM.fBounds.fLeft*SK_Scalar1, -dstM.fBounds.fTop*SK_Scalar1);
     m.postIDiv(texture->width(), texture->height());
 
-    grp->coverageStage(MASK_IDX)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, m)))->unref();
+    grp->coverageStage(MASK_IDX)->setEffect(GrSimpleTextureEffect::Create(texture, m))->unref();
     GrRect d;
     d.setLTRB(SkIntToScalar(dstM.fBounds.fLeft),
               SkIntToScalar(dstM.fBounds.fTop),
@@ -967,16 +938,11 @@
     CHECK_SHOULD_DRAW(draw, false);
 
     GrPaint grPaint;
-    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
-    if (!skPaint2GrPaintShader(this,
-                               paint,
-                               true,
-                               textures,
-                               &grPaint)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
 
-    // can we cheat, and threat a thin stroke as a hairline w/ coverage
+    // can we cheat, and treat a thin stroke as a hairline w/ coverage
     // if we can, we draw lots faster (raster device does this same test)
     SkScalar hairlineCoverage;
     bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
@@ -1007,7 +973,9 @@
 
     SkStrokeRec stroke(paint);
     SkPathEffect* pathEffect = paint.getPathEffect();
-    if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke)) {
+    const SkRect* cullRect = NULL;  // TODO: what is our bounds?
+    if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke,
+                                             cullRect)) {
         pathPtr = &effectPath;
     }
 
@@ -1104,7 +1072,7 @@
         return false;
     }
     // if the entire texture is already in our cache then no reason to tile it
-    if (this->isBitmapInTextureCache(bitmap, params)) {
+    if (GrIsBitmapInCache(fContext, bitmap, &params)) {
         return false;
     }
 
@@ -1200,8 +1168,9 @@
     }
 
     GrPaint grPaint;
-    SkAutoCachedTexture colorLutTexture;
-    if (!skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
+
+    bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());
+    if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {
         return;
     }
     GrTextureParams params;
@@ -1340,8 +1309,6 @@
         return;
     }
 
-    GrEffectStage* stage = grPaint->colorStage(kBitmapTextureIdx);
-
     GrTexture* texture;
     SkAutoCachedTexture act(this, bitmap, &params, &texture);
     if (NULL == texture) {
@@ -1381,7 +1348,7 @@
     }
 
     GrRect textureDomain = GrRect::MakeEmpty();
-    SkAutoTUnref<GrEffect> effect;
+    SkAutoTUnref<GrEffectRef> effect;
     if (needsTextureDomain) {
         // Use a constrained texture domain to avoid color bleeding
         SkScalar left, top, right, bottom;
@@ -1406,7 +1373,7 @@
                                                    GrTextureDomainEffect::kClamp_WrapMode,
                                                    params.isBilerp()));
     } else {
-        effect.reset(SkNEW_ARGS(GrSingleTextureEffect, (texture, params)));
+        effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
     }
     grPaint->colorStage(kBitmapTextureIdx)->setEffect(effect);
     fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
@@ -1418,7 +1385,7 @@
                   GrTexture* srcTexture,
                   GrTexture* dstTexture,
                   const GrRect& rect,
-                  GrEffect* effect) {
+                  GrEffectRef* effect) {
     SkASSERT(srcTexture && srcTexture->getContext() == context);
     GrContext::AutoMatrix am;
     am.setIdentity(context);
@@ -1432,31 +1399,44 @@
 
 };
 
-static GrTexture* filter_texture(SkDevice* device, GrContext* context,
-                                 GrTexture* texture, SkImageFilter* filter,
-                                 const GrRect& rect) {
+static SkBitmap wrap_texture(GrTexture* texture) {
+    SkBitmap result;
+    bool dummy;
+    SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);
+    result.setConfig(config, texture->width(), texture->height());
+    result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
+    return result;
+}
+
+static bool filter_texture(SkDevice* device, GrContext* context,
+                           GrTexture* texture, SkImageFilter* filter,
+                           int w, int h, SkBitmap* result) {
     GrAssert(filter);
     SkDeviceImageFilterProxy proxy(device);
 
     GrTextureDesc desc;
     desc.fFlags = kRenderTarget_GrTextureFlagBit,
-    desc.fWidth = SkScalarCeilToInt(rect.width());
-    desc.fHeight = SkScalarCeilToInt(rect.height());
+    desc.fWidth = w;
+    desc.fHeight = h;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
-    GrEffect* effect;
+    GrEffectRef* effect;
 
     if (filter->canFilterImageGPU()) {
         // Save the render target and set it to NULL, so we don't accidentally draw to it in the
         // filter.  Also set the clip wide open and the matrix to identity.
         GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
-        texture = filter->onFilterImageGPU(&proxy, texture, rect);
+        return filter->filterImageGPU(&proxy, wrap_texture(texture), result);
     } else if (filter->asNewEffect(&effect, texture)) {
         GrAutoScratchTexture dst(context, desc);
-        apply_effect(context, texture, dst.texture(), rect, effect);
-        texture = dst.detach();
+        SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h));
+        apply_effect(context, texture, dst.texture(), r, effect);
+        SkAutoTUnref<GrTexture> resultTex(dst.detach());
         effect->unref();
+        *result = wrap_texture(resultTex.get());
+        return true;
+    } else {
+        return false;
     }
-    return texture;
 }
 
 void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
@@ -1473,8 +1453,7 @@
     int h = bitmap.height();
 
     GrPaint grPaint;
-    SkAutoCachedTexture colorLutTexture;
-    if(!skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
+    if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
         return;
     }
 
@@ -1484,18 +1463,18 @@
     stage->reset();
     // draw sprite uses the default texture params
     SkAutoCachedTexture act(this, bitmap, NULL, &texture);
-    grPaint.colorStage(kBitmapTextureIdx)->setEffect(SkNEW_ARGS
-        (GrSingleTextureEffect, (texture)))->unref();
+    grPaint.colorStage(kBitmapTextureIdx)->setEffect(
+        GrSimpleTextureEffect::Create(texture, SkMatrix::I()))->unref();
 
     SkImageFilter* filter = paint.getImageFilter();
     if (NULL != filter) {
-        GrTexture* filteredTexture = filter_texture(this, fContext, texture, filter,
-                 GrRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)));
-        if (filteredTexture) {
-            grPaint.colorStage(kBitmapTextureIdx)->setEffect(SkNEW_ARGS
-                (GrSingleTextureEffect, (filteredTexture)))->unref();
-            texture = filteredTexture;
-            filteredTexture->unref();
+        SkBitmap filterBitmap;
+        if (filter_texture(this, fContext, texture, filter, w, h, &filterBitmap)) {
+            grPaint.colorStage(kBitmapTextureIdx)->setEffect(
+                GrSimpleTextureEffect::Create((GrTexture*) filterBitmap.getTexture(), SkMatrix::I()))->unref();
+            texture = (GrTexture*) filterBitmap.getTexture();
+            w = filterBitmap.width();
+            h = filterBitmap.height();
         }
     }
 
@@ -1551,33 +1530,31 @@
     CHECK_SHOULD_DRAW(draw, true);
 
     GrPaint grPaint;
-    SkAutoCachedTexture colorLutTexture;
     grPaint.colorStage(kBitmapTextureIdx)->reset();
     if (!dev->bindDeviceAsTexture(&grPaint) ||
-        !skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
+        !skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
         return;
     }
 
-    GrTexture* devTex = grPaint.getColorStage(kBitmapTextureIdx).getEffect()->texture(0);
+    GrTexture* devTex = (*grPaint.getColorStage(kBitmapTextureIdx).getEffect())->texture(0);
     SkASSERT(NULL != devTex);
 
-    SkImageFilter* filter = paint.getImageFilter();
-    if (NULL != filter) {
-        GrRect rect = GrRect::MakeWH(SkIntToScalar(devTex->width()),
-                                     SkIntToScalar(devTex->height()));
-        GrTexture* filteredTexture = filter_texture(this, fContext, devTex, filter, rect);
-        if (filteredTexture) {
-            grPaint.colorStage(kBitmapTextureIdx)->setEffect(SkNEW_ARGS
-                (GrSingleTextureEffect, (filteredTexture)))->unref();
-            devTex = filteredTexture;
-            filteredTexture->unref();
-        }
-    }
-
     const SkBitmap& bm = dev->accessBitmap(false);
     int w = bm.width();
     int h = bm.height();
 
+    SkImageFilter* filter = paint.getImageFilter();
+    if (NULL != filter) {
+        SkBitmap filterBitmap;
+        if (filter_texture(this, fContext, devTex, filter, w, h, &filterBitmap)) {
+            grPaint.colorStage(kBitmapTextureIdx)->setEffect(
+                GrSimpleTextureEffect::Create((GrTexture*) filterBitmap.getTexture(), SkMatrix::I()))->unref();
+            devTex = (GrTexture*) filterBitmap.getTexture();
+            w = filterBitmap.width();
+            h = filterBitmap.height();
+        }
+    }
+
     GrRect dstRect = GrRect::MakeXYWH(SkIntToScalar(x),
                                       SkIntToScalar(y),
                                       SkIntToScalar(w),
@@ -1619,16 +1596,7 @@
     // must be pushed upstack.
     SkAutoCachedTexture act(this, src, NULL, &texture);
 
-    result->setConfig(src.config(), src.width(), src.height());
-    GrRect rect = GrRect::MakeWH(SkIntToScalar(src.width()),
-                                 SkIntToScalar(src.height()));
-    GrTexture* resultTexture = filter_texture(this, fContext, texture, filter, rect);
-    if (resultTexture) {
-        result->setPixelRef(SkNEW_ARGS(SkGrTexturePixelRef,
-                                       (resultTexture)))->unref();
-        resultTexture->unref();
-    }
-    return true;
+    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), result);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1649,29 +1617,19 @@
     CHECK_SHOULD_DRAW(draw, false);
 
     GrPaint grPaint;
-    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
     // we ignore the shader if texs is null.
     if (NULL == texs) {
-        if (!skPaint2GrPaintNoShader(this,
-                                     paint,
-                                     false,
-                                     NULL == colors,
-                                     &textures[kColorFilterTextureIdx],
-                                     &grPaint)) {
+        if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {
             return;
         }
     } else {
-        if (!skPaint2GrPaintShader(this,
-                                   paint,
-                                   NULL == colors,
-                                   textures,
-                                   &grPaint)) {
+        if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {
             return;
         }
     }
 
     if (NULL != xmode && NULL != texs && NULL != colors) {
-        if (!SkXfermode::IsMode(xmode, SkXfermode::kMultiply_Mode)) {
+        if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {
             SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
 #if 0
             return
@@ -1764,12 +1722,7 @@
         SkDraw myDraw(draw);
 
         GrPaint grPaint;
-        SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
-        if (!skPaint2GrPaintShader(this,
-                                   paint,
-                                   true,
-                                   textures,
-                                   &grPaint)) {
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
             return;
         }
         GrTextContext context(fContext, grPaint);
@@ -1792,12 +1745,7 @@
         SkDraw myDraw(draw);
 
         GrPaint grPaint;
-        SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
-        if (!skPaint2GrPaintShader(this,
-                                   paint,
-                                   true,
-                                   textures,
-                                   &grPaint)) {
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
             return;
         }
         GrTextContext context(fContext, grPaint);
@@ -1848,22 +1796,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkGpuDevice::isBitmapInTextureCache(const SkBitmap& bitmap,
-                                         const GrTextureParams& params) const {
-    uint64_t key = bitmap.getGenerationID();
-    key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
-
-    GrTextureDesc desc;
-    desc.fWidth = bitmap.width();
-    desc.fHeight = bitmap.height();
-    desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
-
-    GrCacheData cacheData(key);
-
-    return this->context()->isTextureInCache(desc, cacheData, &params);
-}
-
-
 SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
                                                 int width, int height,
                                                 bool isOpaque,
@@ -1875,29 +1807,24 @@
     desc.fHeight = height;
     desc.fSampleCnt = fRenderTarget->numSamples();
 
-    GrTexture* texture;
-    SkAutoTUnref<GrTexture> tunref;
+    SkAutoTUnref<GrTexture> texture;
     // Skia's convention is to only clear a device if it is non-opaque.
     bool needClear = !isOpaque;
 
 #if CACHE_COMPATIBLE_DEVICE_TEXTURES
     // layers are never draw in repeat modes, so we can request an approx
     // match and ignore any padding.
-    GrContext::ScratchTexMatch matchType = (kSaveLayer_Usage == usage) ?
-                                    GrContext::kApprox_ScratchTexMatch :
-                                    GrContext::kExact_ScratchTexMatch;
-    texture = fContext->lockScratchTexture(desc, matchType);
+    const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
+                                                GrContext::kApprox_ScratchTexMatch :
+                                                GrContext::kExact_ScratchTexMatch;
+    texture.reset(fContext->lockAndRefScratchTexture(desc, match));
 #else
-    tunref.reset(fContext->createUncachedTexture(desc, NULL, 0));
-    texture = tunref.get();
+    texture.reset(fContext->createUncachedTexture(desc, NULL, 0));
 #endif
-    if (texture) {
-        return SkNEW_ARGS(SkGpuDevice,(fContext,
-                                       texture,
-                                       needClear));
+    if (NULL != texture.get()) {
+        return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));
     } else {
-        GrPrintf("---- failed to create compatible device texture [%d %d]\n",
-                    width, height);
+        GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height);
         return NULL;
     }
 }
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 79bc75d..2eb3800 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -56,8 +56,36 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+static void generate_bitmap_cache_id(const SkBitmap& bitmap, GrCacheID* id) {
+    // Our id includes the offset, width, and height so that bitmaps created by extractSubset()
+    // are unique.
+    uint32_t genID = bitmap.getGenerationID();
+    size_t offset = bitmap.pixelRefOffset();
+    int16_t width = static_cast<int16_t>(bitmap.width());
+    int16_t height = static_cast<int16_t>(bitmap.height());
+
+    GrCacheID::Key key;
+    memcpy(key.fData8, &genID, 4);
+    memcpy(key.fData8 + 4, &width, 2);
+    memcpy(key.fData8 + 6, &height, 2);
+    memcpy(key.fData8 + 8, &offset, sizeof(size_t));
+    static const size_t kKeyDataSize = 8 + sizeof(size_t);
+    memset(key.fData8 + kKeyDataSize, 0, sizeof(key) - kKeyDataSize);
+    GR_STATIC_ASSERT(sizeof(key) >= 8 + sizeof(size_t));
+    static const GrCacheID::Domain gBitmapTextureDomain = GrCacheID::GenerateDomain();
+    id->reset(gBitmapTextureDomain, key);
+}
+
+static void generate_bitmap_texture_desc(const SkBitmap& bitmap, GrTextureDesc* desc) {
+    desc->fFlags = kNone_GrTextureFlags;
+    desc->fWidth = bitmap.width();
+    desc->fHeight = bitmap.height();
+    desc->fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
+    desc->fSampleCnt = 0;
+}
+
 static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
-                                              uint64_t key,
+                                              bool cache,
                                               const GrTextureParams* params,
                                               const SkBitmap& origBitmap) {
     SkAutoLockPixels alp(origBitmap);
@@ -71,11 +99,7 @@
     const SkBitmap* bitmap = &origBitmap;
 
     GrTextureDesc desc;
-    desc.fWidth = bitmap->width();
-    desc.fHeight = bitmap->height();
-    desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap->config());
-
-    GrCacheData cacheData(key);
+    generate_bitmap_texture_desc(*bitmap, &desc);
 
     if (SkBitmap::kIndex8_Config == bitmap->config()) {
         // build_compressed_data doesn't do npot->pot expansion
@@ -91,41 +115,38 @@
             // our compressed data will be trimmed, so pass width() for its
             // "rowBytes", since they are the same now.
 
-            if (GrCacheData::kScratch_CacheID != key) {
-                return ctx->createTexture(params, desc, cacheData,
-                                          storage.get(),
-                                          bitmap->width());
+            if (cache) {
+                GrCacheID cacheID;
+                generate_bitmap_cache_id(origBitmap, &cacheID);
+                return ctx->createTexture(params, desc, cacheID, storage.get(), bitmap->width());
             } else {
-                GrTexture* result = ctx->lockScratchTexture(desc,
-                                          GrContext::kExact_ScratchTexMatch);
+                GrTexture* result = ctx->lockAndRefScratchTexture(desc,
+                                                            GrContext::kExact_ScratchTexMatch);
                 result->writePixels(0, 0, bitmap->width(),
                                     bitmap->height(), desc.fConfig,
                                     storage.get());
                 return result;
             }
-
         } else {
             origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
             // now bitmap points to our temp, which has been promoted to 32bits
             bitmap = &tmpBitmap;
+            desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap->config());
         }
     }
 
-    desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap->config());
-    if (GrCacheData::kScratch_CacheID != key) {
+    if (cache) {
         // This texture is likely to be used again so leave it in the cache
-        // but locked.
-        return ctx->createTexture(params, desc, cacheData,
-                                  bitmap->getPixels(),
-                                  bitmap->rowBytes());
+        GrCacheID cacheID;
+        generate_bitmap_cache_id(origBitmap, &cacheID);
+        return ctx->createTexture(params, desc, cacheID, bitmap->getPixels(), bitmap->rowBytes());
     } else {
         // This texture is unlikely to be used again (in its present form) so
         // just use a scratch texture. This will remove the texture from the
         // cache so no one else can find it. Additionally, once unlocked, the
         // scratch texture will go to the end of the list for purging so will
         // likely be available for this volatile bitmap the next time around.
-        GrTexture* result = ctx->lockScratchTexture(desc,
-                                         GrContext::kExact_ScratchTexMatch);
+        GrTexture* result = ctx->lockAndRefScratchTexture(desc, GrContext::kExact_ScratchTexMatch);
         result->writePixels(0, 0,
                             bitmap->width(), bitmap->height(),
                             desc.fConfig,
@@ -135,32 +156,37 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
+bool GrIsBitmapInCache(const GrContext* ctx,
+                       const SkBitmap& bitmap,
+                       const GrTextureParams* params) {
+    GrCacheID cacheID;
+    generate_bitmap_cache_id(bitmap, &cacheID);
 
-GrTexture* GrLockCachedBitmapTexture(GrContext* ctx,
-                                     const SkBitmap& bitmap,
-                                     const GrTextureParams* params) {
+    GrTextureDesc desc;
+    generate_bitmap_texture_desc(bitmap, &desc);
+    return ctx->isTextureInCache(desc, cacheID, params);
+}
+
+GrTexture* GrLockAndRefCachedBitmapTexture(GrContext* ctx,
+                                           const SkBitmap& bitmap,
+                                           const GrTextureParams* params) {
     GrTexture* result = NULL;
 
-    if (!bitmap.isVolatile()) {
-        // If the bitmap isn't changing try to find a cached copy first
-        uint64_t key = bitmap.getGenerationID();
-        key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
+    bool cache = !bitmap.isVolatile();
+
+    if (cache) {
+        // If the bitmap isn't changing try to find a cached copy first.
+
+        GrCacheID cacheID;
+        generate_bitmap_cache_id(bitmap, &cacheID);
 
         GrTextureDesc desc;
-        desc.fWidth = bitmap.width();
-        desc.fHeight = bitmap.height();
-        desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
+        generate_bitmap_texture_desc(bitmap, &desc);
 
-        GrCacheData cacheData(key);
-
-        result = ctx->findTexture(desc, cacheData, params);
-        if (NULL == result) {
-            // didn't find a cached copy so create one
-            result = sk_gr_create_bitmap_texture(ctx, key, params, bitmap);
-        }
-    } else {
-        result = sk_gr_create_bitmap_texture(ctx, GrCacheData::kScratch_CacheID, params, bitmap);
+        result = ctx->findAndRefTexture(desc, cacheID, params);
+    }
+    if (NULL == result) {
+        result = sk_gr_create_bitmap_texture(ctx, cache, params, bitmap);
     }
     if (NULL == result) {
         GrPrintf("---- failed to create texture for cache [%d %d]\n",
@@ -169,10 +195,11 @@
     return result;
 }
 
-void GrUnlockCachedBitmapTexture(GrTexture* texture) {
+void GrUnlockAndUnrefCachedBitmapTexture(GrTexture* texture) {
     GrAssert(NULL != texture->getContext());
 
     texture->getContext()->unlockScratchTexture(texture);
+    texture->unref();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -194,4 +221,3 @@
             return kUnknown_GrPixelConfig;
     }
 }
-
diff --git a/src/gpu/SkGrFontScaler.cpp b/src/gpu/SkGrFontScaler.cpp
index dc637a8..acecbd9 100644
--- a/src/gpu/SkGrFontScaler.cpp
+++ b/src/gpu/SkGrFontScaler.cpp
@@ -199,6 +199,3 @@
     }
     return false;
 }
-
-
-
diff --git a/src/gpu/SkGrPixelRef.cpp b/src/gpu/SkGrPixelRef.cpp
index 081c09f..f8160ab 100644
--- a/src/gpu/SkGrPixelRef.cpp
+++ b/src/gpu/SkGrPixelRef.cpp
@@ -177,4 +177,3 @@
                                 kSkia8888_PM_GrPixelConfig,
                                 buffer, dst->rowBytes());
 }
-
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index d42896d..92df82d 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -8,6 +8,7 @@
 #include "GrConfigConversionEffect.h"
 #include "GrContext.h"
 #include "GrTBackendEffectFactory.h"
+#include "GrSimpleTextureEffect.h"
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLEffectMatrix.h"
 #include "SkMatrix.h"
@@ -15,8 +16,8 @@
 class GrGLConfigConversionEffect : public GrGLEffect {
 public:
     GrGLConfigConversionEffect(const GrBackendEffectFactory& factory,
-                               const GrEffect& s) : INHERITED (factory) {
-        const GrConfigConversionEffect& effect = static_cast<const GrConfigConversionEffect&>(s);
+                               const GrEffectRef& s) : INHERITED (factory) {
+        const GrConfigConversionEffect& effect = CastEffect<GrConfigConversionEffect>(s);
         fSwapRedAndBlue = effect.swapsRedAndBlue();
         fPMConversion = effect.pmConversion();
     }
@@ -67,7 +68,7 @@
 
     void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
         const GrConfigConversionEffect& effect =
-            static_cast<const GrConfigConversionEffect&>(*stage.getEffect());
+            GetEffectFromStage<GrConfigConversionEffect>(stage);
         fEffectMatrix.setData(uman,
                               effect.getMatrix(),
                               stage.getCoordChangeMatrix(),
@@ -75,8 +76,7 @@
     }
 
     static inline EffectKey GenKey(const GrEffectStage& s, const GrGLCaps&) {
-        const GrConfigConversionEffect& effect =
-            static_cast<const GrConfigConversionEffect&>(*s.getEffect());
+        const GrConfigConversionEffect& effect = GetEffectFromStage<GrConfigConversionEffect>(s);
         EffectKey key = static_cast<EffectKey>(effect.swapsRedAndBlue()) |
                         (effect.pmConversion() << 1);
         key <<= GrGLEffectMatrix::kKeyBits;
@@ -115,18 +115,25 @@
     return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
 }
 
-bool GrConfigConversionEffect::isEqual(const GrEffect& s) const {
-    const GrConfigConversionEffect& other = static_cast<const GrConfigConversionEffect&>(s);
-    return other.fSwapRedAndBlue == fSwapRedAndBlue && other.fPMConversion == fPMConversion;
+bool GrConfigConversionEffect::onIsEqual(const GrEffect& s) const {
+    const GrConfigConversionEffect& other = CastEffect<GrConfigConversionEffect>(s);
+    return this->texture(0) == s.texture(0) &&
+           other.fSwapRedAndBlue == fSwapRedAndBlue &&
+           other.fPMConversion == fPMConversion;
+}
+
+void GrConfigConversionEffect::getConstantColorComponents(GrColor* color,
+                                                          uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect);
 
-GrEffect* GrConfigConversionEffect::TestCreate(SkRandom* random,
-                                               GrContext* context,
-                                               GrTexture* textures[]) {
+GrEffectRef* GrConfigConversionEffect::TestCreate(SkRandom* random,
+                                                  GrContext* context,
+                                                  GrTexture* textures[]) {
     PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt));
     bool swapRB;
     if (kNone_PMConversion == pmConv) {
@@ -134,10 +141,12 @@
     } else {
         swapRB = random->nextBool();
     }
-    return SkNEW_ARGS(GrConfigConversionEffect, (textures[GrEffectUnitTest::kSkiaPMTextureIdx],
-                                                 swapRB,
-                                                 pmConv,
-                                                 GrEffectUnitTest::TestMatrix(random)));
+    AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect,
+                                      (textures[GrEffectUnitTest::kSkiaPMTextureIdx],
+                                       swapRB,
+                                       pmConv,
+                                       GrEffectUnitTest::TestMatrix(random))));
+    return CreateEffectRef(effect);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -204,21 +213,22 @@
         // We then verify that two reads produced the same values.
 
         GrPaint paint;
-        SkAutoTUnref<GrEffect> pmToUPMEffect1(SkNEW_ARGS(GrConfigConversionEffect,
-                                                        (dataTex,
-                                                         false,
-                                                         *pmToUPMRule,
-                                                         SkMatrix::I())));
-        SkAutoTUnref<GrEffect> upmToPMEffect(SkNEW_ARGS(GrConfigConversionEffect,
-                                                       (readTex,
-                                                        false,
-                                                        *upmToPMRule,
-                                                        SkMatrix::I())));
-        SkAutoTUnref<GrEffect> pmToUPMEffect2(SkNEW_ARGS(GrConfigConversionEffect,
-                                                        (tempTex,
-                                                         false,
-                                                         *pmToUPMRule,
-                                                         SkMatrix::I())));
+        AutoEffectUnref pmToUPM1(SkNEW_ARGS(GrConfigConversionEffect, (dataTex,
+                                                                       false,
+                                                                       *pmToUPMRule,
+                                                                       SkMatrix::I())));
+        AutoEffectUnref upmToPM(SkNEW_ARGS(GrConfigConversionEffect, (readTex,
+                                                                      false,
+                                                                      *upmToPMRule,
+                                                                      SkMatrix::I())));
+        AutoEffectUnref pmToUPM2(SkNEW_ARGS(GrConfigConversionEffect, (tempTex,
+                                                                       false,
+                                                                       *pmToUPMRule,
+                                                                       SkMatrix::I())));
+
+        SkAutoTUnref<GrEffectRef> pmToUPMEffect1(CreateEffectRef(pmToUPM1));
+        SkAutoTUnref<GrEffectRef> upmToPMEffect(CreateEffectRef(upmToPM));
+        SkAutoTUnref<GrEffectRef> pmToUPMEffect2(CreateEffectRef(pmToUPM2));
 
         context->setRenderTarget(readTex->asRenderTarget());
         paint.colorStage(0)->setEffect(pmToUPMEffect1);
@@ -251,27 +261,26 @@
     }
 }
 
-bool GrConfigConversionEffect::InstallEffect(GrTexture* texture,
-                                             bool swapRedAndBlue,
-                                             PMConversion pmConversion,
-                                             const SkMatrix& matrix,
-                                             GrEffectStage* stage) {
+const GrEffectRef* GrConfigConversionEffect::Create(GrTexture* texture,
+                                                    bool swapRedAndBlue,
+                                                    PMConversion pmConversion,
+                                                    const SkMatrix& matrix) {
     if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
-        // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
+        // If we returned a GrConfigConversionEffect that was equivalent to a GrSimpleTextureEffect
         // then we may pollute our texture cache with redundant shaders. So in the case that no
-        // conversions were requested we instead return a GrSingleTextureEffect.
-        stage->setEffect(SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix)))->unref();
-        return true;
+        // conversions were requested we instead return a GrSimpleTextureEffect.
+        return GrSimpleTextureEffect::Create(texture, matrix);
     } else {
         if (kRGBA_8888_GrPixelConfig != texture->config() &&
             kBGRA_8888_GrPixelConfig != texture->config() &&
             kNone_PMConversion != pmConversion) {
             // The PM conversions assume colors are 0..255
-            return false;
+            return NULL;
         }
-        stage->setEffect(SkNEW_ARGS(GrConfigConversionEffect, (texture,
-                                                               swapRedAndBlue,
-                                                               pmConversion, matrix)))->unref();
-        return true;
+        AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect, (texture,
+                                                                     swapRedAndBlue,
+                                                                     pmConversion,
+                                                                     matrix)));
+        return CreateEffectRef(effect);
     }
 }
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index 48c776c..169c3d7 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -10,6 +10,7 @@
 
 #include "GrSingleTextureEffect.h"
 
+class GrEffectStage;
 class GrGLConfigConversionEffect;
 
 /**
@@ -34,17 +35,17 @@
     };
 
     // Installs an effect in the GrEffectStage to perform a config conversion.
-    static bool InstallEffect(GrTexture*,
-                              bool swapRedAndBlue,
-                              PMConversion pmConversion,
-                              const SkMatrix& matrix,
-                              GrEffectStage* stage);
+    static const GrEffectRef* Create(GrTexture*,
+                                     bool swapRedAndBlue,
+                                     PMConversion pmConversion,
+                                     const SkMatrix& matrix);
 
     static const char* Name() { return "Config Conversion"; }
     typedef GrGLConfigConversionEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     bool swapsRedAndBlue() const { return fSwapRedAndBlue; }
     PMConversion  pmConversion() const { return fPMConversion; }
@@ -64,6 +65,8 @@
                             PMConversion pmConversion,
                             const SkMatrix& matrix);
 
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     bool            fSwapRedAndBlue;
     PMConversion    fPMConversion;
 
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
index 047dc41..8968857 100644
--- a/src/gpu/effects/GrConvolutionEffect.cpp
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -18,7 +18,7 @@
 
 class GrGLConvolutionEffect : public GrGLEffect {
 public:
-    GrGLConvolutionEffect(const GrBackendEffectFactory&, const GrEffect&);
+    GrGLConvolutionEffect(const GrBackendEffectFactory&, const GrEffectRef&);
 
     virtual void emitCode(GrGLShaderBuilder*,
                           const GrEffectStage&,
@@ -44,12 +44,11 @@
 };
 
 GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
-                                             const GrEffect& effect)
+                                             const GrEffectRef& effect)
     : INHERITED(factory)
     , fKernelUni(kInvalidUniformHandle)
     , fImageIncrementUni(kInvalidUniformHandle) {
-    const GrConvolutionEffect& c =
-        static_cast<const GrConvolutionEffect&>(effect);
+    const GrConvolutionEffect& c = CastEffect<GrConvolutionEffect>(effect);
     fRadius = c.radius();
 }
 
@@ -91,7 +90,7 @@
 }
 
 void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
-    const GrConvolutionEffect& conv = static_cast<const GrConvolutionEffect&>(*stage.getEffect());
+    const GrConvolutionEffect& conv = GetEffectFromStage<GrConvolutionEffect>(stage);
     GrTexture& texture = *conv.texture(0);
     // the code we generated was for a specific kernel radius
     GrAssert(conv.radius() == fRadius);
@@ -112,8 +111,8 @@
 }
 
 GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
-    const GrConvolutionEffect& conv = static_cast<const GrConvolutionEffect&>(*s.getEffect());
-    EffectKey key = static_cast<const GrConvolutionEffect&>(*s.getEffect()).radius();
+    const GrConvolutionEffect& conv = GetEffectFromStage<GrConvolutionEffect>(s);
+    EffectKey key = conv.radius();
     key <<= GrGLEffectMatrix::kKeyBits;
     EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
                                                    s.getCoordChangeMatrix(),
@@ -167,10 +166,9 @@
     return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
 }
 
-bool GrConvolutionEffect::isEqual(const GrEffect& sBase) const {
-     const GrConvolutionEffect& s =
-        static_cast<const GrConvolutionEffect&>(sBase);
-    return (INHERITED::isEqual(sBase) &&
+bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrConvolutionEffect& s = CastEffect<GrConvolutionEffect>(sBase);
+    return (this->texture(0) == s.texture(0) &&
             this->radius() == s.radius() &&
             this->direction() == s.direction() &&
             0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
@@ -180,9 +178,9 @@
 
 GR_DEFINE_EFFECT_TEST(GrConvolutionEffect);
 
-GrEffect* GrConvolutionEffect::TestCreate(SkRandom* random,
-                                          GrContext* context,
-                                          GrTexture* textures[]) {
+GrEffectRef* GrConvolutionEffect::TestCreate(SkRandom* random,
+                                             GrContext* context,
+                                             GrTexture* textures[]) {
     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                       GrEffectUnitTest::kAlphaTextureIdx;
     Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
@@ -192,6 +190,5 @@
         kernel[i] = random->nextSScalar1();
     }
 
-    return SkNEW_ARGS(GrConvolutionEffect, (textures[texIdx], dir, radius, kernel));
+    return GrConvolutionEffect::Create(textures[texIdx], dir, radius,kernel);
 }
-
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
index 3638c0c..b2b24e5 100644
--- a/src/gpu/effects/GrConvolutionEffect.h
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -22,13 +22,26 @@
 public:
 
     /// Convolve with an arbitrary user-specified kernel
-    GrConvolutionEffect(GrTexture*, Direction,
-                        int halfWidth, const float* kernel);
+    static GrEffectRef* Create(GrTexture* tex, Direction dir, int halfWidth, const float* kernel) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
+                                                                dir,
+                                                                halfWidth,
+                                                                kernel)));
+        return CreateEffectRef(effect);
+    }
 
     /// Convolve with a Gaussian kernel
-    GrConvolutionEffect(GrTexture*, Direction,
-                        int halfWidth,
-                        float gaussianSigma);
+    static GrEffectRef* CreateGaussian(GrTexture* tex,
+                                       Direction dir,
+                                       int halfWidth,
+                                       float gaussianSigma) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
+                                                                dir,
+                                                                halfWidth,
+                                                                gaussianSigma)));
+        return CreateEffectRef(effect);
+    }
+
     virtual ~GrConvolutionEffect();
 
     const float* kernel() const { return fKernel; }
@@ -38,7 +51,12 @@
     typedef GrGLConvolutionEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+        // If the texture was opaque we could know that the output color if we knew the sum of the
+        // kernel values.
+        *validFlags = 0;
+    }
 
     enum {
         // This was decided based on the min allowed value for the max texture
@@ -56,6 +74,16 @@
     float fKernel[kMaxKernelWidth];
 
 private:
+    GrConvolutionEffect(GrTexture*, Direction,
+                        int halfWidth, const float* kernel);
+
+    /// Convolve with a Gaussian kernel
+    GrConvolutionEffect(GrTexture*, Direction,
+                        int halfWidth,
+                        float gaussianSigma);
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GR_DECLARE_EFFECT_TEST;
 
     typedef Gr1DKernelEffect INHERITED;
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
new file mode 100644
index 0000000..b674879
--- /dev/null
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrSimpleTextureEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrTexture.h"
+
+class GrGLSimpleTextureEffect : public GrGLEffect {
+public:
+    GrGLSimpleTextureEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
+    : INHERITED (factory) {}
+
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrEffectStage&,
+                          EffectKey key,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+        const char* coordName;
+        GrSLType coordType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coordName);
+        builder->fFSCode.appendf("\t%s = ", outputColor);
+        builder->appendTextureLookupAndModulate(&builder->fFSCode,
+                                                inputColor,
+                                                samplers[0],
+                                                coordName,
+                                                coordType);
+        builder->fFSCode.append(";\n");
+    }
+
+    static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        const GrSimpleTextureEffect& ste = GetEffectFromStage<GrSimpleTextureEffect>(stage);
+        return GrGLEffectMatrix::GenKey(ste.getMatrix(),
+                                        stage.getCoordChangeMatrix(),
+                                        ste.texture(0));
+    }
+
+    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
+        const GrSimpleTextureEffect& ste = GetEffectFromStage<GrSimpleTextureEffect>(stage);
+        fEffectMatrix.setData(uman, ste.getMatrix(), stage.getCoordChangeMatrix(), ste.texture(0));
+    }
+
+private:
+    GrGLEffectMatrix fEffectMatrix;
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrSimpleTextureEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
+const GrBackendEffectFactory& GrSimpleTextureEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrSimpleTextureEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrSimpleTextureEffect);
+
+GrEffectRef* GrSimpleTextureEffect::TestCreate(SkRandom* random,
+                                               GrContext* context,
+                                               GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
+    return GrSimpleTextureEffect::Create(textures[texIdx], matrix);
+}
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
new file mode 100644
index 0000000..ffc05e5
--- /dev/null
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrSimpleTextureEffect_DEFINED
+#define GrSimpleTextureEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+
+class GrGLSimpleTextureEffect;
+
+/**
+ * The output color of this effect is a modulation of the input color and a sample from a texture.
+ * The coord to sample the texture is determine by a matrix. It allows explicit specification of
+ * the filtering and wrap modes (GrTextureParams).
+ */
+class GrSimpleTextureEffect : public GrSingleTextureEffect {
+public:
+    /* unfiltered, clamp mode */
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix)));
+        return CreateEffectRef(effect);
+    }
+
+    /* clamp mode */
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, bool bilerp) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, bilerp)));
+        return CreateEffectRef(effect);
+    }
+
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, const GrTextureParams& p) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, p)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrSimpleTextureEffect() {}
+
+    static const char* Name() { return "Texture"; }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    typedef GrGLSimpleTextureEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix)
+        : GrSingleTextureEffect(texture, matrix) {}
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix, bool bilerp)
+        : GrSingleTextureEffect(texture, matrix, bilerp) {}
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix, const GrTextureParams& params)
+        : GrSingleTextureEffect(texture, matrix, params) {}
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        const GrSimpleTextureEffect& ste = CastEffect<GrSimpleTextureEffect>(other);
+        return this->hasSameTextureParamsAndMatrix(ste);
+    }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
index 05eff6b..7183ba3 100644
--- a/src/gpu/effects/GrSingleTextureEffect.cpp
+++ b/src/gpu/effects/GrSingleTextureEffect.cpp
@@ -6,116 +6,26 @@
  */
 
 #include "effects/GrSingleTextureEffect.h"
-#include "gl/GrGLEffect.h"
-#include "gl/GrGLEffectMatrix.h"
-#include "gl/GrGLSL.h"
-#include "gl/GrGLTexture.h"
-#include "GrTBackendEffectFactory.h"
-#include "GrTexture.h"
-
-class GrGLSingleTextureEffect : public GrGLEffect {
-public:
-    GrGLSingleTextureEffect(const GrBackendEffectFactory& factory, const GrEffect&)
-    : INHERITED (factory) {}
-
-    virtual void emitCode(GrGLShaderBuilder* builder,
-                          const GrEffectStage&,
-                          EffectKey key,
-                          const char* vertexCoords,
-                          const char* outputColor,
-                          const char* inputColor,
-                          const TextureSamplerArray& samplers) SK_OVERRIDE {
-        const char* coordName;
-        GrSLType coordType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coordName);
-        builder->fFSCode.appendf("\t%s = ", outputColor);
-        builder->appendTextureLookupAndModulate(&builder->fFSCode,
-                                                inputColor,
-                                                samplers[0],
-                                                coordName,
-                                                coordType);
-        builder->fFSCode.append(";\n");
-    }
-
-    static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
-        const GrSingleTextureEffect& ste =
-            static_cast<const GrSingleTextureEffect&>(*stage.getEffect());
-        return GrGLEffectMatrix::GenKey(ste.getMatrix(),
-                                        stage.getCoordChangeMatrix(),
-                                        ste.texture(0));
-    }
-
-    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
-        const GrSingleTextureEffect& ste =
-            static_cast<const GrSingleTextureEffect&>(*stage.getEffect());
-        fEffectMatrix.setData(uman, ste.getMatrix(), stage.getCoordChangeMatrix(), ste.texture(0));
-    }
-
-private:
-    GrGLEffectMatrix fEffectMatrix;
-    typedef GrGLEffect INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture)
-    : INHERITED(1)
-    , fTextureAccess(texture) {
-    fMatrix.reset();
-}
-
-GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, bool bilerp)
-    : INHERITED(1)
-    , fTextureAccess(texture, bilerp) {
-    fMatrix.reset();
-}
-
-GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const GrTextureParams& params)
-    : INHERITED(1)
-    , fTextureAccess(texture, params) {
-    fMatrix.reset();
-}
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const SkMatrix& m)
-    : INHERITED(1)
-    , fTextureAccess(texture)
+    : fTextureAccess(texture)
     , fMatrix(m) {
+    this->addTextureAccess(&fTextureAccess);
 }
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const SkMatrix& m, bool bilerp)
-    : INHERITED(1)
-    , fTextureAccess(texture, bilerp)
+    : fTextureAccess(texture, bilerp)
     , fMatrix(m) {
+    this->addTextureAccess(&fTextureAccess);
 }
 
 GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
                                              const SkMatrix& m,
                                              const GrTextureParams& params)
-    : INHERITED(1)
-    , fTextureAccess(texture, params)
+    : fTextureAccess(texture, params)
     , fMatrix(m) {
+    this->addTextureAccess(&fTextureAccess);
 }
 
 GrSingleTextureEffect::~GrSingleTextureEffect() {
 }
-
-const GrTextureAccess& GrSingleTextureEffect::textureAccess(int index) const {
-    GrAssert(0 == index);
-    return fTextureAccess;
-}
-
-const GrBackendEffectFactory& GrSingleTextureEffect::getFactory() const {
-    return GrTBackendEffectFactory<GrSingleTextureEffect>::getInstance();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_EFFECT_TEST(GrSingleTextureEffect);
-
-GrEffect* GrSingleTextureEffect::TestCreate(SkRandom* random,
-                                            GrContext* context,
-                                            GrTexture* textures[]) {
-    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
-                                      GrEffectUnitTest::kAlphaTextureIdx;
-    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
-    return SkNEW_ARGS(GrSingleTextureEffect, (textures[texIdx], matrix));
-}
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index 709d3dc..4f25ddb 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -10,54 +10,49 @@
 
 #include "GrEffect.h"
 #include "SkMatrix.h"
-#include "GrTexture.h"
 
-class GrGLSingleTextureEffect;
+class GrTexture;
 
 /**
- * An effect that draws a single texture with a texture matrix; commonly used as a base class. The
- * output color is the texture color is modulated against the input color.
+ * A base class for effects that draw a single texture with a texture matrix.
  */
 class GrSingleTextureEffect : public GrEffect {
-
 public:
-    /** These three constructors assume an identity matrix. TODO: Remove these.*/
-    GrSingleTextureEffect(GrTexture* texture); /* unfiltered, clamp mode */
-    GrSingleTextureEffect(GrTexture* texture, bool bilerp); /* clamp mode */
-    GrSingleTextureEffect(GrTexture* texture, const GrTextureParams&);
+    virtual ~GrSingleTextureEffect();
 
-    /** These three constructors take an explicit matrix */
+    const SkMatrix& getMatrix() const { return fMatrix; }
+
+protected:
     GrSingleTextureEffect(GrTexture*, const SkMatrix&); /* unfiltered, clamp mode */
     GrSingleTextureEffect(GrTexture*, const SkMatrix&, bool bilerp); /* clamp mode */
     GrSingleTextureEffect(GrTexture*, const SkMatrix&, const GrTextureParams&);
 
-    virtual ~GrSingleTextureEffect();
-
-    virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
-
-    static const char* Name() { return "Single Texture"; }
-
-    const SkMatrix& getMatrix() const { return fMatrix; }
-
-    typedef GrGLSingleTextureEffect GLEffect;
-
-    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-
-    virtual bool isEqual(const GrEffect& effect) const SK_OVERRIDE {
-        const GrSingleTextureEffect& ste = static_cast<const GrSingleTextureEffect&>(effect);
-        return INHERITED::isEqual(effect) && fMatrix.cheapEqualTo(ste.getMatrix());
+    /**
+     * Helper for subclass onIsEqual() functions.
+     */
+    bool hasSameTextureParamsAndMatrix(const GrSingleTextureEffect& other) const {
+        const GrTextureAccess& otherAccess = other.fTextureAccess;
+        // We don't have to check the accesses' swizzles because they are inferred from the texture.
+        return fTextureAccess.getTexture() == otherAccess.getTexture() &&
+               fTextureAccess.getParams() == otherAccess.getParams() &&
+               this->getMatrix().cheapEqualTo(other.getMatrix());
     }
 
-    static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) {
-        GrAssert(NULL != texture);
-        SkMatrix mat;
-        mat.setIDiv(texture->width(), texture->height());
-        return mat;
+    /**
+     * Can be used as a helper to implement subclass getConstantColorComponents(). It assumes that
+     * the subclass output color will be a modulation of the input color with a value read from the
+     * texture.
+     */
+    void updateConstantColorComponentsForModulation(GrColor* color, uint32_t* validFlags) const {
+        if ((*validFlags & kA_ValidComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
+            GrPixelConfigIsOpaque(this->texture(0)->config())) {
+            *validFlags = kA_ValidComponentFlag;
+        } else {
+            *validFlags = 0;
+        }
     }
 
 private:
-    GR_DECLARE_EFFECT_TEST;
-
     GrTextureAccess fTextureAccess;
     SkMatrix        fMatrix;
 
diff --git a/src/gpu/effects/GrTextureDomainEffect.cpp b/src/gpu/effects/GrTextureDomainEffect.cpp
index 6884682..833c198 100644
--- a/src/gpu/effects/GrTextureDomainEffect.cpp
+++ b/src/gpu/effects/GrTextureDomainEffect.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrTextureDomainEffect.h"
+#include "GrSimpleTextureEffect.h"
 #include "GrTBackendEffectFactory.h"
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLEffectMatrix.h"
@@ -13,7 +14,7 @@
 
 class GrGLTextureDomainEffect : public GrGLEffect {
 public:
-    GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrEffect&);
+    GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrEffectRef&);
 
     virtual void emitCode(GrGLShaderBuilder*,
                           const GrEffectStage&,
@@ -36,7 +37,7 @@
 };
 
 GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory,
-                                                 const GrEffect&)
+                                                 const GrEffectRef&)
     : INHERITED(factory)
     , fNameUni(GrGLUniformManager::kInvalidUniformHandle) {
     fPrevDomain[0] = SK_FloatNaN;
@@ -49,8 +50,7 @@
                                        const char* outputColor,
                                        const char* inputColor,
                                        const TextureSamplerArray& samplers) {
-    const GrTextureDomainEffect& effect =
-        static_cast<const GrTextureDomainEffect&>(*stage.getEffect());
+    const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage);
 
     const char* coords;
     fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
@@ -80,8 +80,7 @@
 }
 
 void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
-    const GrTextureDomainEffect& effect =
-        static_cast<const GrTextureDomainEffect&>(*stage.getEffect());
+    const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage);
     const GrRect& domain = effect.domain();
 
     float values[4] = {
@@ -91,7 +90,7 @@
         SkScalarToFloat(domain.bottom())
     };
     // vertical flip if necessary
-    if (GrSurface::kBottomLeft_Origin == effect.texture(0)->origin()) {
+    if (kBottomLeft_GrSurfaceOrigin == effect.texture(0)->origin()) {
         values[1] = 1.0f - values[1];
         values[3] = 1.0f - values[3];
         // The top and bottom were just flipped, so correct the ordering
@@ -108,8 +107,7 @@
 }
 
 GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
-    const GrTextureDomainEffect& effect =
-        static_cast<const GrTextureDomainEffect&>(*stage.getEffect());
+    const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage);
     EffectKey key = effect.wrapMode();
     key <<= GrGLEffectMatrix::kKeyBits;
     EffectKey matrixKey = GrGLEffectMatrix::GenKey(effect.getMatrix(),
@@ -121,14 +119,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrEffect* GrTextureDomainEffect::Create(GrTexture* texture,
-                                        const SkMatrix& matrix,
-                                        const GrRect& domain,
-                                        WrapMode wrapMode,
-                                        bool bilerp) {
+GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture,
+                                           const SkMatrix& matrix,
+                                           const GrRect& domain,
+                                           WrapMode wrapMode,
+                                           bool bilerp) {
     static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
     if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) {
-        return SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix, bilerp));
+        return GrSimpleTextureEffect::Create(texture, matrix, bilerp);
     } else {
         SkRect clippedDomain;
         // We don't currently handle domains that are empty or don't intersect the texture.
@@ -142,8 +140,14 @@
         clippedDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
         GrAssert(clippedDomain.fLeft <= clippedDomain.fRight);
         GrAssert(clippedDomain.fTop <= clippedDomain.fBottom);
-        return SkNEW_ARGS(GrTextureDomainEffect,
-                          (texture, matrix, clippedDomain, wrapMode, bilerp));
+
+        AutoEffectUnref effect(SkNEW_ARGS(GrTextureDomainEffect, (texture,
+                                                                  matrix,
+                                                                  clippedDomain,
+                                                                  wrapMode,
+                                                                  bilerp)));
+        return CreateEffectRef(effect);
+
     }
 }
 
@@ -165,18 +169,26 @@
     return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance();
 }
 
-bool GrTextureDomainEffect::isEqual(const GrEffect& sBase) const {
-    const GrTextureDomainEffect& s = static_cast<const GrTextureDomainEffect&>(sBase);
-    return (INHERITED::isEqual(sBase) && this->fTextureDomain == s.fTextureDomain);
+bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrTextureDomainEffect& s = CastEffect<GrTextureDomainEffect>(sBase);
+    return this->hasSameTextureParamsAndMatrix(s) && this->fTextureDomain == s.fTextureDomain;
+}
+
+void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    if (kDecal_WrapMode == fWrapMode) {
+        *validFlags = 0;
+    } else {
+        this->updateConstantColorComponentsForModulation(color, validFlags);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect);
 
-GrEffect* GrTextureDomainEffect::TestCreate(SkRandom* random,
-                                            GrContext* context,
-                                            GrTexture* textures[]) {
+GrEffectRef* GrTextureDomainEffect::TestCreate(SkRandom* random,
+                                               GrContext* context,
+                                               GrTexture* textures[]) {
     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                       GrEffectUnitTest::kAlphaTextureIdx;
     GrRect domain;
diff --git a/src/gpu/effects/GrTextureDomainEffect.h b/src/gpu/effects/GrTextureDomainEffect.h
index c1ce6d1..b7f665c 100644
--- a/src/gpu/effects/GrTextureDomainEffect.h
+++ b/src/gpu/effects/GrTextureDomainEffect.h
@@ -34,11 +34,11 @@
         kDecal_WrapMode,
     };
 
-    static GrEffect* Create(GrTexture*,
-                            const SkMatrix&,
-                            const SkRect& domain,
-                            WrapMode,
-                            bool bilerp = false);
+    static GrEffectRef* Create(GrTexture*,
+                               const SkMatrix&,
+                               const SkRect& domain,
+                               WrapMode,
+                               bool bilerp = false);
 
     virtual ~GrTextureDomainEffect();
 
@@ -47,7 +47,7 @@
     typedef GrGLTextureDomainEffect GLEffect;
 
     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
 
     const SkRect& domain() const { return fTextureDomain; }
     WrapMode wrapMode() const { return fWrapMode; }
@@ -77,6 +77,8 @@
                           WrapMode,
                           bool bilerp);
 
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
     GR_DECLARE_EFFECT_TEST;
 
     typedef GrSingleTextureEffect INHERITED;
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
index 92f5ad5..9081fb6 100644
--- a/src/gpu/effects/GrTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -17,9 +17,6 @@
     #define VALIDATE
 #endif
 
-GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrTextureStripAtlas, GetTextureStripAtlasDomain)
-
-
 int32_t GrTextureStripAtlas::gCacheCount = 0;
 
 GrTHashTable<GrTextureStripAtlas::AtlasEntry,
@@ -73,7 +70,7 @@
 }
 
 GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc)
-    : fCacheID(sk_atomic_inc(&gCacheCount))
+    : fCacheKey(sk_atomic_inc(&gCacheCount))
     , fLockedRows(0)
     , fDesc(desc)
     , fNumRows(desc.fHeight / desc.fRowHeight)
@@ -198,17 +195,21 @@
     texDesc.fWidth = fDesc.fWidth;
     texDesc.fHeight = fDesc.fHeight;
     texDesc.fConfig = fDesc.fConfig;
-    GrCacheData cacheData(fCacheID);
-    cacheData.fResourceDomain = GetTextureStripAtlasDomain();
-    fTexture = fDesc.fContext->findTexture(texDesc, cacheData, &params);
+
+    static const GrCacheID::Domain gTextureStripAtlasDomain = GrCacheID::GenerateDomain();
+    GrCacheID::Key key;
+    *key.fData32 = fCacheKey;
+    memset(key.fData32 + 1, 0, sizeof(key) - sizeof(uint32_t));
+    GrCacheID cacheID(gTextureStripAtlasDomain, key);
+
+    fTexture = fDesc.fContext->findAndRefTexture(texDesc, cacheID, &params);
     if (NULL == fTexture) {
-        fTexture = fDesc.fContext->createTexture(&params, texDesc, cacheData, NULL, 0);
+        fTexture = fDesc.fContext->createTexture(&params, texDesc, cacheID, NULL, 0);
         // This is a new texture, so all of our cache info is now invalid
         this->initLRU();
         fKeyTable.rewind();
     }
     GrAssert(NULL != fTexture);
-    fTexture->ref();
 }
 
 void GrTextureStripAtlas::unlockTexture() {
@@ -343,4 +344,3 @@
     }
 }
 #endif
-
diff --git a/src/gpu/effects/GrTextureStripAtlas.h b/src/gpu/effects/GrTextureStripAtlas.h
index 210d88e..9ac356b 100644
--- a/src/gpu/effects/GrTextureStripAtlas.h
+++ b/src/gpu/effects/GrTextureStripAtlas.h
@@ -21,8 +21,6 @@
  */
 class GrTextureStripAtlas {
 public:
-    GR_DECLARE_RESOURCE_CACHE_DOMAIN(GetTextureStripAtlasDomain)
-
     /**
      * Descriptor struct which we'll use as a hash table key
      **/
@@ -157,7 +155,7 @@
 
     // A unique ID for this texture (formed with: gCacheCount++), so we can be sure that if we
     // get a texture back from the texture cache, that it's the same one we last used.
-    const uint64_t fCacheID;
+    const int32_t fCacheKey;
 
     // Total locks on all rows (when this reaches zero, we can unlock our texture)
     int32_t fLockedRows;
@@ -181,4 +179,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index ec80cda..b140a10 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -426,4 +426,3 @@
     GrPrintf("Two Format Limit: %s\n", (fTwoFormatLimit ? "YES": "NO"));
     GrPrintf("Fragment coord conventions support: %s\n", (fFragCoordsConventionSupport ? "YES": "NO"));
 }
-
diff --git a/src/gpu/gl/GrGLContextInfo.cpp b/src/gpu/gl/GrGLContextInfo.cpp
index 060d236..7a33ac7 100644
--- a/src/gpu/gl/GrGLContextInfo.cpp
+++ b/src/gpu/gl/GrGLContextInfo.cpp
@@ -83,4 +83,3 @@
 bool GrGLContextInfo::isInitialized() const {
     return kNone_GrGLBinding != fBindingInUse;
 }
-
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
index 6cca6c8..f450cc8 100644
--- a/src/gpu/gl/GrGLCreateNullInterface.cpp
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -7,8 +7,8 @@
 
 
 #include "gl/GrGLInterface.h"
-#include "GrTDArray.h"
 #include "GrGLDefines.h"
+#include "SkTDArray.h"
 
 namespace { // added to suppress 'no previous prototype' warning
 
@@ -132,7 +132,7 @@
 
 // In debug builds we do asserts that ensure we agree with GL about when a buffer
 // is mapped.
-static GrTDArray<GrGLuint> gMappedBuffers;
+static SkTDArray<GrGLuint> gMappedBuffers;
 static GrGLuint gCurrArrayBuffer;
 static GrGLuint gCurrElementArrayBuffer;
 
diff --git a/src/gpu/gl/GrGLEffect.cpp b/src/gpu/gl/GrGLEffect.cpp
index 0bbf1f7..ccea269 100644
--- a/src/gpu/gl/GrGLEffect.cpp
+++ b/src/gpu/gl/GrGLEffect.cpp
@@ -20,11 +20,11 @@
 void GrGLEffect::setData(const GrGLUniformManager&, const GrEffectStage&) {
 }
 
-GrGLEffect::EffectKey GrGLEffect::GenTextureKey(const GrEffect& effect,
+GrGLEffect::EffectKey GrGLEffect::GenTextureKey(const GrEffectRef* effect,
                                                 const GrGLCaps& caps) {
     EffectKey key = 0;
-    for (int index = 0; index < effect.numTextures(); ++index) {
-        const GrTextureAccess& access = effect.textureAccess(index);
+    for (int index = 0; index < (*effect)->numTextures(); ++index) {
+        const GrTextureAccess& access = (*effect)->textureAccess(index);
         EffectKey value = GrGLShaderBuilder::KeyForTextureAccess(access, caps) << index;
         GrAssert(0 == (value & key)); // keys for each access ought not to overlap
         key |= value;
diff --git a/src/gpu/gl/GrGLEffect.h b/src/gpu/gl/GrGLEffect.h
index f2142c5..76f865e 100644
--- a/src/gpu/gl/GrGLEffect.h
+++ b/src/gpu/gl/GrGLEffect.h
@@ -12,8 +12,8 @@
 #include "GrGLShaderBuilder.h"
 #include "GrGLShaderVar.h"
 #include "GrGLSL.h"
+#include "GrEffectStage.h"
 
-class GrEffectStage;
 class GrGLTexture;
 
 /** @file
@@ -86,7 +86,28 @@
 
     const char* name() const { return fFactory.name(); }
 
-    static EffectKey GenTextureKey(const GrEffect&, const GrGLCaps&);
+    static EffectKey GenTextureKey(const GrEffectRef*, const GrGLCaps&);
+
+   /**
+    * GrGLEffect subclasses get passed a GrEffectStage in their emitCode and setData functions.
+    * The GrGLEffect usually needs to cast the stage's effect to the GrEffect subclass that
+    * generated the GrGLEffect. This helper does just that.
+    */
+    template <typename T>
+    static const T& GetEffectFromStage(const GrEffectStage& effectStage) {
+        GrAssert(NULL != effectStage.getEffect());
+        return CastEffect<T>(*effectStage.getEffect());
+    }
+
+   /**
+    * Extracts the GrEffect from a GrEffectRef and down-casts to a GrEffect subclass. Usually used
+    * in a GrGLEffect subclass's constructor (which takes const GrEffectRef&).
+    */
+    template <typename T>
+    static const T& CastEffect(const GrEffectRef& effectRef) {
+        GrAssert(NULL != effectRef.get());
+        return *static_cast<const T*>(effectRef.get());
+    }
 
 protected:
     const GrBackendEffectFactory& fFactory;
diff --git a/src/gpu/gl/GrGLEffectMatrix.cpp b/src/gpu/gl/GrGLEffectMatrix.cpp
index 6dcb6e6..5fdde2c 100644
--- a/src/gpu/gl/GrGLEffectMatrix.cpp
+++ b/src/gpu/gl/GrGLEffectMatrix.cpp
@@ -19,7 +19,7 @@
                                      SkMatrix::kPerspective_Mask;
     int combinedTypes = type0 | type1;
 
-    bool reverseY = (NULL != texture) && GrSurface::kBottomLeft_Origin == texture->origin();
+    bool reverseY = (NULL != texture) && kBottomLeft_GrSurfaceOrigin == texture->origin();
 
     if (SkMatrix::kPerspective_Mask & combinedTypes) {
         return kGeneral_Key;
@@ -173,11 +173,11 @@
         case kVoid_GrSLType:
             GrAssert(matrix.isIdentity());
             GrAssert(coordChangeMatrix.isIdentity());
-            GrAssert(NULL == texture || GrSurface::kTopLeft_Origin == texture->origin());
+            GrAssert(NULL == texture || kTopLeft_GrSurfaceOrigin == texture->origin());
             return;
         case kVec2f_GrSLType: {
             GrAssert(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
-            GrAssert(NULL == texture || GrSurface::kTopLeft_Origin == texture->origin());
+            GrAssert(NULL == texture || kTopLeft_GrSurfaceOrigin == texture->origin());
             SkScalar tx = matrix[SkMatrix::kMTransX] + coordChangeMatrix[SkMatrix::kMTransX];
             SkScalar ty = matrix[SkMatrix::kMTransY] + coordChangeMatrix[SkMatrix::kMTransY];
             if (fPrevMatrix.get(SkMatrix::kMTransX) != tx ||
@@ -191,7 +191,7 @@
         case kMat33f_GrSLType: {
             SkMatrix combined;
             combined.setConcat(matrix, coordChangeMatrix);
-            if (NULL != texture && GrSurface::kBottomLeft_Origin == texture->origin()) {
+            if (NULL != texture && kBottomLeft_GrSurfaceOrigin == texture->origin()) {
                 // combined.postScale(1,-1);
                 // combined.postTranslate(0,1);
                 combined.set(SkMatrix::kMSkewY,
diff --git a/src/gpu/gl/GrGLIndexBuffer.cpp b/src/gpu/gl/GrGLIndexBuffer.cpp
index 66ee095..561133a 100644
--- a/src/gpu/gl/GrGLIndexBuffer.cpp
+++ b/src/gpu/gl/GrGLIndexBuffer.cpp
@@ -15,10 +15,11 @@
 #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
 
 GrGLIndexBuffer::GrGLIndexBuffer(GrGpuGL* gpu,
+                                 bool isWrapped,
                                  GrGLuint id,
                                  size_t sizeInBytes,
                                  bool dynamic)
-    : INHERITED(gpu, sizeInBytes, dynamic)
+    : INHERITED(gpu, isWrapped, sizeInBytes, dynamic)
     , fBufferID(id)
     , fLockPtr(NULL) {
 
@@ -26,7 +27,7 @@
 
 void GrGLIndexBuffer::onRelease() {
     // make sure we've not been abandoned
-    if (fBufferID) {
+    if (fBufferID && !this->isWrapped()) {
         GPUGL->notifyIndexBufferDelete(this);
         GL_CALL(DeleteBuffers(1, &fBufferID));
         fBufferID = 0;
@@ -134,4 +135,3 @@
 #endif
     return true;
 }
-
diff --git a/src/gpu/gl/GrGLIndexBuffer.h b/src/gpu/gl/GrGLIndexBuffer.h
index e282001..936e650 100644
--- a/src/gpu/gl/GrGLIndexBuffer.h
+++ b/src/gpu/gl/GrGLIndexBuffer.h
@@ -32,6 +32,7 @@
 
 protected:
     GrGLIndexBuffer(GrGpuGL* gpu,
+                    bool isWrapped,
                     GrGLuint id,
                     size_t sizeInBytes,
                     bool dynamic);
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index 297e1fc..61020bc 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -346,4 +346,3 @@
 
     return true;
 }
-
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp
index 5324dcd..8c6e11e 100644
--- a/src/gpu/gl/GrGLPath.cpp
+++ b/src/gpu/gl/GrGLPath.cpp
@@ -33,6 +33,7 @@
     return gTable[verb];
 }
 
+#ifdef SK_DEBUG
 inline int num_pts(const SkPath::Verb verb) {
     static const int gTable[] = {
         1, // move
@@ -50,9 +51,12 @@
     GrAssert(verb >= 0 && (size_t)verb < GR_ARRAY_COUNT(gTable));
     return gTable[verb];
 }
+#endif
 }
 
-GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu) {
+static const bool kIsWrapped = false; // The constructor creates the GL path object.
+
+GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu, kIsWrapped) {
     GL_CALL_RET(fPathID, GenPaths(1));
     SkPath::Iter iter(path, true);
 
@@ -90,7 +94,7 @@
 }
 
 void GrGLPath::onRelease() {
-    if (0 != fPathID) {
+    if (0 != fPathID && !this->isWrapped()) {
         GL_CALL(DeletePaths(fPathID, 1));
         fPathID = 0;
     }
@@ -103,4 +107,3 @@
 
     INHERITED::onAbandon();
 }
-
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 0558701..2aa7236 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -10,17 +10,20 @@
 #include "GrAllocator.h"
 #include "GrEffect.h"
 #include "GrGLEffect.h"
+#include "GrGpuGL.h"
 #include "GrGLShaderVar.h"
 #include "GrBackendEffectFactory.h"
 #include "SkTrace.h"
 #include "SkXfermode.h"
 
+#include "SkRTConf.h"
+
 SK_DEFINE_INST_COUNT(GrGLProgram)
 
 #define GL_CALL(X) GR_GL_CALL(fContextInfo.interface(), X)
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(fContextInfo.interface(), R, X)
 
-#define PRINT_SHADERS 0
+SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false, "Print the source code for all shaders generated.");
 
 #define COL_ATTR_NAME "aColor"
 #define COV_ATTR_NAME "aCoverage"
@@ -32,16 +35,6 @@
     s->appendS32(coordIdx);
 }
 
-inline const char* float_vector_type_str(int count) {
-    return GrGLShaderVar::TypeString(GrSLFloatVectorType(count));
-}
-
-inline const char* vector_all_coords(int count) {
-    static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
-    return ALL[count];
-}
-
 inline const char* declared_color_output_name() { return "fsColorOut"; }
 inline const char* dual_source_output_name() { return "dualSourceOut"; }
 
@@ -126,12 +119,14 @@
     }
 }
 
+namespace {
+
 // given two blend coeffecients determine whether the src
 // and/or dst computation can be omitted.
-static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
-                                   SkXfermode::Coeff dstCoeff,
-                                   bool* needSrcValue,
-                                   bool* needDstValue) {
+inline void need_blend_inputs(SkXfermode::Coeff srcCoeff,
+                              SkXfermode::Coeff dstCoeff,
+                              bool* needSrcValue,
+                              bool* needDstValue) {
     if (SkXfermode::kZero_Coeff == srcCoeff) {
         switch (dstCoeff) {
             // these all read the src
@@ -170,9 +165,9 @@
  * Create a blend_coeff * value string to be used in shader code. Sets empty
  * string if result is trivially zero.
  */
-static void blendTermString(SkString* str, SkXfermode::Coeff coeff,
-                             const char* src, const char* dst,
-                             const char* value) {
+inline void blend_term_string(SkString* str, SkXfermode::Coeff coeff,
+                       const char* src, const char* dst,
+                       const char* value) {
     switch (coeff) {
     case SkXfermode::kZero_Coeff:    /** 0 */
         *str = "";
@@ -213,23 +208,24 @@
  * Adds a line to the fragment shader code which modifies the color by
  * the specified color filter.
  */
-static void addColorFilter(SkString* fsCode, const char * outputVar,
-                           SkXfermode::Coeff uniformCoeff,
-                           SkXfermode::Coeff colorCoeff,
-                           const char* filterColor,
-                           const char* inColor) {
+void add_color_filter(SkString* fsCode, const char * outputVar,
+                      SkXfermode::Coeff uniformCoeff,
+                      SkXfermode::Coeff colorCoeff,
+                      const char* filterColor,
+                      const char* inColor) {
     SkString colorStr, constStr;
-    blendTermString(&colorStr, colorCoeff, filterColor, inColor, inColor);
-    blendTermString(&constStr, uniformCoeff, filterColor, inColor, filterColor);
+    blend_term_string(&colorStr, colorCoeff, filterColor, inColor, inColor);
+    blend_term_string(&constStr, uniformCoeff, filterColor, inColor, filterColor);
 
     fsCode->appendf("\t%s = ", outputVar);
     GrGLSLAdd4f(fsCode, colorStr.c_str(), constStr.c_str());
     fsCode->append(";\n");
 }
+}
 
 bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
                                   GrGLShaderBuilder* builder) const {
-    if (fDesc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit) {
+    if (fDesc.fVertexLayout & GrDrawState::kEdge_VertexLayoutBit) {
         const char *vsName, *fsName;
         builder->addVarying(kVec4f_GrSLType, "Edge", &vsName, &fsName);
         builder->fVSAttrs.push_back().set(kVec4f_GrSLType,
@@ -280,6 +276,13 @@
             builder->fFSCode.appendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName);
             builder->fFSCode.append("\tedgeAlpha = outerAlpha * innerAlpha;\n");
             break;
+        case GrDrawState::kEllipse_EdgeType:
+            builder->fFSCode.append("\tfloat edgeAlpha;\n");
+            builder->fFSCode.appendf("\tvec2 offset = (%s.xy - %s.xy);\n", builder->fragmentPosition(), fsName);
+            builder->fFSCode.appendf("\toffset.y *= %s.w;\n", fsName);
+            builder->fFSCode.append("\tfloat d = length(offset);\n");
+            builder->fFSCode.appendf("\tedgeAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
+            break;
         default:
             GrCrash("Unknown Edge Type!");
             break;
@@ -308,8 +311,8 @@
             } break;
         case GrGLProgram::Desc::kUniform_ColorInput: {
             const char* name;
-            fUniforms.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                                                      kVec4f_GrSLType, "Color", &name);
+            fUniformHandles.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                            kVec4f_GrSLType, "Color", &name);
             *inColor = name;
             break;
         }
@@ -326,8 +329,8 @@
 
 void GrGLProgram::genUniformCoverage(GrGLShaderBuilder* builder, SkString* inOutCoverage) {
     const char* covUniName;
-    fUniforms.fCoverageUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                                                 kVec4f_GrSLType, "Coverage", &covUniName);
+    fUniformHandles.fCoverageUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                       kVec4f_GrSLType, "Coverage", &covUniName);
     if (inOutCoverage->size()) {
         builder->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
                                   covUniName, inOutCoverage->c_str());
@@ -463,20 +466,21 @@
     SkString shader;
 
     builder.getShader(GrGLShaderBuilder::kVertex_ShaderType, &shader);
-#if PRINT_SHADERS
-    GrPrintf(shader.c_str());
-    GrPrintf("\n");
-#endif
+    if (c_PrintShaders) {
+        GrPrintf(shader.c_str());
+        GrPrintf("\n");
+    }
+
     if (!(fVShaderID = compile_shader(fContextInfo, GR_GL_VERTEX_SHADER, shader))) {
         return false;
     }
 
     if (builder.fUsesGS) {
         builder.getShader(GrGLShaderBuilder::kGeometry_ShaderType, &shader);
-#if PRINT_SHADERS
-        GrPrintf(shader.c_str());
-        GrPrintf("\n");
-#endif
+        if (c_PrintShaders) {
+            GrPrintf(shader.c_str());
+            GrPrintf("\n");
+        }
         if (!(fGShaderID = compile_shader(fContextInfo, GR_GL_GEOMETRY_SHADER, shader))) {
             return false;
         }
@@ -485,10 +489,10 @@
     }
 
     builder.getShader(GrGLShaderBuilder::kFragment_ShaderType, &shader);
-#if PRINT_SHADERS
-    GrPrintf(shader.c_str());
-    GrPrintf("\n");
-#endif
+    if (c_PrintShaders) {
+        GrPrintf(shader.c_str());
+        GrPrintf("\n");
+    }
     if (!(fFShaderID = compile_shader(fContextInfo, GR_GL_FRAGMENT_SHADER, shader))) {
         return false;
     }
@@ -542,8 +546,8 @@
 
     bool needColorFilterUniform;
     bool needComputedColor;
-    needBlendInputs(uniformCoeff, colorCoeff,
-                    &needColorFilterUniform, &needComputedColor);
+    need_blend_inputs(uniformCoeff, colorCoeff,
+                      &needColorFilterUniform, &needComputedColor);
 
     // the dual source output has no canonical var name, have to
     // declare an output, which is incompatible with gl_FragColor/gl_FragData.
@@ -560,8 +564,8 @@
     }
 
     const char* viewMName;
-    fUniforms.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_ShaderType,
-                                                  kMat33f_GrSLType, "ViewM", &viewMName);
+    fUniformHandles.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                                        kMat33f_GrSLType, "ViewM", &viewMName);
 
 
     builder.fVSCode.appendf("\tvec3 pos3 = %s * vec3(%s, 1);\n"
@@ -583,7 +587,7 @@
     // add texture coordinates that are used to the list of vertex attr decls
     SkString texCoordAttrs[GrDrawState::kMaxTexCoords];
     for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
+        if (GrDrawState::VertexUsesTexCoordIdx(t, layout)) {
             tex_attr_name(t, texCoordAttrs + t);
             builder.fVSAttrs.push_back().set(kVec2f_GrSLType,
                 GrGLShaderVar::kAttribute_TypeModifier,
@@ -607,7 +611,7 @@
 
                 const char* inCoords;
                 // figure out what our input coords are
-                int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                int tcIdx = GrDrawState::VertexTexCoordsForStage(s, layout);
                 if (tcIdx < 0) {
                     inCoords = builder.positionAttribute().c_str();
                 } else {
@@ -617,13 +621,12 @@
                 }
 
                 builder.setCurrentStage(s);
-                fEffects[s] = GenStageCode(*stages[s],
-                                           fDesc.fEffectKeys[s],
-                                           &fUniforms.fStages[s],
-                                           inColor.size() ? inColor.c_str() : NULL,
-                                           outColor.c_str(),
-                                           inCoords,
-                                           &builder);
+                fEffects[s] = builder.createAndEmitGLEffect(*stages[s],
+                                                            fDesc.fEffectKeys[s],
+                                                            inColor.size() ? inColor.c_str() : NULL,
+                                                            outColor.c_str(),
+                                                            inCoords,
+                                                            &fUniformHandles.fSamplerUnis[s]);
                 builder.setNonStage();
                 inColor = outColor;
             }
@@ -639,15 +642,16 @@
         if (uniformCoeffIsZero) {
             uniformCoeff = SkXfermode::kZero_Coeff;
             bool bogus;
-            needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
-                            &needColorFilterUniform, &bogus);
+            need_blend_inputs(SkXfermode::kZero_Coeff, colorCoeff,
+                              &needColorFilterUniform, &bogus);
         }
     }
     const char* colorFilterColorUniName = NULL;
     if (needColorFilterUniform) {
-        fUniforms.fColorFilterUni = builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                                                       kVec4f_GrSLType, "FilterColor",
-                                                       &colorFilterColorUniName);
+        fUniformHandles.fColorFilterUni = builder.addUniform(
+                                                        GrGLShaderBuilder::kFragment_ShaderType,
+                                                        kVec4f_GrSLType, "FilterColor",
+                                                        &colorFilterColorUniName);
     }
     bool wroteFragColorZero = false;
     if (SkXfermode::kZero_Coeff == uniformCoeff &&
@@ -659,7 +663,7 @@
     } else if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) {
         builder.fFSCode.append("\tvec4 filteredColor;\n");
         const char* color = adjustInColor(inColor);
-        addColorFilter(&builder.fFSCode, "filteredColor", uniformCoeff,
+        add_color_filter(&builder.fFSCode, "filteredColor", uniformCoeff,
                        colorCoeff, colorFilterColorUniName, color);
         inColor = "filteredColor";
     }
@@ -704,7 +708,7 @@
                     const char* inCoords;
                     // figure out what our input coords are
                     int tcIdx =
-                        GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                        GrDrawState::VertexTexCoordsForStage(s, layout);
                     if (tcIdx < 0) {
                         inCoords = builder.positionAttribute().c_str();
                     } else {
@@ -722,13 +726,13 @@
                         inCoverage.append("4");
                     }
                     builder.setCurrentStage(s);
-                    fEffects[s] = GenStageCode(*stages[s],
-                                               fDesc.fEffectKeys[s],
-                                               &fUniforms.fStages[s],
-                                               inCoverage.size() ? inCoverage.c_str() : NULL,
-                                               outCoverage.c_str(),
-                                               inCoords,
-                                               &builder);
+                    fEffects[s] = builder.createAndEmitGLEffect(
+                                                    *stages[s],
+                                                    fDesc.fEffectKeys[s],
+                                                    inCoverage.size() ? inCoverage.c_str() : NULL,
+                                                    outCoverage.c_str(),
+                                                    inCoords,
+                                                    &fUniformHandles.fSamplerUnis[s]);
                     builder.setNonStage();
                     inCoverage = outCoverage;
                 }
@@ -803,7 +807,7 @@
 
     builder.finished(fProgramID);
     this->initSamplerUniforms();
-    fUniforms.fRTHeight = builder.getRTHeightUniform();
+    fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
 
     return true;
 }
@@ -874,72 +878,48 @@
 
 void GrGLProgram::initSamplerUniforms() {
     GL_CALL(UseProgram(fProgramID));
+    // We simply bind the uniforms to successive texture units beginning at 0. setData() assumes this
+    // behavior.
+    GrGLint texUnitIdx = 0;
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        int count = fUniforms.fStages[s].fSamplerUniforms.count();
-        // FIXME: We're still always reserving one texture per stage. After GrTextureParams are
-        // expressed by the effect rather than the GrEffectStage we can move texture binding
-        // into GrGLProgram and it should be easier to fix this.
-        GrAssert(count <= 1);
-        for (int t = 0; t < count; ++t) {
-            UniformHandle uh = fUniforms.fStages[s].fSamplerUniforms[t];
-            if (GrGLUniformManager::kInvalidUniformHandle != uh) {
-                fUniformManager.setSampler(uh, s);
+        int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+        for (int u = 0; u < numSamplers; ++u) {
+            UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+            if (GrGLUniformManager::kInvalidUniformHandle != handle) {
+                fUniformManager.setSampler(handle, texUnitIdx);
+                ++texUnitIdx;
             }
         }
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Stage code generation
 
-// TODO: Move this function to GrGLShaderBuilder
-GrGLEffect* GrGLProgram::GenStageCode(const GrEffectStage& stage,
-                                      GrGLEffect::EffectKey key,
-                                      StageUniforms* uniforms,
-                                      const char* fsInColor, // NULL means no incoming color
-                                      const char* fsOutColor,
-                                      const char* vsInCoord,
-                                      GrGLShaderBuilder* builder) {
+void GrGLProgram::setData(GrGpuGL* gpu) {
+    const GrDrawState& drawState = gpu->getDrawState();
 
-    const GrEffect* effect = stage.getEffect();
-    GrGLEffect* glEffect = effect->getFactory().createGLInstance(*effect);
-
-    // setup texture samplers for GL effect
-    int numTextures = effect->numTextures();
-    SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
-    textureSamplers.push_back_n(numTextures);
-    for (int i = 0; i < numTextures; ++i) {
-        textureSamplers[i].init(builder, &effect->textureAccess(i));
-        uniforms->fSamplerUniforms.push_back(textureSamplers[i].fSamplerUniform);
-    }
-
-    // Enclose custom code in a block to avoid namespace conflicts
-    builder->fVSCode.appendf("\t{ // %s\n", glEffect->name());
-    builder->fFSCode.appendf("\t{ // %s \n", glEffect->name());
-    glEffect->emitCode(builder,
-                       stage,
-                       key,
-                       vsInCoord,
-                       fsOutColor,
-                       fsInColor,
-                       textureSamplers);
-    builder->fVSCode.appendf("\t}\n");
-    builder->fFSCode.appendf("\t}\n");
-
-    return glEffect;
-}
-
-void GrGLProgram::setData(const GrDrawState& drawState) {
     int rtHeight = drawState.getRenderTarget()->height();
-    if (GrGLUniformManager::kInvalidUniformHandle != fUniforms.fRTHeight && fRTHeight != rtHeight) {
-        fUniformManager.set1f(fUniforms.fRTHeight, SkIntToScalar(rtHeight));
+    if (GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fRTHeightUni &&
+        fRTHeight != rtHeight) {
+        fUniformManager.set1f(fUniformHandles.fRTHeightUni, SkIntToScalar(rtHeight));
         fRTHeight = rtHeight;
     }
+    GrGLint texUnitIdx = 0;
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
         if (NULL != fEffects[s]) {
             const GrEffectStage& stage = drawState.getStage(s);
             GrAssert(NULL != stage.getEffect());
             fEffects[s]->setData(fUniformManager, stage);
+            int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+            for (int u = 0; u < numSamplers; ++u) {
+                UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+                if (GrGLUniformManager::kInvalidUniformHandle != handle) {
+                    const GrTextureAccess& access = (*stage.getEffect())->textureAccess(u);
+                    GrGLTexture* texture = static_cast<GrGLTexture*>(access.getTexture());
+                    gpu->bindTexture(texUnitIdx, access.getParams(), texture);
+                    ++texUnitIdx;
+                }
+            }
         }
     }
 }
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 3902a8c..513bf75 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -48,7 +48,9 @@
 
     virtual ~GrGLProgram();
 
-    /** Call to abandon GL objects owned by this program */
+    /**
+     * Call to abandon GL objects owned by this program.
+     */
     void abandon();
 
     /**
@@ -68,10 +70,11 @@
     static int TexCoordAttributeIdx(int tcIdx) { return 4 + tcIdx; }
 
     /**
-     * This function uploads uniforms and calls each GrEffect's setData. It is called before a draw
-     * occurs using the program after the program has already been bound.
+     * This function uploads uniforms and calls each GrGLEffect's setData. It is called before a
+     * draw occurs using the program after the program has already been bound. It also uses the
+     * GrGpuGL object to bind the textures required by the GrGLEffects.
      */
-    void setData(const GrDrawState& drawState);
+    void setData(GrGpuGL*);
 
     // Parameters that affect code generation
     // This structs should be kept compact; it is input to an expensive hash key generator.
@@ -114,30 +117,24 @@
         bool                        fDiscardIfOutsideEdge;
 
         // stripped of bits that don't affect program generation
-        GrVertexLayout fVertexLayout;
+        GrVertexLayout              fVertexLayout;
 
         /** Non-zero if this stage has an effect */
-        GrGLEffect::EffectKey fEffectKeys[GrDrawState::kNumStages];
+        GrGLEffect::EffectKey       fEffectKeys[GrDrawState::kNumStages];
 
         // To enable experimental geometry shader code (not for use in
         // production)
 #if GR_GL_EXPERIMENTAL_GS
-        bool fExperimentalGS;
+        bool                        fExperimentalGS;
 #endif
-
-        uint8_t fColorInput;        // casts to enum ColorInput
-        uint8_t fCoverageInput;     // casts to enum CoverageInput
-        uint8_t fDualSrcOutput;     // casts to enum DualSrcOutput
-        int8_t fFirstCoverageStage;
-        SkBool8 fEmitsPointSize;
-
-        uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
+        uint8_t                     fColorInput;            // casts to enum ColorInput
+        uint8_t                     fCoverageInput;         // casts to enum ColorInput
+        uint8_t                     fDualSrcOutput;         // casts to enum DualSrcOutput
+        int8_t                      fFirstCoverageStage;
+        SkBool8                     fEmitsPointSize;
+        uint8_t                     fColorFilterXfermode;   // casts to enum SkXfermode::Mode
     };
-    GR_STATIC_ASSERT(!(sizeof(Desc) % 4));
-
 private:
-    struct StageUniforms;
-
     GrGLProgram(const GrGLContextInfo& gl,
                 const Desc& desc,
                 const GrEffectStage* stages[]);
@@ -151,14 +148,6 @@
 
     void genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
 
-    static GrGLEffect* GenStageCode(const GrEffectStage& stage,
-                                    GrGLEffect::EffectKey key,
-                                    StageUniforms* stageUniforms, // TODO: Eliminate this
-                                    const char* fsInColor, // NULL means no incoming color
-                                    const char* fsOutColor,
-                                    const char* vsInCoord,
-                                    GrGLShaderBuilder* builder);
-
     void genGeometryShader(GrGLShaderBuilder* segments) const;
 
     typedef GrGLUniformManager::UniformHandle UniformHandle;
@@ -183,41 +172,39 @@
 
     const char* adjustInColor(const SkString& inColor) const;
 
-    struct StageUniforms {
-        SkTArray<UniformHandle, true> fSamplerUniforms;
-    };
+    typedef SkSTArray<4, UniformHandle, true> SamplerUniSArray;
 
-    struct Uniforms {
-        UniformHandle fViewMatrixUni;
-        UniformHandle fColorUni;
-        UniformHandle fCoverageUni;
-        UniformHandle fColorFilterUni;
+    struct UniformHandles {
+        UniformHandle       fViewMatrixUni;
+        UniformHandle       fColorUni;
+        UniformHandle       fCoverageUni;
+        UniformHandle       fColorFilterUni;
         // We use the render target height to provide a y-down frag coord when specifying
         // origin_upper_left is not supported.
-        UniformHandle fRTHeight;
-        StageUniforms fStages[GrDrawState::kNumStages];
-        Uniforms() {
+        UniformHandle       fRTHeightUni;
+        // An array of sampler uniform handles for each effect.
+        SamplerUniSArray    fSamplerUnis[GrDrawState::kNumStages];
+
+        UniformHandles() {
             fViewMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
             fColorUni = GrGLUniformManager::kInvalidUniformHandle;
             fCoverageUni = GrGLUniformManager::kInvalidUniformHandle;
             fColorFilterUni = GrGLUniformManager::kInvalidUniformHandle;
-            fRTHeight = GrGLUniformManager::kInvalidUniformHandle;
+            fRTHeightUni = GrGLUniformManager::kInvalidUniformHandle;
         }
     };
 
-    // IDs
-    GrGLuint    fVShaderID;
-    GrGLuint    fGShaderID;
-    GrGLuint    fFShaderID;
-    GrGLuint    fProgramID;
-
+    // GL IDs
+    GrGLuint                    fVShaderID;
+    GrGLuint                    fGShaderID;
+    GrGLuint                    fFShaderID;
+    GrGLuint                    fProgramID;
     // The matrix sent to GL is determined by both the client's matrix and
     // the size of the viewport.
-    SkMatrix  fViewMatrix;
-    SkISize   fViewportSize;
+    SkMatrix                    fViewMatrix;
+    SkISize                     fViewportSize;
 
-    // these reflect the current values of uniforms
-    // (GL uniform values travel with program)
+    // these reflect the current values of uniforms (GL uniform values travel with program)
     GrColor                     fColor;
     GrColor                     fCoverage;
     GrColor                     fColorFilterColor;
@@ -225,11 +212,11 @@
 
     GrGLEffect*                 fEffects[GrDrawState::kNumStages];
 
-    Desc fDesc;
+    Desc                        fDesc;
     const GrGLContextInfo&      fContextInfo;
 
     GrGLUniformManager          fUniformManager;
-    Uniforms                    fUniforms;
+    UniformHandles              fUniformHandles;
 
     friend class GrGpuGL; // TODO: remove this by adding getters and moving functionality.
 
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index 9bbc842..47128e7 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -20,7 +20,6 @@
     fTexFBOID               = desc.fTexFBOID;
     fMSColorRenderbufferID  = desc.fMSColorRenderbufferID;
     fViewport               = viewport;
-    fOwnIDs                 = desc.fOwnIDs;
     fTexIDObj               = texID;
     GrSafeRef(fTexIDObj);
 }
@@ -46,12 +45,13 @@
                                    GrGLTexID* texID,
                                    GrGLTexture* texture)
     : INHERITED(gpu,
+                desc.fIsWrapped,
                 texture,
                 MakeDesc(kNone_GrTextureFlags,
                          viewport.fWidth, viewport.fHeight,
                          desc.fConfig, desc.fSampleCnt),
                 texture->origin()) {
-    GrAssert(kBottomLeft_Origin == texture->origin());
+    GrAssert(kBottomLeft_GrSurfaceOrigin == texture->origin());
     GrAssert(NULL != texID);
     GrAssert(NULL != texture);
     // FBO 0 can't also be a texture, right?
@@ -69,17 +69,18 @@
                                    const Desc& desc,
                                    const GrGLIRect& viewport)
     : INHERITED(gpu,
+                desc.fIsWrapped,
                 NULL,
                 MakeDesc(kNone_GrTextureFlags,
                          viewport.fWidth, viewport.fHeight,
                          desc.fConfig, desc.fSampleCnt),
-                kBottomLeft_Origin) {
+                kBottomLeft_GrSurfaceOrigin) {
     this->init(desc, viewport, NULL);
 }
 
 void GrGLRenderTarget::onRelease() {
     GPUGL->notifyRenderTargetDelete(this);
-    if (fOwnIDs) {
+    if (!this->isWrapped()) {
         if (fTexFBOID) {
             GL_CALL(DeleteFramebuffers(1, &fTexFBOID));
         }
@@ -108,4 +109,3 @@
     }
     INHERITED::onAbandon();
 }
-
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
index b26223e..9a39ca1 100644
--- a/src/gpu/gl/GrGLRenderTarget.h
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -28,7 +28,7 @@
         GrGLuint      fRTFBOID;
         GrGLuint      fTexFBOID;
         GrGLuint      fMSColorRenderbufferID;
-        bool          fOwnIDs;
+        bool          fIsWrapped;
         GrPixelConfig fConfig;
         int           fSampleCnt;
     };
@@ -89,10 +89,6 @@
 
     GrGLuint      fMSColorRenderbufferID;
 
-    // Should this object delete IDs when it is destroyed or does someone
-    // else own them.
-    bool        fOwnIDs;
-
     // when we switch to this render target we want to set the viewport to
     // only render to to content area (as opposed to the whole allocation) and
     // we want the rendering to be at top left (GL has origin in bottom left)
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
index c0d3d5e..4559fdd 100644
--- a/src/gpu/gl/GrGLSL.h
+++ b/src/gpu/gl/GrGLSL.h
@@ -57,7 +57,7 @@
 };
 
 namespace {
-inline int GrSLTypeToVecLength(GrSLType type) {
+static inline int GrSLTypeToVecLength(GrSLType type) {
     static const int kVecLengths[] = {
         0, // kVoid_GrSLType
         1, // kFloat_GrSLType
@@ -72,14 +72,14 @@
     return kVecLengths[type];
 }
 
-const char* GrGLSLOnesVecf(int count) {
+static inline const char* GrGLSLOnesVecf(int count) {
     static const char* kONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
                                      "vec3(1,1,1)", "vec4(1,1,1,1)"};
     GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(kONESVEC));
     return kONESVEC[count];
 }
 
-const char* GrGLSLZerosVecf(int count) {
+static inline const char* GrGLSLZerosVecf(int count) {
     static const char* kZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
                                       "vec3(0,0,0)", "vec4(0,0,0,0)"};
     GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(kZEROSVEC));
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index 8239740..a8514ad 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -172,7 +172,7 @@
                                                                      int count,
                                                                      const char** outName) {
     GrAssert(name && strlen(name));
-    static const uint32_t kVisibilityMask = kVertex_ShaderType | kFragment_ShaderType;
+    SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_ShaderType | kFragment_ShaderType);
     GrAssert(0 == (~kVisibilityMask & visibility));
     GrAssert(0 != visibility);
 
@@ -412,3 +412,39 @@
 void GrGLShaderBuilder::finished(GrGLuint programID) {
     fUniformManager.getUniformLocations(programID, fUniforms);
 }
+
+GrGLEffect* GrGLShaderBuilder::createAndEmitGLEffect(
+                                const GrEffectStage& stage,
+                                GrGLEffect::EffectKey key,
+                                const char* fsInColor,
+                                const char* fsOutColor,
+                                const char* vsInCoord,
+                                SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles) {
+    GrAssert(NULL != stage.getEffect());
+
+    const GrEffectRef& effect = *stage.getEffect();
+    int numTextures = effect->numTextures();
+    SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
+    textureSamplers.push_back_n(numTextures);
+    for (int i = 0; i < numTextures; ++i) {
+        textureSamplers[i].init(this, &effect->textureAccess(i), i);
+        samplerHandles->push_back(textureSamplers[i].fSamplerUniform);
+    }
+
+    GrGLEffect* glEffect = effect->getFactory().createGLInstance(effect);
+
+    // Enclose custom code in a block to avoid namespace conflicts
+    this->fVSCode.appendf("\t{ // %s\n", glEffect->name());
+    this->fFSCode.appendf("\t{ // %s \n", glEffect->name());
+    glEffect->emitCode(this,
+                       stage,
+                       key,
+                       vsInCoord,
+                       fsOutColor,
+                       fsInColor,
+                       textureSamplers);
+    this->fVSCode.appendf("\t}\n");
+    this->fFSCode.appendf("\t}\n");
+
+    return glEffect;
+}
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index 852079a..87136cd 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -11,7 +11,6 @@
 #include "GrAllocator.h"
 #include "GrBackendEffectFactory.h"
 #include "GrEffect.h"
-#include "gl/GrGLShaderVar.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLUniformManager.h"
 
@@ -24,7 +23,7 @@
 class GrGLShaderBuilder {
 public:
     /**
-     * Used by GrGLEffects to add texture reads to their shader code.
+     * Passed to GrGLEffects to add texture reads to their shader code.
      */
     class TextureSampler {
     public:
@@ -46,15 +45,19 @@
         const GrTextureAccess* textureAccess() const { return fTextureAccess; }
 
     private:
-        void init(GrGLShaderBuilder* builder, const GrTextureAccess* access) {
+        // The idx param is used to ensure multiple samplers within a single effect have unique
+        // uniform names.
+        void init(GrGLShaderBuilder* builder, const GrTextureAccess* access, int idx) {
             GrAssert(NULL == fTextureAccess);
             GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform);
 
             GrAssert(NULL != builder);
             GrAssert(NULL != access);
+            SkString name;
+            name.printf("Sampler%d_", idx);
             fSamplerUniform = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                                   kSampler2D_GrSLType,
-                                                  "Sampler");
+                                                  name.c_str());
             GrAssert(GrGLUniformManager::kInvalidUniformHandle != fSamplerUniform);
 
             fTextureAccess = access;
@@ -160,25 +163,31 @@
       */
     const GrGLShaderVar& positionAttribute() const { return *fPositionVar; }
 
+    /**
+     * Interfaces used by GrGLProgram.
+     * TODO: Hide these from the GrEffects using friend or splitting this into two related classes.
+     * Also, GrGLProgram's shader string construction should be moved to this class.
+     */
+
     /** Called after building is complete to get the final shader string. */
     void getShader(ShaderType, SkString*) const;
 
-    /**
-     * TODO: Make this do all the compiling, linking, etc. Hide from the GrEffects
-     */
-    void finished(GrGLuint programID);
-
-    /**
-     * Sets the current stage (used to make variable names unique).
-     * TODO: Hide from the GrEffects
-     */
     void setCurrentStage(int stageIdx) { fCurrentStageIdx = stageIdx; }
     void setNonStage() { fCurrentStageIdx = kNonStageIdx; }
-
+    // TODO: move remainder of shader code generation to this class and call this privately
+    // Handles of sampler uniforms generated for the effect are appended to samplerHandles.
+    GrGLEffect* createAndEmitGLEffect(
+                                const GrEffectStage& stage,
+                                GrBackendEffectFactory::EffectKey key,
+                                const char* fsInColor, // NULL means no incoming color
+                                const char* fsOutColor,
+                                const char* vsInCoord,
+                                SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles);
     GrGLUniformManager::UniformHandle getRTHeightUniform() const { return fRTHeightUniform; }
+    // TODO: Make this do all the compiling, linking, etc.
+    void finished(GrGLuint programID);
 
 private:
-
     typedef GrTAllocator<GrGLShaderVar> VarArray;
 
     void appendDecls(const VarArray&, SkString*) const;
diff --git a/src/gpu/gl/GrGLStencilBuffer.cpp b/src/gpu/gl/GrGLStencilBuffer.cpp
index 030b54e..d9322c2 100644
--- a/src/gpu/gl/GrGLStencilBuffer.cpp
+++ b/src/gpu/gl/GrGLStencilBuffer.cpp
@@ -22,7 +22,7 @@
 }
 
 void GrGLStencilBuffer::onRelease() {
-    if (0 != fRenderbufferID) {
+    if (0 != fRenderbufferID && !this->isWrapped()) {
         GrGpuGL* gpuGL = (GrGpuGL*) this->getGpu();
         const GrGLInterface* gl = gpuGL->glInterface();
         GR_GL_CALL(gl, DeleteRenderbuffers(1, &fRenderbufferID));
@@ -37,5 +37,3 @@
 
     INHERITED::onAbandon();
 }
-
-
diff --git a/src/gpu/gl/GrGLStencilBuffer.h b/src/gpu/gl/GrGLStencilBuffer.h
index 2d175f6..2bf33ef 100644
--- a/src/gpu/gl/GrGLStencilBuffer.h
+++ b/src/gpu/gl/GrGLStencilBuffer.h
@@ -23,11 +23,13 @@
         bool      fPacked;
     };
 
-    GrGLStencilBuffer(GrGpu* gpu, GrGLint rbid,
+    GrGLStencilBuffer(GrGpu* gpu,
+                      bool isWrapped,
+                      GrGLint rbid,
                       int width, int height,
                       int sampleCnt,
                       const Format& format)
-        : GrStencilBuffer(gpu, width, height, format.fStencilBits, sampleCnt)
+        : GrStencilBuffer(gpu, isWrapped, width, height, format.fStencilBits, sampleCnt)
         , fFormat(format)
         , fRenderbufferID(rbid) {
     }
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index 6c815e8..f798b31 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -25,10 +25,10 @@
     fTexIDObj           = SkNEW_ARGS(GrGLTexID,
                                      (GPUGL->glInterface(),
                                       textureDesc.fTextureID,
-                                      textureDesc.fOwnsID));
+                                      textureDesc.fIsWrapped));
 
     if (NULL != rtDesc) {
-        GrAssert(kBottomLeft_Origin == textureDesc.fOrigin);
+        GrAssert(kBottomLeft_GrSurfaceOrigin == textureDesc.fOrigin);
         GrGLIRect vp;
         vp.fLeft   = 0;
         vp.fWidth  = textureDesc.fWidth;
@@ -42,14 +42,14 @@
 
 GrGLTexture::GrGLTexture(GrGpuGL* gpu,
                          const Desc& textureDesc)
-    : INHERITED(gpu, textureDesc, textureDesc.fOrigin) {
+    : INHERITED(gpu, textureDesc.fIsWrapped, textureDesc, textureDesc.fOrigin) {
     this->init(gpu, textureDesc, NULL);
 }
 
 GrGLTexture::GrGLTexture(GrGpuGL* gpu,
                          const Desc& textureDesc,
                          const GrGLRenderTarget::Desc& rtDesc)
-    : INHERITED(gpu, textureDesc, textureDesc.fOrigin) {
+    : INHERITED(gpu, textureDesc.fIsWrapped, textureDesc, textureDesc.fOrigin) {
     this->init(gpu, textureDesc, &rtDesc);
 }
 
@@ -74,4 +74,3 @@
 GrBackendObject GrGLTexture::getTextureHandle() const {
     return fTexIDObj->id();
 }
-
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index 4666bfb..2314821 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -19,14 +19,14 @@
 public:
     SK_DECLARE_INST_COUNT(GrGLTexID)
 
-    GrGLTexID(const GrGLInterface* gl, GrGLuint texID, bool ownsID)
+    GrGLTexID(const GrGLInterface* gl, GrGLuint texID, bool isWrapped)
         : fGL(gl)
         , fTexID(texID)
-        , fOwnsID(ownsID) {
+        , fIsWrapped(isWrapped) {
     }
 
     virtual ~GrGLTexID() {
-        if (0 != fTexID && fOwnsID) {
+        if (0 != fTexID && !fIsWrapped) {
             GR_GL_CALL(fGL, DeleteTextures(1, &fTexID));
         }
     }
@@ -37,7 +37,7 @@
 private:
     const GrGLInterface* fGL;
     GrGLuint             fTexID;
-    bool                 fOwnsID;
+    bool                 fIsWrapped;
 
     typedef GrRefCnt INHERITED;
 };
@@ -58,8 +58,8 @@
 
     struct Desc : public GrTextureDesc {
         GrGLuint        fTextureID;
-        bool            fOwnsID;
-        Origin          fOrigin;
+        bool            fIsWrapped;
+        GrSurfaceOrigin fOrigin;
     };
 
     // creates a texture that is also an RT
diff --git a/src/gpu/gl/GrGLUtil.cpp b/src/gpu/gl/GrGLUtil.cpp
index 55236aa..3f110cc 100644
--- a/src/gpu/gl/GrGLUtil.cpp
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -205,4 +205,3 @@
     GR_GL_CALL_RET(gl, v, GetString(GR_GL_VENDOR));
     return GrGLGetVendorFromString((const char*) v);
 }
-
diff --git a/src/gpu/gl/GrGLVertexBuffer.cpp b/src/gpu/gl/GrGLVertexBuffer.cpp
index 7cee29e..1512fef 100644
--- a/src/gpu/gl/GrGLVertexBuffer.cpp
+++ b/src/gpu/gl/GrGLVertexBuffer.cpp
@@ -15,17 +15,18 @@
 #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
 
 GrGLVertexBuffer::GrGLVertexBuffer(GrGpuGL* gpu,
+                                   bool isWrapped,
                                    GrGLuint id,
                                    size_t sizeInBytes,
                                    bool dynamic)
-    : INHERITED(gpu, sizeInBytes, dynamic)
+    : INHERITED(gpu, isWrapped, sizeInBytes, dynamic)
     , fBufferID(id)
     , fLockPtr(NULL) {
 }
 
 void GrGLVertexBuffer::onRelease() {
     // make sure we've not been abandoned
-    if (fBufferID) {
+    if (fBufferID && !this->isWrapped()) {
         GPUGL->notifyVertexBufferDelete(this);
         GL_CALL(DeleteBuffers(1, &fBufferID));
         fBufferID = 0;
@@ -146,4 +147,3 @@
 #endif
     return true;
 }
-
diff --git a/src/gpu/gl/GrGLVertexBuffer.h b/src/gpu/gl/GrGLVertexBuffer.h
index bb829d3..17b0283 100644
--- a/src/gpu/gl/GrGLVertexBuffer.h
+++ b/src/gpu/gl/GrGLVertexBuffer.h
@@ -29,6 +29,7 @@
 
 protected:
     GrGLVertexBuffer(GrGpuGL* gpu,
+                     bool isWrapped,
                      GrGLuint id,
                      size_t sizeInBytes,
                      bool dynamic);
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index f01fd3f..7ca07a0 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -485,6 +485,12 @@
         return NULL;
     }
 
+    // FIXME:  add support for TopLeft RT's by flipping all draws.
+    if (desc.fFlags & kRenderTarget_GrBackendTextureFlag &&
+        kBottomLeft_GrSurfaceOrigin != desc.fOrigin) {
+        return NULL;
+    }
+
     int maxSize = this->getCaps().maxTextureSize();
     if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
         return NULL;
@@ -498,8 +504,8 @@
     glTexDesc.fConfig = desc.fConfig;
     glTexDesc.fSampleCnt = desc.fSampleCnt;
     glTexDesc.fTextureID = static_cast<GrGLuint>(desc.fTextureHandle);
-    glTexDesc.fOwnsID = false;
-    glTexDesc.fOrigin = GrSurface::kBottomLeft_Origin;
+    glTexDesc.fIsWrapped = true;
+    glTexDesc.fOrigin = desc.fOrigin;
 
     GrGLTexture* texture = NULL;
     if (desc.fFlags & kRenderTarget_GrBackendTextureFlag) {
@@ -507,7 +513,6 @@
         glRTDesc.fRTFBOID = 0;
         glRTDesc.fTexFBOID = 0;
         glRTDesc.fMSColorRenderbufferID = 0;
-        glRTDesc.fOwnIDs = true;
         glRTDesc.fConfig = desc.fConfig;
         glRTDesc.fSampleCnt = desc.fSampleCnt;
         if (!this->createRenderTargetObjects(glTexDesc.fWidth,
@@ -535,7 +540,7 @@
     glDesc.fMSColorRenderbufferID = 0;
     glDesc.fTexFBOID = GrGLRenderTarget::kUnresolvableFBOID;
     glDesc.fSampleCnt = desc.fSampleCnt;
-    glDesc.fOwnIDs = false;
+    glDesc.fIsWrapped = true;
     GrGLIRect viewport;
     viewport.fLeft   = 0;
     viewport.fBottom = 0;
@@ -550,8 +555,10 @@
         format.fPacked = false;
         format.fStencilBits = desc.fStencilBits;
         format.fTotalBits = desc.fStencilBits;
+        static const bool kIsSBWrapped = false;
         GrGLStencilBuffer* sb = SkNEW_ARGS(GrGLStencilBuffer,
                                            (this,
+                                            kIsSBWrapped,
                                             0,
                                             desc.fWidth,
                                             desc.fHeight,
@@ -674,7 +681,7 @@
     bool swFlipY = false;
     bool glFlipY = false;
     if (NULL != data) {
-        if (GrSurface::kBottomLeft_Origin == desc.fOrigin) {
+        if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
             if (this->glCaps().unpackFlipYSupport()) {
                 glFlipY = true;
             } else {
@@ -827,7 +834,7 @@
     desc->fMSColorRenderbufferID = 0;
     desc->fRTFBOID = 0;
     desc->fTexFBOID = 0;
-    desc->fOwnIDs = true;
+    desc->fIsWrapped = false;
 
     GrGLenum status;
 
@@ -862,7 +869,7 @@
     // below here we may bind the FBO
     fHWBoundRenderTarget = NULL;
     if (desc->fRTFBOID != desc->fTexFBOID) {
-        GrAssert(desc->fSampleCnt > 1);
+        GrAssert(desc->fSampleCnt > 0);
         GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER,
                                desc->fMSColorRenderbufferID));
         if (!renderbuffer_storage_msaa(fGLContextInfo,
@@ -941,13 +948,12 @@
     glTexDesc.fHeight = desc.fHeight;
     glTexDesc.fConfig = desc.fConfig;
     glTexDesc.fSampleCnt = desc.fSampleCnt;
-
-    glTexDesc.fOwnsID = true;
+    glTexDesc.fIsWrapped = false;
 
     glRTDesc.fMSColorRenderbufferID = 0;
     glRTDesc.fRTFBOID = 0;
     glRTDesc.fTexFBOID = 0;
-    glRTDesc.fOwnIDs = true;
+    glRTDesc.fIsWrapped = false;
     glRTDesc.fConfig = glTexDesc.fConfig;
 
     bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit);
@@ -957,7 +963,7 @@
     // We keep GrRenderTargets in GL's normal orientation so that they
     // can be drawn to by the outside world without the client having
     // to render upside down.
-    glTexDesc.fOrigin = renderTarget ? GrSurface::kBottomLeft_Origin : GrSurface::kTopLeft_Origin;
+    glTexDesc.fOrigin = renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
 
     glRTDesc.fSampleCnt = desc.fSampleCnt;
     if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() &&
@@ -1112,8 +1118,9 @@
             // whatever sizes GL gives us. In that case we query for the size.
             GrGLStencilBuffer::Format format = sFmt;
             get_stencil_rb_sizes(this->glInterface(), sbID, &format);
+            static const bool kIsWrapped = false;
             SkAutoTUnref<GrStencilBuffer> sb(SkNEW_ARGS(GrGLStencilBuffer,
-                                                  (this, sbID, width, height,
+                                                  (this, kIsWrapped, sbID, width, height,
                                                   samples, format)));
             if (this->attachStencilBufferToRenderTarget(sb, rt)) {
                 fLastSuccessfulStencilFmtIdx = sIdx;
@@ -1214,8 +1221,9 @@
             fHWGeometryState.fVertexBuffer = NULL;
             return NULL;
         }
+        static const bool kIsWrapped = false;
         GrGLVertexBuffer* vertexBuffer = SkNEW_ARGS(GrGLVertexBuffer,
-                                                    (this, id,
+                                                    (this, kIsWrapped, id,
                                                      size, dynamic));
         fHWGeometryState.fVertexBuffer = vertexBuffer;
         return vertexBuffer;
@@ -1242,8 +1250,9 @@
             fHWGeometryState.fIndexBuffer = NULL;
             return NULL;
         }
+        static const bool kIsWrapped = false;
         GrIndexBuffer* indexBuffer = SkNEW_ARGS(GrGLIndexBuffer,
-                                                (this, id, size, dynamic));
+                                                (this, kIsWrapped, id, size, dynamic));
         fHWGeometryState.fIndexBuffer = indexBuffer;
         return indexBuffer;
     }
@@ -1597,53 +1606,28 @@
     #endif
 #endif
 
-void GrGpuGL::onGpuDrawIndexed(GrPrimitiveType type,
-                               uint32_t startVertex,
-                               uint32_t startIndex,
-                               uint32_t vertexCount,
-                               uint32_t indexCount) {
-    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+void GrGpuGL::onGpuDraw(const DrawInfo& info) {
+    int extraStartIndexOffset;
+    this->setupGeometry(info, &extraStartIndexOffset);
 
-    GrGLvoid* indices = (GrGLvoid*)(sizeof(uint16_t) * startIndex);
-
-    GrAssert(NULL != fHWGeometryState.fIndexBuffer);
+    GrAssert((size_t)info.primitiveType() < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
     GrAssert(NULL != fHWGeometryState.fVertexBuffer);
 
-    // our setupGeometry better have adjusted this to zero since
-    // DrawElements always draws from the begining of the arrays for idx 0.
-    GrAssert(0 == startVertex);
-
-    GL_CALL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
-                         GR_GL_UNSIGNED_SHORT, indices));
-#if SWAP_PER_DRAW
-    glFlush();
-    #if GR_MAC_BUILD
-        aglSwapBuffers(aglGetCurrentContext());
-        int set_a_break_pt_here = 9;
-        aglSwapBuffers(aglGetCurrentContext());
-    #elif GR_WIN32_BUILD
-        SwapBuf();
-        int set_a_break_pt_here = 9;
-        SwapBuf();
-    #endif
-#endif
-}
-
-void GrGpuGL::onGpuDrawNonIndexed(GrPrimitiveType type,
-                                  uint32_t startVertex,
-                                  uint32_t vertexCount) {
-    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
-
-    GrAssert(NULL != fHWGeometryState.fVertexBuffer);
-
-    // our setupGeometry better have adjusted this to zero.
-    // DrawElements doesn't take an offset so we always adjus the startVertex.
-    GrAssert(0 == startVertex);
-
-    // pass 0 for parameter first. We have to adjust gl*Pointer() to
-    // account for startVertex in the DrawElements case. So we always
-    // rely on setupGeometry to have accounted for startVertex.
-    GL_CALL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
+    if (info.isIndexed()) {
+        GrAssert(NULL != fHWGeometryState.fIndexBuffer);
+        GrGLvoid* indices = (GrGLvoid*)(sizeof(uint16_t) * (info.startIndex() +
+                                                            extraStartIndexOffset));
+        // info.startVertex() was accounted for by setupGeometry.
+        GL_CALL(DrawElements(gPrimitiveType2GLMode[info.primitiveType()],
+                             info.indexCount(),
+                             GR_GL_UNSIGNED_SHORT,
+                             indices));
+    } else {
+        // Pass 0 for parameter first. We have to adjust glVertexAttribPointer() to account for
+        // startVertex in the DrawElements case. So we always rely on setupGeometry to have
+        // accounted for startVertex.
+        GL_CALL(DrawArrays(gPrimitiveType2GLMode[info.primitiveType()], 0, info.vertexCount()));
+    }
 #if SWAP_PER_DRAW
     glFlush();
     #if GR_MAC_BUILD
@@ -1679,7 +1663,6 @@
 }
 }
 
-
 void GrGpuGL::setStencilPathSettings(const GrPath&,
                                      SkPath::FillType fill,
                                      GrStencilSettings* settings) {
@@ -2024,43 +2007,25 @@
 
 }
 
-void GrGpuGL::flushBoundTextureAndParams(int stageIdx) {
-    GrDrawState* drawState = this->drawState();
-    // FIXME: Assuming at most one texture per effect
-    const GrEffect* effect = drawState->stage(stageIdx)->getEffect();
-    if (effect->numTextures() > 0) {
-        GrGLTexture* nextTexture =  static_cast<GrGLTexture*>(effect->texture(0));
-        if (NULL != nextTexture) {
-            const GrTextureParams& texParams = effect->textureAccess(0).getParams();
-            this->flushBoundTextureAndParams(stageIdx, texParams, nextTexture);
-        }
-    }
-}
+void GrGpuGL::bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture) {
+    GrAssert(NULL != texture);
 
-void GrGpuGL::flushBoundTextureAndParams(int stageIdx,
-                                         const GrTextureParams& params,
-                                         GrGLTexture* nextTexture) {
-
-    // true for now, but maybe not with GrEffect.
-    GrAssert(NULL != nextTexture);
     // If we created a rt/tex and rendered to it without using a texture and now we're texturing
     // from the rt it will still be the last bound texture, but it needs resolving. So keep this
     // out of the "last != next" check.
-    GrGLRenderTarget* texRT =  static_cast<GrGLRenderTarget*>(nextTexture->asRenderTarget());
+    GrGLRenderTarget* texRT =  static_cast<GrGLRenderTarget*>(texture->asRenderTarget());
     if (NULL != texRT) {
         this->onResolveRenderTarget(texRT);
     }
 
-    if (fHWBoundTextures[stageIdx] != nextTexture) {
-        this->setTextureUnit(stageIdx);
-        GL_CALL(BindTexture(GR_GL_TEXTURE_2D, nextTexture->textureID()));
-        //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
-        fHWBoundTextures[stageIdx] = nextTexture;
+    if (fHWBoundTextures[unitIdx] != texture) {
+        this->setTextureUnit(unitIdx);
+        GL_CALL(BindTexture(GR_GL_TEXTURE_2D, texture->textureID()));
+        fHWBoundTextures[unitIdx] = texture;
     }
 
     ResetTimestamp timestamp;
-    const GrGLTexture::TexParams& oldTexParams =
-                            nextTexture->getCachedTexParams(&timestamp);
+    const GrGLTexture::TexParams& oldTexParams = texture->getCachedTexParams(&timestamp);
     bool setAll = timestamp < this->getResetTimestamp();
     GrGLTexture::TexParams newTexParams;
 
@@ -2069,10 +2034,10 @@
     newTexParams.fWrapS = tile_to_gl_wrap(params.getTileModeX());
     newTexParams.fWrapT = tile_to_gl_wrap(params.getTileModeY());
     memcpy(newTexParams.fSwizzleRGBA,
-           GrGLShaderBuilder::GetTexParamSwizzle(nextTexture->config(), this->glCaps()),
+           GrGLShaderBuilder::GetTexParamSwizzle(texture->config(), this->glCaps()),
            sizeof(newTexParams.fSwizzleRGBA));
     if (setAll || newTexParams.fFilter != oldTexParams.fFilter) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
                               GR_GL_TEXTURE_MAG_FILTER,
                               newTexParams.fFilter));
@@ -2081,13 +2046,13 @@
                               newTexParams.fFilter));
     }
     if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
                               GR_GL_TEXTURE_WRAP_S,
                               newTexParams.fWrapS));
     }
     if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
                               GR_GL_TEXTURE_WRAP_T,
                               newTexParams.fWrapT));
@@ -2096,12 +2061,11 @@
         (setAll || memcmp(newTexParams.fSwizzleRGBA,
                           oldTexParams.fSwizzleRGBA,
                           sizeof(newTexParams.fSwizzleRGBA)))) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         set_tex_swizzle(newTexParams.fSwizzleRGBA,
                         this->glInterface());
     }
-    nextTexture->setCachedTexParams(newTexParams,
-                                    this->getResetTimestamp());
+    texture->setCachedTexParams(newTexParams, this->getResetTimestamp());
 }
 
 void GrGpuGL::flushMiscFixedFunctionState() {
@@ -2382,4 +2346,3 @@
         }
     }
 }
-
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 33834cf..3b8c16f 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -36,6 +36,11 @@
         return fGLContextInfo.glslGeneration();
     }
 
+    // Used by GrGLProgram to bind necessary textures for GrGLEffects.
+    void bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture);
+
+    bool programUnitTest();
+
     // GrGpu overrides
     virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
                                                             const SK_OVERRIDE;
@@ -51,10 +56,7 @@
 
     virtual void abandonResources() SK_OVERRIDE;
 
-    bool programUnitTest();
-
-
-protected:
+private:
     // GrGpu overrides
     virtual void onResetContext() SK_OVERRIDE;
 
@@ -94,14 +96,7 @@
 
     virtual void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE;
 
-    virtual void onGpuDrawIndexed(GrPrimitiveType type,
-                                  uint32_t startVertex,
-                                  uint32_t startIndex,
-                                  uint32_t vertexCount,
-                                  uint32_t indexCount) SK_OVERRIDE;
-    virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
-                                     uint32_t vertexCount,
-                                     uint32_t numVertices) SK_OVERRIDE;
+    virtual void onGpuDraw(const DrawInfo&) SK_OVERRIDE;
 
     virtual void setStencilPathSettings(const GrPath&,
                                         SkPath::FillType,
@@ -113,31 +108,26 @@
     virtual void clearStencilClip(const GrIRect& rect,
                                   bool insideClip) SK_OVERRIDE;
     virtual bool flushGraphicsState(DrawType) SK_OVERRIDE;
-    virtual void setupGeometry(int* startVertex,
-                               int* startIndex,
-                               int vertexCount,
-                               int indexCount) SK_OVERRIDE;
-
-private:
 
     const GrGLCaps& glCaps() const { return fGLContextInfo.caps(); }
 
     // binds texture unit in GL
     void setTextureUnit(int unitIdx);
 
-    // binds appropriate vertex and index buffers, also returns any extra
-    // extra verts or indices to offset by.
-    void setBuffers(bool indexed,
-                    int* extraVertexOffset,
-                    int* extraIndexOffset);
+    // Sets up vertex attribute pointers and strides. On return startIndexOffset specifies an
+    // offset into the index buffer to the first index to be read (in addition to
+    // info.startIndex()). It accounts for the fact that index buffer pool may have provided space
+    // in the middle of a larger index buffer.
+    void setupGeometry(const DrawInfo& info, int* startIndexOffset);
+    // binds appropriate vertex and index buffers, also returns any extra verts or indices to
+    // offset by based on how space was allocated in pool VB/IBs.
+    void setBuffers(bool indexed, int* extraVertexOffset, int* extraIndexOffset);
 
     // Subclasses should call this to flush the blend state.
-    // The params should be the final coeffecients to apply
+    // The params should be the final coefficients to apply
     // (after any blending optimizations or dual source blending considerations
     // have been accounted for).
-    void flushBlend(bool isLines,
-                    GrBlendCoeff srcCoeff,
-                    GrBlendCoeff dstCoeff);
+    void flushBlend(bool isLines, GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff);
 
     bool hasExtension(const char* ext) const {
         return fGLContextInfo.hasExtension(ext);
@@ -196,16 +186,6 @@
         const GrGLContextInfo&      fGL;
     };
 
-    // binds the texture and sets its texture params
-    // This may also perform a downsample on the src texture which may or may
-    // not modify the scissor test and rect. So in flushGraphicsState a
-    // call to flushScissor must occur after all textures have been flushed via
-    // this function.
-    void flushBoundTextureAndParams(int stage);
-    void flushBoundTextureAndParams(int stage,
-                                    const GrTextureParams& params,
-                                    GrGLTexture* nextTexture);
-
     // sets the color specified by GrDrawState::setColor()
     void flushColor(GrColor color);
 
@@ -370,4 +350,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 5850fc4..d5e8fbd 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -151,7 +151,9 @@
             SkScalarToFloat(m[SkMatrix::kMTransY]),
             SkScalarToFloat(m[SkMatrix::kMPersp2])
         };
-        fCurrentProgram->fUniformManager.setMatrix3f(fCurrentProgram->fUniforms.fViewMatrixUni, mt);
+        fCurrentProgram->fUniformManager.setMatrix3f(
+                                            fCurrentProgram->fUniformHandles.fViewMatrixUni,
+                                            mt);
         fCurrentProgram->fViewMatrix = vm;
         fCurrentProgram->fViewportSize = viewportSize;
     }
@@ -163,7 +165,7 @@
     const ProgramDesc& desc = fCurrentProgram->getDesc();
     const GrDrawState& drawState = this->getDrawState();
 
-    if (this->getVertexLayout() & kColor_VertexLayoutBit) {
+    if (this->getVertexLayout() & GrDrawState::kColor_VertexLayoutBit) {
         // color will be specified per-vertex as an attribute
         // invalidate the const vertex attrib color
         fHWConstAttribColor = GrColor_ILLEGAL;
@@ -183,9 +185,10 @@
                     // OpenGL ES doesn't support unsigned byte varieties of glUniform
                     GrGLfloat c[4];
                     GrColorToRGBAFloat(color, c);
-                    GrAssert(kInvalidUniformHandle !=  fCurrentProgram->fUniforms.fColorUni);
-                    fCurrentProgram->fUniformManager.set4fv(fCurrentProgram->fUniforms.fColorUni,
-                                                            0, 1, c);
+                    GrAssert(kInvalidUniformHandle !=  fCurrentProgram->fUniformHandles.fColorUni);
+                    fCurrentProgram->fUniformManager.set4fv(
+                                                        fCurrentProgram->fUniformHandles.fColorUni,
+                                                        0, 1, c);
                     fCurrentProgram->fColor = color;
                 }
                 break;
@@ -196,7 +199,7 @@
                 GrCrash("Unknown color type.");
         }
     }
-    UniformHandle filterColorUni = fCurrentProgram->fUniforms.fColorFilterUni;
+    UniformHandle filterColorUni = fCurrentProgram->fUniformHandles.fColorFilterUni;
     if (kInvalidUniformHandle != filterColorUni &&
         fCurrentProgram->fColorFilterColor != drawState.getColorFilterColor()) {
         GrGLfloat c[4];
@@ -211,7 +214,7 @@
     // const GrDrawState& drawState = this->getDrawState();
 
 
-    if (this->getVertexLayout() & kCoverage_VertexLayoutBit) {
+    if (this->getVertexLayout() & GrDrawState::kCoverage_VertexLayoutBit) {
         // coverage will be specified per-vertex as an attribute
         // invalidate the const vertex attrib coverage
         fHWConstAttribCoverage = GrColor_ILLEGAL;
@@ -234,9 +237,11 @@
                     // glUniform
                     GrGLfloat c[4];
                     GrColorToRGBAFloat(coverage, c);
-                    GrAssert(kInvalidUniformHandle !=  fCurrentProgram->fUniforms.fCoverageUni);
-                    fCurrentProgram->fUniformManager.set4fv(fCurrentProgram->fUniforms.fCoverageUni,
-                                                            0, 1, c);
+                    GrAssert(kInvalidUniformHandle !=
+                             fCurrentProgram->fUniformHandles.fCoverageUni);
+                    fCurrentProgram->fUniformManager.set4fv(
+                                                    fCurrentProgram->fUniformHandles.fCoverageUni,
+                                                    0, 1, c);
                     fCurrentProgram->fCoverage = coverage;
                 }
                 break;
@@ -302,13 +307,7 @@
         this->flushColor(color);
         this->flushCoverage(coverage);
 
-        fCurrentProgram->setData(drawState);
-
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            if (this->isStageEnabled(s)) {
-                this->flushBoundTextureAndParams(s);
-            }
-        }
+        fCurrentProgram->setData(this);
     }
     this->flushStencil(type);
     this->flushViewMatrix(type);
@@ -318,8 +317,7 @@
     GrIRect* devRect = NULL;
     GrIRect devClipBounds;
     if (drawState.isClipState()) {
-        fClip->getConservativeBounds(drawState.getRenderTarget(),
-                                     &devClipBounds);
+        this->getClip()->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds);
         devRect = &devClipBounds;
     }
     // This must come after textures are flushed because a texture may need
@@ -342,10 +340,7 @@
     #error "unknown GR_TEXT_SCALAR type"
 #endif
 
-void GrGpuGL::setupGeometry(int* startVertex,
-                            int* startIndex,
-                            int vertexCount,
-                            int indexCount) {
+void GrGpuGL::setupGeometry(const DrawInfo& info, int* startIndexOffset) {
 
     int newColorOffset;
     int newCoverageOffset;
@@ -354,45 +349,36 @@
 
     GrVertexLayout currLayout = this->getVertexLayout();
 
-    GrGLsizei newStride = VertexSizeAndOffsetsByIdx(
-                                            currLayout,
-                                            newTexCoordOffsets,
-                                            &newColorOffset,
-                                            &newCoverageOffset,
-                                            &newEdgeOffset);
+    GrGLsizei newStride = GrDrawState::VertexSizeAndOffsetsByIdx(currLayout,
+                                                                 newTexCoordOffsets,
+                                                                 &newColorOffset,
+                                                                 &newCoverageOffset,
+                                                                 &newEdgeOffset);
     int oldColorOffset;
     int oldCoverageOffset;
     int oldTexCoordOffsets[GrDrawState::kMaxTexCoords];
     int oldEdgeOffset;
 
-    GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(
-                                            fHWGeometryState.fVertexLayout,
-                                            oldTexCoordOffsets,
-                                            &oldColorOffset,
-                                            &oldCoverageOffset,
-                                            &oldEdgeOffset);
-    bool indexed = NULL != startIndex;
+    GrGLsizei oldStride = GrDrawState::VertexSizeAndOffsetsByIdx(fHWGeometryState.fVertexLayout,
+                                                                 oldTexCoordOffsets,
+                                                                 &oldColorOffset,
+                                                                 &oldCoverageOffset,
+                                                                 &oldEdgeOffset);
 
     int extraVertexOffset;
-    int extraIndexOffset;
-    this->setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);
+    this->setBuffers(info.isIndexed(), &extraVertexOffset, startIndexOffset);
 
     GrGLenum scalarType;
     bool texCoordNorm;
-    if (currLayout & kTextFormat_VertexLayoutBit) {
+    if (currLayout & GrDrawState::kTextFormat_VertexLayoutBit) {
         scalarType = TEXT_COORDS_GL_TYPE;
         texCoordNorm = SkToBool(TEXT_COORDS_ARE_NORMALIZED);
     } else {
-//        GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
         scalarType = GR_GL_FLOAT;
         texCoordNorm = false;
     }
 
-    size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride;
-    *startVertex = 0;
-    if (indexed) {
-        *startIndex += extraIndexOffset;
-    }
+    size_t vertexOffset = (info.startVertex() + extraVertexOffset) * newStride;
 
     // all the Pointers must be set if any of these are true
     bool allOffsetsChange =  fHWGeometryState.fArrayPtrsDirty ||
@@ -403,7 +389,7 @@
     // or the type/normalization changed based on text vs nontext type coords.
     bool posAndTexChange = allOffsetsChange ||
                            (((TEXT_COORDS_GL_TYPE != GR_GL_FLOAT) || TEXT_COORDS_ARE_NORMALIZED) &&
-                                (kTextFormat_VertexLayoutBit &
+                                (GrDrawState::kTextFormat_VertexLayoutBit &
                                   (fHWGeometryState.fVertexLayout ^ currLayout)));
 
     if (posAndTexChange) {
@@ -505,14 +491,14 @@
     desc->fEmitsPointSize = isPoints;
 
     bool requiresAttributeColors = !skipColor &&
-                                   SkToBool(desc->fVertexLayout & kColor_VertexLayoutBit);
+                                   SkToBool(desc->fVertexLayout & GrDrawState::kColor_VertexLayoutBit);
     bool requiresAttributeCoverage = !skipCoverage &&
-                                     SkToBool(desc->fVertexLayout & kCoverage_VertexLayoutBit);
+                                     SkToBool(desc->fVertexLayout & GrDrawState::kCoverage_VertexLayoutBit);
 
     // fColorInput/fCoverageInput records how colors are specified for the.
     // program. So we strip the bits from the layout to avoid false negatives
     // when searching for an existing program in the cache.
-    desc->fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit);
+    desc->fVertexLayout &= ~(GrDrawState::kColor_VertexLayoutBit | GrDrawState::kCoverage_VertexLayoutBit);
 
     desc->fColorFilterXfermode = skipColor ?
                                 SkXfermode::kDst_Mode :
@@ -521,7 +507,7 @@
     // no reason to do edge aa or look at per-vertex coverage if coverage is
     // ignored
     if (skipCoverage) {
-        desc->fVertexLayout &= ~(kEdge_VertexLayoutBit | kCoverage_VertexLayoutBit);
+        desc->fVertexLayout &= ~(GrDrawState::kEdge_VertexLayoutBit | GrDrawState::kCoverage_VertexLayoutBit);
     }
 
     bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
@@ -551,7 +537,7 @@
 
     int lastEnabledStage = -1;
 
-    if (!skipCoverage && (desc->fVertexLayout &GrDrawTarget::kEdge_VertexLayoutBit)) {
+    if (!skipCoverage && (desc->fVertexLayout &GrDrawState::kEdge_VertexLayoutBit)) {
         desc->fVertexEdgeType = drawState.getVertexEdgeType();
         desc->fDiscardIfOutsideEdge = drawState.getStencil().doesWrite();
     } else {
@@ -565,7 +551,7 @@
         bool skip = s < drawState.getFirstCoverageStage() ? skipColor : skipCoverage;
         if (!skip && drawState.isStageEnabled(s)) {
             lastEnabledStage = s;
-            const GrEffect* effect = drawState.getStage(s).getEffect();
+            const GrEffectRef& effect = *drawState.getStage(s).getEffect();
             const GrBackendEffectFactory& factory = effect->getFactory();
             desc->fEffectKeys[s] = factory.glEffectKey(drawState.getStage(s), this->glCaps());
         } else {
@@ -594,7 +580,7 @@
     // other coverage inputs
     if (!hasCoverage) {
         hasCoverage = requiresAttributeCoverage ||
-                      (desc->fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit);
+                      (desc->fVertexLayout & GrDrawState::kEdge_VertexLayoutBit);
     }
 
     if (hasCoverage) {
diff --git a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
index 19fe44f..bc1e0ee 100644
--- a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
+++ b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
@@ -153,4 +153,3 @@
     glInterface.get()->ref();
     return glInterface.get();
 }
-
diff --git a/src/gpu/gl/debug/GrTextureUnitObj.cpp b/src/gpu/gl/debug/GrTextureUnitObj.cpp
index b7c7b0c..316dcec 100644
--- a/src/gpu/gl/debug/GrTextureUnitObj.cpp
+++ b/src/gpu/gl/debug/GrTextureUnitObj.cpp
@@ -29,4 +29,3 @@
         fTexture->setBound(this);
     }
 }
-
diff --git a/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp b/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp
index 2f712e3..1b4b7f6 100644
--- a/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp
+++ b/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp
@@ -11,7 +11,7 @@
 #include "../GrGLUtil.h"
 
 #define GL_GLEXT_PROTOTYPES
-#include <GL/osmesa.h>
+#include "osmesa_wrapper.h"
 
 #define GR_GL_GET_PROC(F) interface->f ## F = (GrGL ## F ## Proc) \
         OSMesaGetProcAddress("gl" #F);
diff --git a/src/gpu/gl/mesa/SkMesaGLContext.cpp b/src/gpu/gl/mesa/SkMesaGLContext.cpp
index cac021c..6c6e422 100644
--- a/src/gpu/gl/mesa/SkMesaGLContext.cpp
+++ b/src/gpu/gl/mesa/SkMesaGLContext.cpp
@@ -30,7 +30,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkMesaGLContext::SkMesaGLContext()
-    : fContext(NULL)
+    : fContext(static_cast<Context>(NULL))
     , fImage(NULL) {
     GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext));
 }
diff --git a/src/gpu/gl/mesa/osmesa_wrapper.h b/src/gpu/gl/mesa/osmesa_wrapper.h
new file mode 100644
index 0000000..70de993
--- /dev/null
+++ b/src/gpu/gl/mesa/osmesa_wrapper.h
@@ -0,0 +1,16 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Older versions of XQuartz have a bug where a header included by osmesa.h
+// defines GL_GLEXT_PROTOTYPES. This will cause a redefinition warning if
+// the file that includes osmesa.h already defined it. XCode 3 uses a version
+// of gcc (4.2.1) that does not support the diagnostic pragma to disable a
+// warning (added in 4.2.4). So we use the system_header pragma to shut GCC
+// up about warnings in osmesa.h
+#pragma GCC system_header
+#include <GL/osmesa.h>
diff --git a/src/gpu/gr_unittests.cpp b/src/gpu/gr_unittests.cpp
index 1d7653b..7f5c7e9 100644
--- a/src/gpu/gr_unittests.cpp
+++ b/src/gpu/gr_unittests.cpp
@@ -6,13 +6,10 @@
  * found in the LICENSE file.
  */
 
-
-
 #include "GrBinHashKey.h"
 #include "GrDrawTarget.h"
 #include "SkMatrix.h"
 #include "GrRedBlackTree.h"
-#include "GrTDArray.h"
 
 // FIXME: needs to be in a header
 void gr_run_unittests();
@@ -28,37 +25,6 @@
 }
 #include "GrTBSearch.h"
 
-static void dump(const GrTDArray<int>& array) {
-#if 0
-    for (int i = 0; i < array.count(); i++) {
-        printf(" %d", array[i]);
-    }
-    printf("\n");
-#endif
-}
-
-static void test_tdarray() {
-    GrTDArray<int> array;
-
-    *array.append() = 0; dump(array);
-    *array.append() = 2; dump(array);
-    *array.append() = 4; dump(array);
-    *array.append() = 6; dump(array);
-    GrAssert(array.count() == 4);
-
-    *array.insert(0) = -1; dump(array);
-    *array.insert(2) = 1; dump(array);
-    *array.insert(4) = 3; dump(array);
-    *array.insert(7) = 7; dump(array);
-    GrAssert(array.count() == 8);
-    array.remove(3); dump(array);
-    array.remove(0); dump(array);
-    array.removeShuffle(4); dump(array);
-    array.removeShuffle(1); dump(array);
-    GrAssert(array.count() == 4);
-}
-
-
 static void test_bsearch() {
     const int array[] = {
         1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99
@@ -106,9 +72,8 @@
 
 
 void gr_run_unittests() {
-    test_tdarray();
     test_bsearch();
     test_binHashKey();
     GrRedBlackTree<int>::UnitTest();
-    GrDrawTarget::VertexLayoutUnitTest();
+    GrDrawState::VertexLayoutUnitTest();
 }
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index b6fc489..b388d58 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -31,4 +31,3 @@
                    const SkPaint* paint) {
     asIB(this)->onDraw(canvas, x, y, paint);
 }
-
diff --git a/src/image/SkImagePriv.cpp b/src/image/SkImagePriv.cpp
index e10a44c..c44e439 100644
--- a/src/image/SkImagePriv.cpp
+++ b/src/image/SkImagePriv.cpp
@@ -141,4 +141,3 @@
     canvas->drawPicture(*picture);
     canvas->restoreToCount(saveCount);
 }
-
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 648b259..2687025 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -21,4 +21,3 @@
 };
 
 #endif
-
diff --git a/src/image/SkImage_Codec.cpp b/src/image/SkImage_Codec.cpp
index b46ac2d..9d81dc9 100644
--- a/src/image/SkImage_Codec.cpp
+++ b/src/image/SkImage_Codec.cpp
@@ -64,4 +64,3 @@
 
     return SkNEW_ARGS(SkImage_Codec, (data, bitmap.width(), bitmap.height()));
 }
-
diff --git a/src/image/SkImage_Picture.cpp b/src/image/SkImage_Picture.cpp
index 959e47b..447299f 100644
--- a/src/image/SkImage_Picture.cpp
+++ b/src/image/SkImage_Picture.cpp
@@ -52,4 +52,3 @@
 
     return SkNEW_ARGS(SkImage_Picture, (playback));
 }
-
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 8638052..d4ed171 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -159,4 +159,3 @@
 SkPixelRef* SkBitmapImageGetPixelRef(SkImage* image) {
     return ((SkImage_Raster*)image)->getPixelRef();
 }
-
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 6547780..37995b1 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -131,4 +131,3 @@
                      const SkPaint* paint) {
     return asSB(this)->onDraw(canvas, x, y, paint);
 }
-
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index e5ca498..b9c4890 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -73,4 +73,3 @@
 };
 
 #endif
-
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 74fbe0b..c497577 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -144,4 +144,3 @@
 
     return SkNEW_ARGS(SkSurface_Gpu, (ctx, tex->asRenderTarget()));
 }
-
diff --git a/src/image/SkSurface_Picture.cpp b/src/image/SkSurface_Picture.cpp
index 5499844..ba3ee63 100644
--- a/src/image/SkSurface_Picture.cpp
+++ b/src/image/SkSurface_Picture.cpp
@@ -91,4 +91,3 @@
 
     return SkNEW_ARGS(SkSurface_Picture, (width, height));
 }
-
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
index 455ef1b..6e972dd 100644
--- a/src/image/SkSurface_Raster.cpp
+++ b/src/image/SkSurface_Raster.cpp
@@ -171,4 +171,3 @@
     SkAutoTUnref<SkPixelRef> pr(SkNEW_ARGS(SkMallocPixelRef, (pixels, size, NULL, true)));
     return SkNEW_ARGS(SkSurface_Raster, (info, pr, rowBytes));
 }
-
diff --git a/src/images/SkFDStream.cpp b/src/images/SkFDStream.cpp
index 5bf0850..d38813c 100644
--- a/src/images/SkFDStream.cpp
+++ b/src/images/SkFDStream.cpp
@@ -89,4 +89,3 @@
     }
     return 0;
 }
-
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 2980896..a0d7ba9 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -191,4 +191,3 @@
     }
     return success;
 }
-
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index 3008130..f81c601 100644
--- a/src/images/SkImageDecoder_libgif.cpp
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -99,7 +99,11 @@
 
 void CheckFreeExtension(SavedImage* Image) {
     if (Image->ExtensionBlocks) {
+#if GIFLIB_MAJOR < 5
         FreeExtension(Image);
+#else
+        GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
+#endif
     }
 }
 
@@ -151,7 +155,11 @@
 }
 
 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
+#if GIFLIB_MAJOR < 5
     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
+#else
+    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
+#endif
     if (NULL == gif) {
         return error_return(gif, *bm, "DGifOpen");
     }
@@ -166,6 +174,9 @@
     int width, height;
     GifRecordType recType;
     GifByteType *extData;
+#if GIFLIB_MAJOR >= 5
+    int extFunction;
+#endif
     int transpIndex = -1;   // -1 means we don't have it (yet)
 
     do {
@@ -296,21 +307,35 @@
             } break;
 
         case EXTENSION_RECORD_TYPE:
+#if GIFLIB_MAJOR < 5
             if (DGifGetExtension(gif, &temp_save.Function,
                                  &extData) == GIF_ERROR) {
+#else
+            if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
+#endif
                 return error_return(gif, *bm, "DGifGetExtension");
             }
 
             while (extData != NULL) {
                 /* Create an extension block with our data */
+#if GIFLIB_MAJOR < 5
                 if (AddExtensionBlock(&temp_save, extData[0],
                                       &extData[1]) == GIF_ERROR) {
+#else
+                if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
+                                         &gif->ExtensionBlocks,
+                                         extFunction,
+                                         extData[0],
+                                         &extData[1]) == GIF_ERROR) {
+#endif
                     return error_return(gif, *bm, "AddExtensionBlock");
                 }
                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
                     return error_return(gif, *bm, "DGifGetExtensionNext");
                 }
+#if GIFLIB_MAJOR < 5
                 temp_save.Function = 0;
+#endif
             }
             break;
 
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
index 5167fe1..edfaa2e 100644
--- a/src/images/SkImageDecoder_libico.cpp
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -390,4 +390,3 @@
 }
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libico_dfactory);
-
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index 6bae965..867c41c 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -679,4 +679,3 @@
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);
-
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
index 175a444..c475ec1 100644
--- a/src/images/SkImageDecoder_wbmp.cpp
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -161,4 +161,3 @@
 }
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_wbmp_dfactory);
-
diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp
index e05a28c..4b52fcd 100644
--- a/src/images/SkImageEncoder.cpp
+++ b/src/images/SkImageEncoder.cpp
@@ -38,4 +38,3 @@
     SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
     return enc.get() && enc.get()->encodeStream(stream, bm, quality);
 }
-
diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
index 39a1de9..299166c 100644
--- a/src/images/SkImageRef.cpp
+++ b/src/images/SkImageRef.cpp
@@ -15,14 +15,12 @@
 
 //#define DUMP_IMAGEREF_LIFECYCLE
 
-// can't be static, as SkImageRef_Pool needs to see it
-SK_DECLARE_GLOBAL_MUTEX(gImageRefMutex);
 
 ///////////////////////////////////////////////////////////////////////////////
 
 SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config,
-                       int sampleSize)
-        : SkPixelRef(&gImageRefMutex), fErrorInDecoding(false) {
+                       int sampleSize, SkBaseMutex* mutex)
+        : SkPixelRef(mutex), fErrorInDecoding(false) {
     SkASSERT(stream);
     stream->ref();
     fStream = stream;
@@ -39,7 +37,6 @@
 }
 
 SkImageRef::~SkImageRef() {
-    SkASSERT(&gImageRefMutex == this->mutex());
 
 #ifdef DUMP_IMAGEREF_LIFECYCLE
     SkDebugf("delete ImageRef %p [%d] data=%d\n",
@@ -51,7 +48,7 @@
 }
 
 bool SkImageRef::getInfo(SkBitmap* bitmap) {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(this->mutex());
 
     if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) {
         return false;
@@ -89,7 +86,6 @@
 }
 
 bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
-    SkASSERT(&gImageRefMutex == this->mutex());
 
     if (fErrorInDecoding) {
         return false;
@@ -144,8 +140,6 @@
 }
 
 void* SkImageRef::onLockPixels(SkColorTable** ct) {
-    SkASSERT(&gImageRefMutex == this->mutex());
-
     if (NULL == fBitmap.getPixels()) {
         (void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode);
     }
@@ -156,11 +150,6 @@
     return fBitmap.getPixels();
 }
 
-void SkImageRef::onUnlockPixels() {
-    // we're already have the mutex locked
-    SkASSERT(&gImageRefMutex == this->mutex());
-}
-
 size_t SkImageRef::ramUsed() const {
     size_t size = 0;
 
@@ -175,8 +164,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer, &gImageRefMutex), fErrorInDecoding(false) {
+SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex)
+        : INHERITED(buffer, mutex), fErrorInDecoding(false) {
     fConfig = (SkBitmap::Config)buffer.readUInt();
     fSampleSize = buffer.readInt();
     fDoDither = buffer.readBool();
@@ -198,4 +187,3 @@
     fStream->rewind();
     buffer.writeStream(fStream, fStream->getLength());
 }
-
diff --git a/src/images/SkImageRefPool.cpp b/src/images/SkImageRefPool.cpp
index b8bbfd2..0a3d7bf 100644
--- a/src/images/SkImageRefPool.cpp
+++ b/src/images/SkImageRefPool.cpp
@@ -190,4 +190,3 @@
     }
 #endif
 }
-
diff --git a/src/images/SkImageRefPool.h b/src/images/SkImageRefPool.h
index 08633ee..1e74a6d 100644
--- a/src/images/SkImageRefPool.h
+++ b/src/images/SkImageRefPool.h
@@ -47,4 +47,3 @@
 };
 
 #endif
-
diff --git a/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
index e62816a..6af8653 100644
--- a/src/images/SkImageRef_GlobalPool.cpp
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -9,7 +9,7 @@
 #include "SkImageRefPool.h"
 #include "SkThread.h"
 
-extern SkBaseMutex gImageRefMutex;
+SK_DECLARE_STATIC_MUTEX(gGlobalPoolMutex);
 
 /*
  *  This returns the lazily-allocated global pool. It must be called
@@ -27,16 +27,16 @@
 SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkStream* stream,
                                              SkBitmap::Config config,
                                              int sampleSize)
-        : SkImageRef(stream, config, sampleSize) {
-    this->mutex()->acquire();
+        : SkImageRef(stream, config, sampleSize, &gGlobalPoolMutex) {
+    SkASSERT(&gGlobalPoolMutex == this->mutex());
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->addToHead(this);
-    this->mutex()->release();
 }
 
 SkImageRef_GlobalPool::~SkImageRef_GlobalPool() {
-    this->mutex()->acquire();
+    SkASSERT(&gGlobalPoolMutex == this->mutex());
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->detach(this);
-    this->mutex()->release();
 }
 
 /*  By design, onUnlockPixels() already is inside the mutex-lock,
@@ -65,36 +65,36 @@
 }
 
 SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer) {
-    this->mutex()->acquire();
+        : INHERITED(buffer, &gGlobalPoolMutex) {
+    SkASSERT(&gGlobalPoolMutex == this->mutex());
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->addToHead(this);
-    this->mutex()->release();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // global imagerefpool wrappers
 
 size_t SkImageRef_GlobalPool::GetRAMBudget() {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     return GetGlobalPool()->getRAMBudget();
 }
 
 void SkImageRef_GlobalPool::SetRAMBudget(size_t size) {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->setRAMBudget(size);
 }
 
 size_t SkImageRef_GlobalPool::GetRAMUsed() {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     return GetGlobalPool()->getRAMUsed();
 }
 
 void SkImageRef_GlobalPool::SetRAMUsed(size_t usage) {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->setRAMUsed(usage);
 }
 
 void SkImageRef_GlobalPool::DumpPool() {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->dump();
 }
diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/images/SkImageRef_ashmem.cpp
similarity index 97%
rename from src/ports/SkImageRef_ashmem.cpp
rename to src/images/SkImageRef_ashmem.cpp
index f8a9bb9..dc60465 100644
--- a/src/ports/SkImageRef_ashmem.cpp
+++ b/src/images/SkImageRef_ashmem.cpp
@@ -42,8 +42,6 @@
     fRec.fPinned = false;
 
     fCT = NULL;
-
-    this->useDefaultMutex();   // we don't need/want the shared imageref mutex
 }
 
 SkImageRef_ashmem::~SkImageRef_ashmem() {
@@ -230,5 +228,4 @@
         setURI(uri);
         sk_free(uri);
     }
-    this->useDefaultMutex();   // we don't need/want the shared imageref mutex
 }
diff --git a/src/ports/SkImageRef_ashmem.h b/src/images/SkImageRef_ashmem.h
similarity index 100%
rename from src/ports/SkImageRef_ashmem.h
rename to src/images/SkImageRef_ashmem.h
diff --git a/src/images/SkImages.cpp b/src/images/SkImages.cpp
index 0bcc33f..5b6bf6b 100644
--- a/src/images/SkImages.cpp
+++ b/src/images/SkImages.cpp
@@ -9,6 +9,13 @@
 #include "SkImageRef_GlobalPool.h"
 #include "SkImages.h"
 
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkImageRef_ashmem.h"
+#endif
+
 void SkImages::InitializeFlattenables() {
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageRef_GlobalPool)
+#ifdef SK_BUILD_FOR_ANDROID
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageRef_ashmem)
+#endif
 }
diff --git a/src/images/SkMovie.cpp b/src/images/SkMovie.cpp
index e671950..ea4a76c 100644
--- a/src/images/SkMovie.cpp
+++ b/src/images/SkMovie.cpp
@@ -107,4 +107,3 @@
 
     return movie;
 }
-
diff --git a/src/images/SkMovie_gif.cpp b/src/images/SkMovie_gif.cpp
index 9cebc0f..cda1525 100644
--- a/src/images/SkMovie_gif.cpp
+++ b/src/images/SkMovie_gif.cpp
@@ -40,7 +40,11 @@
 
 SkGIFMovie::SkGIFMovie(SkStream* stream)
 {
+#if GIFLIB_MAJOR < 5
     fGIF = DGifOpen( stream, Decode );
+#else
+    fGIF = DGifOpen( stream, Decode, NULL );
+#endif
     if (NULL == fGIF)
         return;
 
diff --git a/src/images/SkPageFlipper.cpp b/src/images/SkPageFlipper.cpp
index 4fd8632..a53f47b 100644
--- a/src/images/SkPageFlipper.cpp
+++ b/src/images/SkPageFlipper.cpp
@@ -74,5 +74,3 @@
     fDirty1->setEmpty();
     return *fDirty0;
 }
-
-
diff --git a/src/opts/SkBitmapProcState_opts_SSE2.cpp b/src/opts/SkBitmapProcState_opts_SSE2.cpp
index 1e2dc93..c2de259 100644
--- a/src/opts/SkBitmapProcState_opts_SSE2.cpp
+++ b/src/opts/SkBitmapProcState_opts_SSE2.cpp
@@ -678,8 +678,6 @@
     // ( 0,  0,  0,  0,  0,  0,  0,  0)
     __m128i zero = _mm_setzero_si128();
 
-    __m128i _m_shift_5 = _mm_set1_epi32((1<<5)-1);
-    __m128i _m_shift_6 = _mm_set1_epi32((1<<6)-1);
     do {
         uint32_t XX = *xy++;    // x0:14 | 4 | x1:14
         unsigned x0 = XX >> 18;
diff --git a/src/opts/SkBitmapProcState_opts_arm.cpp b/src/opts/SkBitmapProcState_opts_arm.cpp
index 8b64773..e044ad8 100644
--- a/src/opts/SkBitmapProcState_opts_arm.cpp
+++ b/src/opts/SkBitmapProcState_opts_arm.cpp
@@ -218,4 +218,3 @@
             break;
     }
 }
-
diff --git a/src/opts/SkBitmapProcState_opts_none.cpp b/src/opts/SkBitmapProcState_opts_none.cpp
index 82be4ea..3a186b5 100644
--- a/src/opts/SkBitmapProcState_opts_none.cpp
+++ b/src/opts/SkBitmapProcState_opts_none.cpp
@@ -21,5 +21,3 @@
 
 // empty implementation just uses default supplied function pointers
 void SkBitmapProcState::platformProcs() {}
-
-
diff --git a/src/opts/SkBlitRect_opts_SSE2.cpp b/src/opts/SkBlitRect_opts_SSE2.cpp
index 6d8a296..cb61813 100644
--- a/src/opts/SkBlitRect_opts_SSE2.cpp
+++ b/src/opts/SkBlitRect_opts_SSE2.cpp
@@ -130,4 +130,3 @@
         SkBlitRow::ColorRect32(destination, width, height, rowBytes, color);
     }
 }
-
diff --git a/src/opts/SkBlitRect_opts_SSE2.h b/src/opts/SkBlitRect_opts_SSE2.h
index d3ec0e3..4d2f74a 100644
--- a/src/opts/SkBlitRect_opts_SSE2.h
+++ b/src/opts/SkBlitRect_opts_SSE2.h
@@ -21,4 +21,3 @@
 
 
 #endif
-
diff --git a/src/opts/SkBlitRow_opts_none.cpp b/src/opts/SkBlitRow_opts_none.cpp
index 5f4598e..88e1af3 100644
--- a/src/opts/SkBlitRow_opts_none.cpp
+++ b/src/opts/SkBlitRow_opts_none.cpp
@@ -43,4 +43,3 @@
                                                  RowFlags flags) {
     return NULL;
 }
-
diff --git a/src/opts/SkUtils_opts_none.cpp b/src/opts/SkUtils_opts_none.cpp
index bb2558c..d905425 100644
--- a/src/opts/SkUtils_opts_none.cpp
+++ b/src/opts/SkUtils_opts_none.cpp
@@ -21,5 +21,3 @@
 SkBlitRow::ColorRectProc PlatformColorRectProcFactory() {
     return NULL;
 }
-
-
diff --git a/src/opts/opts_check_SSE2.cpp b/src/opts/opts_check_SSE2.cpp
index 96d0dea..6370058 100644
--- a/src/opts/opts_check_SSE2.cpp
+++ b/src/opts/opts_check_SSE2.cpp
@@ -244,5 +244,3 @@
         return NULL;
     }
 }
-
-
diff --git a/src/opts/opts_check_arm.cpp b/src/opts/opts_check_arm.cpp
index 69cedb2..ba407d7 100644
--- a/src/opts/opts_check_arm.cpp
+++ b/src/opts/opts_check_arm.cpp
@@ -65,4 +65,3 @@
 SkBlitRow::ColorRectProc PlatformColorRectProcFactory() {
     return NULL;
 }
-
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 0de011c..e253a5f 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -14,6 +14,7 @@
 #include "SkClipStack.h"
 #include "SkData.h"
 #include "SkDraw.h"
+#include "SkFontHost.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
 #include "SkPath.h"
@@ -28,6 +29,7 @@
 #include "SkRect.h"
 #include "SkString.h"
 #include "SkTextFormatParams.h"
+#include "SkTemplates.h"
 #include "SkTypeface.h"
 #include "SkTypes.h"
 
@@ -75,7 +77,7 @@
 
     SkMatrix ident;
     ident.reset();
-    SkAutoGlyphCache autoCache(paint, &ident);
+    SkAutoGlyphCache autoCache(paint, NULL, &ident);
     SkGlyphCache* cache = autoCache.getCache();
 
     const char* start = reinterpret_cast<const char*>(glyphs);
@@ -102,6 +104,69 @@
     *y = *y - yAdj;
 }
 
+static size_t max_glyphid_for_typeface(const SkTypeface* typeface) {
+    SkAdvancedTypefaceMetrics* metrics;
+    metrics = SkFontHost::GetAdvancedTypefaceMetrics(
+            SkTypeface::UniqueID(typeface),
+            SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo,
+            NULL, 0);
+
+    int lastGlyphID = 0;
+    if (metrics) {
+        lastGlyphID = metrics->fLastGlyphID;
+        metrics->unref();
+    }
+    return lastGlyphID;
+}
+
+typedef SkAutoSTMalloc<128, uint16_t> SkGlyphStorage;
+
+static size_t force_glyph_encoding(const SkPaint& paint, const void* text,
+                                   size_t len, SkGlyphStorage* storage,
+                                   uint16_t** glyphIDs) {
+    // Make sure we have a glyph id encoding.
+    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+        size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
+        storage->reset(numGlyphs);
+        paint.textToGlyphs(text, len, storage->get());
+        *glyphIDs = storage->get();
+        return numGlyphs;
+    }
+
+    // For user supplied glyph ids we need to validate them.
+    SkASSERT((len & 1) == 0);
+    size_t numGlyphs = len / 2;
+    const uint16_t* input =
+        reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
+
+    int maxGlyphID = max_glyphid_for_typeface(paint.getTypeface());
+    size_t validated;
+    for (validated = 0; validated < numGlyphs; ++validated) {
+        if (input[validated] > maxGlyphID) {
+            break;
+        }
+    }
+    if (validated >= numGlyphs) {
+        *glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
+        return numGlyphs;
+    }
+
+    // Silently drop anything out of range.
+    storage->reset(numGlyphs);
+    if (validated > 0) {
+        memcpy(storage->get(), input, validated * sizeof(uint16_t));
+    }
+
+    for (size_t i = validated; i < numGlyphs; ++i) {
+        storage->get()[i] = input[i];
+        if (input[i] > maxGlyphID) {
+            storage->get()[i] = 0;
+        }
+    }
+    *glyphIDs = storage->get();
+    return numGlyphs;
+}
+
 static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
                                SkWStream* content) {
     // Flip the text about the x-axis to account for origin swap and include
@@ -816,20 +881,11 @@
         return;
     }
 
-    // We want the text in glyph id encoding and a writable buffer, so we end
-    // up making a copy either way.
-    size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
-    uint16_t* glyphIDs = reinterpret_cast<uint16_t*>(
-            sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
-    SkAutoFree autoFreeGlyphIDs(glyphIDs);
-    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
-        paint.textToGlyphs(text, len, glyphIDs);
-        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    } else {
-        SkASSERT((len & 1) == 0);
-        SkASSERT(len / 2 == numGlyphs);
-        memcpy(glyphIDs, text, len);
-    }
+    SkGlyphStorage storage(0);
+    uint16_t* glyphIDs = NULL;
+    size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
+                                            &glyphIDs);
+    textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
     align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y);
@@ -865,22 +921,11 @@
         return;
     }
 
-    // Make sure we have a glyph id encoding.
-    SkAutoFree glyphStorage;
-    uint16_t* glyphIDs;
-    size_t numGlyphs;
-    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
-        numGlyphs = paint.textToGlyphs(text, len, NULL);
-        glyphIDs = reinterpret_cast<uint16_t*>(sk_malloc_flags(
-                numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
-        glyphStorage.set(glyphIDs);
-        paint.textToGlyphs(text, len, glyphIDs);
-        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    } else {
-        SkASSERT((len & 1) == 0);
-        numGlyphs = len / 2;
-        glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
-    }
+    SkGlyphStorage storage(0);
+    uint16_t* glyphIDs = NULL;
+    size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
+                                            &glyphIDs);
+    textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
     content.entry()->fContent.writeText("BT\n");
@@ -1006,8 +1051,7 @@
         fResourceDict = SkNEW(SkPDFDict);
 
         if (fGraphicStateResources.count()) {
-            SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
-            extGState->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> extGState(new SkPDFDict());
             for (int i = 0; i < fGraphicStateResources.count(); i++) {
                 SkString nameString("G");
                 nameString.appendS32(i);
@@ -1019,8 +1063,7 @@
         }
 
         if (fXObjectResources.count()) {
-            SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
-            xObjects->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> xObjects(new SkPDFDict());
             for (int i = 0; i < fXObjectResources.count(); i++) {
                 SkString nameString("X");
                 nameString.appendS32(i);
@@ -1032,8 +1075,7 @@
         }
 
         if (fFontResources.count()) {
-            SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
-            fonts->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> fonts(new SkPDFDict());
             for (int i = 0; i < fFontResources.count(); i++) {
                 SkString nameString("F");
                 nameString.appendS32(i);
@@ -1044,8 +1086,7 @@
         }
 
         if (fShaderResources.count()) {
-            SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
-            patterns->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> patterns(new SkPDFDict());
             for (int i = 0; i < fShaderResources.count(); i++) {
                 SkString nameString("P");
                 nameString.appendS32(i);
@@ -1058,8 +1099,7 @@
         // For compatibility, add all proc sets (only used for output to PS
         // devices).
         const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
-        SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
-        procSets->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFArray> procSets(new SkPDFArray());
         procSets->reserve(SK_ARRAY_COUNT(procs));
         for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
             procSets->appendName(procs[i]);
@@ -1266,9 +1306,8 @@
     SkPaint stockPaint;
     this->drawPaint(draw, stockPaint);
     SkAutoTUnref<SkPDFFormXObject> maskFormXObject(createFormXObjectFromDevice());
-    SkRefPtr<SkPDFGraphicState> sMaskGS =
-        SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject, invertClip);
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    SkAutoTUnref<SkPDFGraphicState> sMaskGS(
+        SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject, invertClip));
 
     // Draw the xobject with the clip as a mask.
     ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
@@ -1283,8 +1322,7 @@
     fXObjectResources.push(xobject);
     xobject->ref();
 
-    sMaskGS = SkPDFGraphicState::GetNoSMaskGraphicState();
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &content.entry()->fContent);
 }
@@ -1421,27 +1459,25 @@
         return;
     }
 
-    SkRefPtr<SkPDFGraphicState> sMaskGS;
+    SkAutoTUnref<SkPDFGraphicState> sMaskGS;
     if (xfermode == SkXfermode::kSrcIn_Mode ||
             xfermode == SkXfermode::kSrcOut_Mode) {
-        sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
-                dst, xfermode == SkXfermode::kSrcOut_Mode);
+        sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
+                dst, xfermode == SkXfermode::kSrcOut_Mode));
         fXObjectResources.push(srcFormXObject.get());
         srcFormXObject.get()->ref();
     } else {
-        sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
-                srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode);
+        sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
+                srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode));
         // dst already added to fXObjectResources in drawFormXObjectWithClip.
     }
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &inClipContentEntry.entry()->fContent);
 
     SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
                                 &inClipContentEntry.entry()->fContent);
 
-    sMaskGS = SkPDFGraphicState::GetNoSMaskGraphicState();
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &inClipContentEntry.entry()->fContent);
 }
@@ -1474,7 +1510,7 @@
     entry->fShaderIndex = -1;
 
     // PDF treats a shader as a color, so we only set one or the other.
-    SkRefPtr<SkPDFObject> pdfShader;
+    SkAutoTUnref<SkPDFObject> pdfShader;
     const SkShader* shader = paint.getShader();
     SkColor color = paint.getColor();
     if (shader) {
@@ -1494,8 +1530,7 @@
         fInitialTransform.mapRect(&boundsTemp);
         boundsTemp.roundOut(&bounds);
 
-        pdfShader = SkPDFShader::GetPDFShader(*shader, transform, bounds);
-        SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref
+        pdfShader.reset(SkPDFShader::GetPDFShader(*shader, transform, bounds));
 
         if (pdfShader.get()) {
             // pdfShader has been canonicalized so we can directly compare
@@ -1504,7 +1539,7 @@
             if (resourceIndex < 0) {
                 resourceIndex = fShaderResources.count();
                 fShaderResources.push(pdfShader.get());
-                pdfShader->ref();
+                pdfShader.get()->ref();
             }
             entry->fShaderIndex = resourceIndex;
         } else {
@@ -1523,15 +1558,16 @@
         }
     }
 
-    SkRefPtr<SkPDFGraphicState> newGraphicState;
+    SkAutoTUnref<SkPDFGraphicState> newGraphicState;
     if (color == paint.getColor()) {
-        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(paint);
+        newGraphicState.reset(
+                SkPDFGraphicState::GetGraphicStateForPaint(paint));
     } else {
         SkPaint newPaint = paint;
         newPaint.setColor(color);
-        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(newPaint);
+        newGraphicState.reset(
+                SkPDFGraphicState::GetGraphicStateForPaint(newPaint));
     }
-    newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref.
     int resourceIndex = addGraphicStateResource(newGraphicState.get());
     entry->fGraphicStateIndex = resourceIndex;
 
@@ -1572,13 +1608,12 @@
 }
 
 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
-    SkRefPtr<SkPDFFont> newFont = SkPDFFont::GetFontResource(typeface, glyphID);
-    newFont->unref();  // getFontResource and SkRefPtr both took a ref.
+    SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface, glyphID));
     int resourceIndex = fFontResources.find(newFont.get());
     if (resourceIndex < 0) {
         resourceIndex = fFontResources.count();
         fFontResources.push(newFont.get());
-        newFont->ref();
+        newFont.get()->ref();
     }
     return resourceIndex;
 }
@@ -1625,4 +1660,3 @@
 bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
     return false;
 }
-
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index 7e064ee..c7266d8 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -98,13 +98,11 @@
         fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
 
         /* TODO(vandebo): output intent
-        SkRefPtr<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
-        outputIntent->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
         outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref();
         outputIntent->insert("OutputConditionIdentifier",
                              new SkPDFString("sRGB"))->unref();
-        SkRefPtr<SkPDFArray> intentArray = new SkPDFArray;
-        intentArray->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray;
         intentArray->append(outputIntent.get());
         fDocCatalog->insert("OutputIntent", intentArray.get());
         */
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 9a8f136..89b4fc9 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -155,13 +155,12 @@
     // if the data was NUL terminated so that we can use strstr() to search it.
     // Make as few copies as possible given these constraints.
     SkDynamicMemoryWStream dynamicStream;
-    SkRefPtr<SkMemoryStream> staticStream;
+    SkAutoTUnref<SkMemoryStream> staticStream;
     SkData* data = NULL;
     const uint8_t* src;
     size_t srcLen;
     if ((srcLen = srcStream->getLength()) > 0) {
-        staticStream = new SkMemoryStream(srcLen + 1);
-        staticStream->unref();  // new and SkRefPtr both took a ref.
+        staticStream.reset(new SkMemoryStream(srcLen + 1));
         src = (const uint8_t*)staticStream->getMemoryBase();
         if (srcStream->getMemoryBase() != NULL) {
             memcpy((void *)src, srcStream->getMemoryBase(), srcLen);
@@ -318,8 +317,7 @@
                 break;
             }
             case SkAdvancedTypefaceMetrics::WidthRange::kRange: {
-                SkRefPtr<SkPDFArray> advanceArray = new SkPDFArray();
-                advanceArray->unref();  // SkRefPtr and new both took a ref.
+                SkAutoTUnref<SkPDFArray> advanceArray(new SkPDFArray());
                 for (int j = 0; j < advanceInfo->fAdvance.count(); j++)
                     appendAdvance(advanceInfo->fAdvance[j], emSize,
                                   advanceArray.get());
@@ -530,8 +528,7 @@
     append_tounicode_header(&cmap);
     append_cmap_sections(glyphToUnicode, subset, &cmap);
     append_cmap_footer(&cmap);
-    SkRefPtr<SkMemoryStream> cmapStream = new SkMemoryStream();
-    cmapStream->unref();  // SkRefPtr and new took a reference.
+    SkAutoTUnref<SkMemoryStream> cmapStream(new SkMemoryStream());
     cmapStream->setData(cmap.copyToData())->unref();
     return new SkPDFStream(cmapStream.get());
 }
@@ -547,9 +544,8 @@
                                   const SkTypeface* typeface,
                                   const SkTDArray<uint32_t>& subset,
                                   SkPDFStream** fontStream) {
-    SkRefPtr<SkStream> fontData =
-            SkFontHost::OpenStream(SkTypeface::UniqueID(typeface));
-    fontData->unref();  // SkRefPtr and OpenStream both took a ref.
+    SkAutoTUnref<SkStream> fontData(
+            SkFontHost::OpenStream(SkTypeface::UniqueID(typeface)));
 
     int fontSize = fontData->getLength();
 
@@ -766,11 +762,12 @@
         return CanonicalFonts()[relatedFontIndex].fFont;
     }
 
-    SkRefPtr<SkAdvancedTypefaceMetrics> fontMetrics;
+    SkAutoTUnref<SkAdvancedTypefaceMetrics> fontMetrics;
     SkPDFDict* relatedFontDescriptor = NULL;
     if (relatedFontIndex >= 0) {
         SkPDFFont* relatedFont = CanonicalFonts()[relatedFontIndex].fFont;
-        fontMetrics = relatedFont->fontInfo();
+        fontMetrics.reset(relatedFont->fontInfo());
+        SkSafeRef(fontMetrics.get());
         relatedFontDescriptor = relatedFont->getFontDescriptor();
 
         // This only is to catch callers who pass invalid glyph ids.
@@ -794,18 +791,16 @@
         info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
                   info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
 #endif
-        fontMetrics =
-            SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0);
-        SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref.
+        fontMetrics.reset(
+            SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0));
 #if defined (SK_SFNTLY_SUBSETTER)
-        if (fontMetrics &&
+        if (fontMetrics.get() &&
             fontMetrics->fType != SkAdvancedTypefaceMetrics::kTrueType_Font) {
             // Font does not support subsetting, get new info with advance.
             info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
                       info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
-            fontMetrics =
-                SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0);
-            SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref
+            fontMetrics.reset(
+                SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0));
         }
 #endif
     }
@@ -856,6 +851,8 @@
           fFirstGlyphID(1),
           fLastGlyphID(info ? info->fLastGlyphID : 0),
           fFontInfo(info) {
+    SkSafeRef(typeface);
+    SkSafeRef(info);
     if (info == NULL) {
         fFontType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
     } else if (info->fMultiMaster) {
@@ -906,7 +903,8 @@
     if (info == NULL || info == fFontInfo.get()) {
         return;
     }
-    fFontInfo = info;
+    fFontInfo.reset(info);
+    SkSafeRef(info);
 }
 
 uint16_t SkPDFFont::firstGlyphID() const {
@@ -924,6 +922,7 @@
 void SkPDFFont::addResource(SkPDFObject* object) {
     SkASSERT(object != NULL);
     fResources.push(object);
+    object->ref();
 }
 
 SkPDFDict* SkPDFFont::getFontDescriptor() {
@@ -931,7 +930,8 @@
 }
 
 void SkPDFFont::setFontDescriptor(SkPDFDict* descriptor) {
-    fDescriptor = descriptor;
+    fDescriptor.reset(descriptor);
+    SkSafeRef(descriptor);
 }
 
 bool SkPDFFont::addCommonFontDescriptorEntries(int16_t defaultWidth) {
@@ -1002,9 +1002,9 @@
     if (fFontInfo == NULL || fFontInfo->fGlyphToUnicode.begin() == NULL) {
         return;
     }
-    SkRefPtr<SkPDFStream> pdfCmap =
-        generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset);
-    addResource(pdfCmap.get());  // Pass reference from new.
+    SkAutoTUnref<SkPDFStream> pdfCmap(
+        generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset));
+    addResource(pdfCmap.get());
     insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
 }
 
@@ -1039,14 +1039,11 @@
     insertName("BaseFont", fontInfo()->fFontName);
     insertName("Encoding", "Identity-H");
 
-    SkPDFCIDFont* newCIDFont;
-    newCIDFont = new SkPDFCIDFont(fontInfo(), typeface(), subset);
-
-    // Pass ref new created to fResources.
-    addResource(newCIDFont);
-    SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
-    descendantFonts->unref();  // SkRefPtr and new took a reference.
-    descendantFonts->append(new SkPDFObjRef(newCIDFont))->unref();
+    SkAutoTUnref<SkPDFCIDFont> newCIDFont(
+            new SkPDFCIDFont(fontInfo(), typeface(), subset));
+    addResource(newCIDFont.get());
+    SkAutoTUnref<SkPDFArray> descendantFonts(new SkPDFArray());
+    descendantFonts->append(new SkPDFObjRef(newCIDFont.get()))->unref();
     insert("DescendantFonts", descendantFonts.get());
 
     populateToUnicodeTable(subset);
@@ -1069,9 +1066,9 @@
 
 bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
                                      const SkTDArray<uint32_t>* subset) {
-    SkRefPtr<SkPDFDict> descriptor = new SkPDFDict("FontDescriptor");
-    descriptor->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFDict> descriptor(new SkPDFDict("FontDescriptor"));
     setFontDescriptor(descriptor.get());
+    addResource(descriptor.get());
 
     switch (getType()) {
         case SkAdvancedTypefaceMetrics::kTrueType_Font: {
@@ -1084,22 +1081,20 @@
                                                   &rawStream);
             SkASSERT(fontSize);
             SkASSERT(rawStream);
-            SkRefPtr<SkPDFStream> fontStream = rawStream;
-            // SkRefPtr and new both ref()'d fontStream, pass one.
+            SkAutoTUnref<SkPDFStream> fontStream(rawStream);
             addResource(fontStream.get());
 
             fontStream->insertInt("Length1", fontSize);
             descriptor->insert("FontFile2",
-                                new SkPDFObjRef(fontStream.get()))->unref();
+                               new SkPDFObjRef(fontStream.get()))->unref();
             break;
         }
         case SkAdvancedTypefaceMetrics::kCFF_Font:
         case SkAdvancedTypefaceMetrics::kType1CID_Font: {
-            SkRefPtr<SkStream> fontData =
-                SkFontHost::OpenStream(SkTypeface::UniqueID(typeface()));
-            fontData->unref();  // SkRefPtr and OpenStream both took a ref.
-            SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
-            // SkRefPtr and new both ref()'d fontStream, pass one.
+            SkAutoTUnref<SkStream> fontData(
+                SkFontHost::OpenStream(SkTypeface::UniqueID(typeface())));
+            SkAutoTUnref<SkPDFStream> fontStream(
+                new SkPDFStream(fontData.get()));
             addResource(fontStream.get());
 
             if (getType() == SkAdvancedTypefaceMetrics::kCFF_Font) {
@@ -1115,9 +1110,6 @@
             SkASSERT(false);
     }
 
-    addResource(descriptor.get());
-    descriptor->ref();
-
     insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
     return addCommonFontDescriptorEntries(defaultWidth);
 }
@@ -1132,20 +1124,18 @@
             subset->exportTo(&glyphIDs);
         }
 
-        SkRefPtr<SkAdvancedTypefaceMetrics> fontMetrics;
         SkAdvancedTypefaceMetrics::PerGlyphInfo info;
         info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo;
         info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
                   info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
         uint32_t* glyphs = (glyphIDs.count() == 1) ? NULL : glyphIDs.begin();
         uint32_t glyphsCount = glyphs ? glyphIDs.count() : 0;
-        fontMetrics =
+        SkAutoTUnref<SkAdvancedTypefaceMetrics> fontMetrics(
             SkFontHost::GetAdvancedTypefaceMetrics(
                     SkTypeface::UniqueID(typeface()),
                     info,
                     glyphs,
-                    glyphsCount);
-        SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref
+                    glyphsCount));
         setFontInfo(fontMetrics.get());
         addFontDescriptor(0, &glyphIDs);
     } else {
@@ -1164,8 +1154,7 @@
         SkASSERT(false);
     }
 
-    SkRefPtr<SkPDFDict> sysInfo = new SkPDFDict;
-    sysInfo->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> sysInfo(new SkPDFDict);
     sysInfo->insert("Registry", new SkPDFString("Adobe"))->unref();
     sysInfo->insert("Ordering", new SkPDFString("Identity"))->unref();
     sysInfo->insertInt("Supplement", 0);
@@ -1173,11 +1162,10 @@
 
     if (fontInfo()->fGlyphWidths.get()) {
         int16_t defaultWidth = 0;
-        SkRefPtr<SkPDFArray> widths =
+        SkAutoTUnref<SkPDFArray> widths(
             composeAdvanceData(fontInfo()->fGlyphWidths.get(),
                                fontInfo()->fEmSize, &appendWidth,
-                               &defaultWidth);
-        widths->unref();  // SkRefPtr and compose both took a reference.
+                               &defaultWidth));
         if (widths->size())
             insert("W", widths.get());
         if (defaultWidth != 0) {
@@ -1190,11 +1178,10 @@
         defaultAdvance.fVerticalAdvance = 0;
         defaultAdvance.fOriginXDisp = 0;
         defaultAdvance.fOriginYDisp = 0;
-        SkRefPtr<SkPDFArray> advances =
+        SkAutoTUnref<SkPDFArray> advances(
             composeAdvanceData(fontInfo()->fVerticalMetrics.get(),
                                fontInfo()->fEmSize, &appendVerticalAdvance,
-                               &defaultAdvance);
-        advances->unref();  // SkRefPtr and compose both took a ref.
+                               &defaultAdvance));
         if (advances->size())
             insert("W2", advances.get());
         if (defaultAdvance.fVerticalAdvance ||
@@ -1224,31 +1211,27 @@
 SkPDFType1Font::~SkPDFType1Font() {}
 
 bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
-    SkRefPtr<SkPDFDict> descriptor = getFontDescriptor();
-    if (descriptor.get() != NULL) {
-        addResource(descriptor.get());
-        descriptor->ref();
-        insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+    if (getFontDescriptor() != NULL) {
+        SkPDFDict* descriptor = getFontDescriptor();
+        addResource(descriptor);
+        insert("FontDescriptor", new SkPDFObjRef(descriptor))->unref();
         return true;
     }
 
-    descriptor = new SkPDFDict("FontDescriptor");
-    descriptor->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFDict> descriptor(new SkPDFDict("FontDescriptor"));
     setFontDescriptor(descriptor.get());
 
     size_t header SK_INIT_TO_AVOID_WARNING;
     size_t data SK_INIT_TO_AVOID_WARNING;
     size_t trailer SK_INIT_TO_AVOID_WARNING;
-    SkRefPtr<SkStream> rawFontData =
-        SkFontHost::OpenStream(SkTypeface::UniqueID(typeface()));
-    rawFontData->unref();  // SkRefPtr and OpenStream both took a ref.
+    SkAutoTUnref<SkStream> rawFontData(
+        SkFontHost::OpenStream(SkTypeface::UniqueID(typeface())));
     SkStream* fontData = handleType1Stream(rawFontData.get(), &header, &data,
                                            &trailer);
     if (fontData == NULL) {
         return false;
     }
-    SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData);
-    // SkRefPtr and new both ref()'d fontStream, pass one.
+    SkAutoTUnref<SkPDFStream> fontStream(new SkPDFStream(fontData));
     addResource(fontStream.get());
     fontStream->insertInt("Length1", header);
     fontStream->insertInt("Length2", data);
@@ -1256,7 +1239,6 @@
     descriptor->insert("FontFile", new SkPDFObjRef(fontStream.get()))->unref();
 
     addResource(descriptor.get());
-    descriptor->ref();
     insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
 
     return addCommonFontDescriptorEntries(defaultWidth);
@@ -1297,12 +1279,10 @@
 
     addWidthInfoFromRange(defaultWidth, widthRangeEntry);
 
-    SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
-    encoding->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> encoding(new SkPDFDict("Encoding"));
     insert("Encoding", encoding.get());
 
-    SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
-    encDiffs->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> encDiffs(new SkPDFArray);
     encoding->insert("Differences", encDiffs.get());
 
     encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
@@ -1317,8 +1297,7 @@
 void SkPDFType1Font::addWidthInfoFromRange(
         int16_t defaultWidth,
         const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) {
-    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
-    widthArray->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFArray> widthArray(new SkPDFArray());
     int firstChar = 0;
     if (widthRangeEntry) {
         const uint16_t emSize = fontInfo()->fEmSize;
@@ -1362,7 +1341,7 @@
     SkPaint paint;
     paint.setTypeface(typeface());
     paint.setTextSize(1000);
-    SkAutoGlyphCache autoCache(paint, NULL);
+    SkAutoGlyphCache autoCache(paint, NULL, NULL);
     SkGlyphCache* cache = autoCache.getCache();
     // If fLastGlyphID isn't set (because there is not fFontInfo), look it up.
     if (lastGlyphID() == 0) {
@@ -1377,22 +1356,18 @@
     fontMatrix.setScale(SkScalarInvert(1000), -SkScalarInvert(1000));
     insert("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix))->unref();
 
-    SkRefPtr<SkPDFDict> charProcs = new SkPDFDict;
-    charProcs->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> charProcs(new SkPDFDict);
     insert("CharProcs", charProcs.get());
 
-    SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
-    encoding->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> encoding(new SkPDFDict("Encoding"));
     insert("Encoding", encoding.get());
 
-    SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
-    encDiffs->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> encDiffs(new SkPDFArray);
     encoding->insert("Differences", encDiffs.get());
     encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
     encDiffs->appendInt(1);
 
-    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
-    widthArray->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFArray> widthArray(new SkPDFArray());
 
     SkIRect bbox = SkIRect::MakeEmpty();
     for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
@@ -1415,13 +1390,11 @@
             SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(),
                                   &content);
         }
-        SkRefPtr<SkMemoryStream> glyphStream = new SkMemoryStream();
-        glyphStream->unref();  // SkRefPtr and new both took a ref.
+        SkAutoTUnref<SkMemoryStream> glyphStream(new SkMemoryStream());
         glyphStream->setData(content.copyToData())->unref();
 
-        SkRefPtr<SkPDFStream> glyphDescription =
-            new SkPDFStream(glyphStream.get());
-        // SkRefPtr and new both ref()'d charProcs, pass one.
+        SkAutoTUnref<SkPDFStream> glyphDescription(
+            new SkPDFStream(glyphStream.get()));
         addResource(glyphDescription.get());
         charProcs->insert(characterName.c_str(),
                           new SkPDFObjRef(glyphDescription.get()))->unref();
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index f463ed7..693b911 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -180,7 +180,7 @@
         FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID);
     };
 
-    SkRefPtr<SkTypeface> fTypeface;
+    SkAutoTUnref<SkTypeface> fTypeface;
 
     // The glyph IDs accessible with this font.  For Type1 (non CID) fonts,
     // this will be a subset if the font has more than 255 glyphs.
@@ -188,9 +188,9 @@
     uint16_t fLastGlyphID;
     // The font info is only kept around after construction for large
     // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs.
-    SkRefPtr<SkAdvancedTypefaceMetrics> fFontInfo;
+    SkAutoTUnref<SkAdvancedTypefaceMetrics> fFontInfo;
     SkTDArray<SkPDFObject*> fResources;
-    SkRefPtr<SkPDFDict> fDescriptor;
+    SkAutoTUnref<SkPDFDict> fDescriptor;
 
     SkAdvancedTypefaceMetrics::FontType fFontType;
 
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
index e148056..5e33995 100644
--- a/src/pdf/SkPDFFormXObject.cpp
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -31,8 +31,7 @@
     getResources(&dummy_resourceList);
 #endif
 
-    SkRefPtr<SkStream> content = device->content();
-    content->unref();  // SkRefPtr and content() both took a reference.
+    SkAutoTUnref<SkStream> content(device->content());
     setData(content.get());
 
     insertName("Type", "XObject");
@@ -55,8 +54,7 @@
 
     // Right now SkPDFFormXObject is only used for saveLayer, which implies
     // isolated blending.  Do this conditionally if that changes.
-    SkRefPtr<SkPDFDict> group = new SkPDFDict("Group");
-    group->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> group(new SkPDFDict("Group"));
     group->insertName("S", "Transparency");
     group->insert("I", new SkPDFBool(true))->unref();  // Isolated.
     insert("Group", group.get());
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index ec9b0e7..ea8621f 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -16,7 +16,7 @@
 static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
     switch (mode) {
         case SkXfermode::kSrcOver_Mode:    return "Normal";
-        case SkXfermode::kMultiply_Mode:   return "Multiply";
+        case SkXfermode::kModulate_Mode:   return "Multiply";
         case SkXfermode::kScreen_Mode:     return "Screen";
         case SkXfermode::kOverlay_Mode:    return "Overlay";
         case SkXfermode::kDarken_Mode:     return "Darken";
@@ -113,16 +113,14 @@
     if (!invertFunction) {
         // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
         // a type 2 function, so we use a type 4 function.
-        SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
-        domainAndRange->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray);
         domainAndRange->reserve(2);
         domainAndRange->appendInt(0);
         domainAndRange->appendInt(1);
 
         static const char psInvert[] = "{1 exch sub}";
-        SkRefPtr<SkMemoryStream> psInvertStream =
-            new SkMemoryStream(&psInvert, strlen(psInvert), true);
-        psInvertStream->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkMemoryStream> psInvertStream(
+            new SkMemoryStream(&psInvert, strlen(psInvert), true));
 
         invertFunction = new SkPDFStream(psInvertStream.get());
         invertFunction->insertInt("FunctionType", 4);
@@ -139,8 +137,7 @@
     // enough that it's not worth canonicalizing.
     SkAutoMutexAcquire lock(CanonicalPaintsMutex());
 
-    SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
-    sMaskDict->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
     sMaskDict->insertName("S", "Alpha");
     sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
 
@@ -200,9 +197,8 @@
         fPopulated = true;
         insertName("Type", "ExtGState");
 
-        SkRefPtr<SkPDFScalar> alpha =
-            new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF));
-        alpha->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFScalar> alpha(
+            new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF)));
         insert("CA", alpha.get());
         insert("ca", alpha.get());
 
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
index 429667a..1b93f6e 100644
--- a/src/pdf/SkPDFImage.cpp
+++ b/src/pdf/SkPDFImage.cpp
@@ -305,8 +305,7 @@
     if (!doingAlpha && alphaOnly) {
         // For alpha only images, we stretch a single pixel of black for
         // the color/shape part.
-        SkRefPtr<SkPDFInt> one = new SkPDFInt(1);
-        one->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
         insert("Width", one.get());
         insert("Height", one.get());
     } else {
@@ -335,16 +334,12 @@
     insertInt("BitsPerComponent", bitsPerComp);
 
     if (config == SkBitmap::kRGB_565_Config) {
-        SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
-        zeroVal->unref();  // SkRefPtr and new both took a reference.
-        SkRefPtr<SkPDFScalar> scale5Val =
-                new SkPDFScalar(SkFloatToScalar(8.2258f));  // 255/2^5-1
-        scale5Val->unref();  // SkRefPtr and new both took a reference.
-        SkRefPtr<SkPDFScalar> scale6Val =
-                new SkPDFScalar(SkFloatToScalar(4.0476f));  // 255/2^6-1
-        scale6Val->unref();  // SkRefPtr and new both took a reference.
-        SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
-        decodeValue->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
+        SkAutoTUnref<SkPDFScalar> scale5Val(
+                new SkPDFScalar(SkFloatToScalar(8.2258f)));  // 255/2^5-1
+        SkAutoTUnref<SkPDFScalar> scale6Val(
+                new SkPDFScalar(SkFloatToScalar(4.0476f)));  // 255/2^6-1
+        SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
         decodeValue->reserve(6);
         decodeValue->append(zeroVal.get());
         decodeValue->append(scale5Val.get());
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
index 717f435..f47f8ff 100644
--- a/src/pdf/SkPDFPage.cpp
+++ b/src/pdf/SkPDFPage.cpp
@@ -15,6 +15,7 @@
 SkPDFPage::SkPDFPage(SkPDFDevice* content)
     : SkPDFDict("Page"),
       fDevice(content) {
+  SkSafeRef(content);
 }
 
 SkPDFPage::~SkPDFPage() {}
@@ -32,10 +33,8 @@
             }
         }
 
-        SkRefPtr<SkStream> content = fDevice->content();
-        content->unref();  // SkRefPtr and content() both took a reference.
-        fContentStream = new SkPDFStream(content.get());
-        fContentStream->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkStream> content(fDevice->content());
+        fContentStream.reset(new SkPDFStream(content.get()));
         insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
     }
     catalog->addObject(fContentStream.get(), firstPage);
@@ -67,12 +66,9 @@
     // one child.
     static const int kNodeSize = 8;
 
-    SkRefPtr<SkPDFName> kidsName = new SkPDFName("Kids");
-    kidsName->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFName> countName = new SkPDFName("Count");
-    countName->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFName> parentName = new SkPDFName("Parent");
-    parentName->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids"));
+    SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count"));
+    SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent"));
 
     // curNodes takes a reference to its items, which it passes to pageTree.
     SkTDArray<SkPDFDict*> curNodes;
@@ -95,11 +91,9 @@
             }
 
             SkPDFDict* newNode = new SkPDFDict("Pages");
-            SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode);
-            newNodeRef->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode));
 
-            SkRefPtr<SkPDFArray> kids = new SkPDFArray;
-            kids->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
             kids->reserve(kNodeSize);
 
             int count = 0;
@@ -118,12 +112,19 @@
                 }
             }
 
-            newNode->insert(kidsName.get(), kids.get());
+            // treeCapacity is the number of leaf nodes possible for the
+            // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
+            // It is hard to count the number of leaf nodes in the current
+            // subtree. However, by construction, we know that unless it's the
+            // last subtree for the current depth, the leaf count will be
+            // treeCapacity, otherwise it's what ever is left over after
+            // consuming treeCapacity chunks.
             int pageCount = treeCapacity;
-            if (count < kNodeSize) {
-                pageCount = pages.count() % treeCapacity;
+            if (i == curNodes.count()) {
+                pageCount = ((pages.count() - 1) % treeCapacity) + 1;
             }
             newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
+            newNode->insert(kidsName.get(), kids.get());
             nextRoundNodes.push(newNode);  // Transfer reference.
         }
 
diff --git a/src/pdf/SkPDFPage.h b/src/pdf/SkPDFPage.h
index 8ef909e..72ba335 100644
--- a/src/pdf/SkPDFPage.h
+++ b/src/pdf/SkPDFPage.h
@@ -91,10 +91,10 @@
 
 private:
     // Multiple pages may reference the content.
-    SkRefPtr<SkPDFDevice> fDevice;
+    SkAutoTUnref<SkPDFDevice> fDevice;
 
     // Once the content is finalized, put it into a stream for output.
-    SkRefPtr<SkPDFStream> fContentStream;
+    SkAutoTUnref<SkPDFStream> fContentStream;
 };
 
 #endif
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index db5beb8..7958de3 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -612,8 +612,7 @@
         return;
     }
 
-    SkRefPtr<SkPDFArray> domain = new SkPDFArray;
-    domain->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> domain(new SkPDFArray);
     domain->reserve(4);
     domain->appendScalar(bbox.fLeft);
     domain->appendScalar(bbox.fRight);
@@ -640,16 +639,14 @@
         functionCode = codeFunction(*info);
     }
 
-    SkRefPtr<SkPDFStream> function = makePSFunction(functionCode, domain.get());
-    // Pass one reference to fResources, SkRefPtr and new both took a reference.
-    fResources.push(function.get());
-
-    SkRefPtr<SkPDFDict> pdfShader = new SkPDFDict;
-    pdfShader->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
     pdfShader->insertInt("ShadingType", 1);
     pdfShader->insertName("ColorSpace", "DeviceRGB");
     pdfShader->insert("Domain", domain.get());
-    pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref();
+
+    SkPDFStream* function = makePSFunction(functionCode, domain.get());
+    pdfShader->insert("Function", new SkPDFObjRef(function))->unref();
+    fResources.push(function);  // Pass ownership to resource list.
 
     insertInt("PatternType", 2);
     insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
@@ -825,8 +822,7 @@
         }
     }
 
-    SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray;
-    patternBBoxArray->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> patternBBoxArray(new SkPDFArray);
     patternBBoxArray->reserve(4);
     patternBBoxArray->appendScalar(patternBBox.fLeft);
     patternBBoxArray->appendScalar(patternBBox.fTop);
@@ -834,11 +830,10 @@
     patternBBoxArray->appendScalar(patternBBox.fBottom);
 
     // Put the canvas into the pattern stream (fContent).
-    SkRefPtr<SkStream> content = pattern.content();
-    content->unref();  // SkRefPtr and content() both took a reference.
+    SkAutoTUnref<SkStream> content(pattern.content());
+    setData(content.get());
     pattern.getResources(&fResources, false);
 
-    setData(content.get());
     insertName("Type", "Pattern");
     insertInt("PatternType", 1);
     insertInt("PaintType", 1);
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
index d113a0b..38e874e 100644
--- a/src/pdf/SkPDFStream.cpp
+++ b/src/pdf/SkPDFStream.cpp
@@ -21,19 +21,20 @@
 SkPDFStream::SkPDFStream(SkStream* stream)
     : fState(kUnused_State),
       fData(stream) {
+    SkSafeRef(stream);
 }
 
 SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) {
     SkMemoryStream* stream = new SkMemoryStream;
     stream->setData(data);
-    fData = stream;
-    fData->unref();  // SkRefPtr and new both took a reference.
+    fData.reset(stream);  // Transfer ownership.
 }
 
 SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream)
         : SkPDFDict(),
           fState(kUnused_State),
-          fData(pdfStream.fData) {
+          fData(pdfStream.fData.get()) {
+    fData.get()->ref();
     bool removeLength = true;
     // Don't uncompress an already compressed stream, but we could.
     if (pdfStream.fState == kCompressed_State) {
@@ -84,7 +85,8 @@
 SkPDFStream::SkPDFStream() : fState(kUnused_State) {}
 
 void SkPDFStream::setData(SkStream* stream) {
-    fData = stream;
+    fData.reset(stream);
+    SkSafeRef(stream);
 }
 
 bool SkPDFStream::populate(SkPDFCatalog* catalog) {
@@ -96,8 +98,7 @@
             if (compressedData.getOffset() < fData->getLength()) {
                 SkMemoryStream* stream = new SkMemoryStream;
                 stream->setData(compressedData.copyToData())->unref();
-                fData = stream;
-                fData->unref();  // SkRefPtr and new both took a reference.
+                fData.reset(stream);  // Transfer ownership.
                 insertName("Filter", "FlateDecode");
             }
             fState = kCompressed_State;
@@ -108,8 +109,7 @@
     } else if (fState == kNoCompression_State && !skip_compression(catalog) &&
                SkFlate::HaveFlate()) {
         if (!fSubstitute.get()) {
-            fSubstitute = new SkPDFStream(*this);
-            fSubstitute->unref();  // SkRefPtr and new both took a reference.
+            fSubstitute.reset(new SkPDFStream(*this));
             catalog->setSubstitute(this, fSubstitute.get());
         }
         return false;
diff --git a/src/pdf/SkPDFStream.h b/src/pdf/SkPDFStream.h
index b3a7ad3..6f7a08e 100644
--- a/src/pdf/SkPDFStream.h
+++ b/src/pdf/SkPDFStream.h
@@ -62,8 +62,8 @@
     State fState;
 
     // TODO(vandebo): Use SkData (after removing deprecated constructor).
-    SkRefPtr<SkStream> fData;
-    SkRefPtr<SkPDFStream> fSubstitute;
+    SkAutoTUnref<SkStream> fData;
+    SkAutoTUnref<SkPDFStream> fSubstitute;
 
     typedef SkPDFDict INHERITED;
 
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 7fb1e95..59250c8 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -73,7 +73,10 @@
     }
 }
 
-SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
+SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {
+    SkSafeRef(obj);
+}
+
 SkPDFObjRef::~SkPDFObjRef() {}
 
 void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index 28034ef..03799d0 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -112,7 +112,7 @@
     virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
 
 private:
-    SkRefPtr<SkPDFObject> fObj;
+    SkAutoTUnref<SkPDFObject> fObj;
 
     typedef SkPDFObject INHERITED;
 };
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index 8a86c58..6cffb1b 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -837,5 +837,3 @@
     }
     return status;
 }
-
-
diff --git a/src/pipe/utils/SamplePipeControllers.cpp b/src/pipe/utils/SamplePipeControllers.cpp
index 59f612b..98fdff3 100644
--- a/src/pipe/utils/SamplePipeControllers.cpp
+++ b/src/pipe/utils/SamplePipeControllers.cpp
@@ -51,7 +51,7 @@
         rect.setLTRB(0, top, bitmap.width(), bottom);
         top = bottom;
 
-        bool extracted = bitmap.extractSubset(&fBitmaps[i], rect);
+        SkDEBUGCODE(bool extracted = )bitmap.extractSubset(&fBitmaps[i], rect);
         SkASSERT(extracted);
         SkDevice* device = new SkDevice(fBitmaps[i]);
         SkCanvas* canvas = new SkCanvas(device);
diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp
index aed7001..6d03414 100644
--- a/src/ports/FontHostConfiguration_android.cpp
+++ b/src/ports/FontHostConfiguration_android.cpp
@@ -28,13 +28,17 @@
  * can read these variables that are relevant to the current parsing.
  */
 struct FamilyData {
-    FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) :
-            parser(parserRef), families(familiesRef), currentTag(NO_TAG) {};
+    FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef, const AndroidLocale &localeRef) :
+            parser(parserRef), families(familiesRef), currentTag(NO_TAG),
+            locale(localeRef), currentFamilyLangMatch(false), familyLangMatchCount(0) {}
 
     XML_Parser *parser;                // The expat parser doing the work
     SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
     FontFamily *currentFamily;         // The current family being created
     int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
+    const AndroidLocale &locale;       // The locale to which we compare the "lang" attribute of File.
+    bool currentFamilyLangMatch;       // If currentFamily's File has a "lang" attribute and matches locale.
+    int familyLangMatchCount;          // Number of families containing File which has a "lang" attribute and matches locale.
 };
 
 /**
@@ -90,10 +94,28 @@
         familyData->currentTag = NAMESET_TAG;
     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
         familyData->currentTag = FILESET_TAG;
-    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
-            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
-        // If it's a Name, parse the text inside
+    } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
         XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+    } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
+        // From JB MR1, the File tag has a "lang" attribute to specify a language specific font file
+        // and the family entry has higher priority than the others without "lang" attribute.
+        bool includeTheEntry = true;
+        for (int i = 0; atts[i] != NULL; i += 2) {
+            const char* attribute = atts[i];
+            const char* value = atts[i+1];
+            if (strncmp(attribute, "lang", 4) == 0) {
+                if (strcmp(value, familyData->locale.language) == 0) {
+                    // Found matching "lang" attribute. The current Family will have higher priority in the family list.
+                    familyData->currentFamilyLangMatch = true;
+                } else {
+                    // Don't include the entry if "lang" is specified but not matching.
+                    includeTheEntry = false;
+                }
+            }
+        }
+        if (includeTheEntry) {
+            XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+        }
     }
 }
 
@@ -106,7 +128,12 @@
     int len = strlen(tag);
     if (strncmp(tag, "family", len)== 0) {
         // Done parsing a Family - store the created currentFamily in the families array
-        *familyData->families.append() = familyData->currentFamily;
+        if (familyData->currentFamilyLangMatch) {
+            *familyData->families.insert(familyData->familyLangMatchCount++) = familyData->currentFamily;
+            familyData->currentFamilyLangMatch = false;
+        } else {
+            *familyData->families.append() = familyData->currentFamily;
+        }
         familyData->currentFamily = NULL;
     } else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
         familyData->currentTag = NO_TAG;
@@ -150,18 +177,16 @@
  *      /system/etc/fallback_fonts-ja.xml
  *      /system/etc/fallback_fonts.xml
  */
-FILE* openLocalizedFile(const char* origname) {
+FILE* openLocalizedFile(const char* origname, const AndroidLocale& locale) {
     FILE* file = 0;
     SkString basename;
     SkString filename;
-    AndroidLocale locale;
 
     basename.set(origname);
     // Remove the .xml suffix. We'll add it back in a moment.
     if (basename.endsWith(".xml")) {
         basename.resize(basename.size()-4);
     }
-    getLocale(locale);
     // Try first with language and region
     filename.printf("%s-%s-%s.xml", basename.c_str(), locale.language, locale.region);
     file = fopen(filename.c_str(), "r");
@@ -183,11 +208,13 @@
  * families array.
  */
 void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
+    AndroidLocale locale;
+    getLocale(locale);
     XML_Parser parser = XML_ParserCreate(NULL);
-    FamilyData *familyData = new FamilyData(&parser, families);
-    XML_SetUserData(parser, familyData);
+    FamilyData familyData(&parser, families, locale);
+    XML_SetUserData(parser, &familyData);
     XML_SetElementHandler(parser, startElementHandler, endElementHandler);
-    FILE *file = openLocalizedFile(filename);
+    FILE *file = openLocalizedFile(filename, locale);
     // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
     // are optional - failure here is okay because one of these optional files may not exist.
     if (file == NULL) {
@@ -203,6 +230,8 @@
         }
         XML_Parse(parser, buffer, len, done);
     }
+    fclose(file);
+    XML_ParserFree(parser);
 }
 
 void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
diff --git a/src/ports/SkDebug_nacl.cpp b/src/ports/SkDebug_nacl.cpp
index b1d260f..6e35f09 100644
--- a/src/ports/SkDebug_nacl.cpp
+++ b/src/ports/SkDebug_nacl.cpp
@@ -36,4 +36,3 @@
         gPluginInstance->PostMessage(msg);
     }
 }
-
diff --git a/src/ports/SkDebug_stdio.cpp b/src/ports/SkDebug_stdio.cpp
index bc3d98b..318269e 100644
--- a/src/ports/SkDebug_stdio.cpp
+++ b/src/ports/SkDebug_stdio.cpp
@@ -22,4 +22,3 @@
     va_end(args);
     fprintf(stderr, "%s", buffer);
 }
-
diff --git a/src/ports/SkDebug_win.cpp b/src/ports/SkDebug_win.cpp
index b3077f1..4b340b0 100644
--- a/src/ports/SkDebug_win.cpp
+++ b/src/ports/SkDebug_win.cpp
@@ -26,4 +26,3 @@
     OutputDebugStringA(buffer);
     printf(buffer);
 }
-
diff --git a/src/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp
index 69e73f5..3b5e993 100644
--- a/src/ports/SkFontHost_FONTPATH.cpp
+++ b/src/ports/SkFontHost_FONTPATH.cpp
@@ -320,4 +320,3 @@
 
     return SkFontHost::CreateScalerContext(desc);
 }
-
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 71899f4..47e77ac 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -1366,4 +1366,3 @@
     FT_Done_FreeType(library);
     return true;
 }
-
diff --git a/src/ports/SkFontHost_ascender.cpp b/src/ports/SkFontHost_ascender.cpp
index 067d474..d20cc82 100644
--- a/src/ports/SkFontHost_ascender.cpp
+++ b/src/ports/SkFontHost_ascender.cpp
@@ -227,4 +227,3 @@
 {
     return SkNEW_ARGS(SkScalerContext_Ascender, (desc));
 }
-
diff --git a/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp
index 0f79a30..355ea79 100644
--- a/src/ports/SkFontHost_fontconfig.cpp
+++ b/src/ports/SkFontHost_fontconfig.cpp
@@ -249,4 +249,3 @@
     // We don't handle font fallback, WebKit does.
     return 0;
 }
-
diff --git a/src/ports/SkFontHost_freetype_mac.cpp b/src/ports/SkFontHost_freetype_mac.cpp
index 652fb46..bc197d6 100644
--- a/src/ports/SkFontHost_freetype_mac.cpp
+++ b/src/ports/SkFontHost_freetype_mac.cpp
@@ -103,4 +103,3 @@
     SkDEBUGFAIL("Not supported");
     return NULL;
 }
-
diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
index adcee49..07235c8 100644
--- a/src/ports/SkFontHost_linux.cpp
+++ b/src/ports/SkFontHost_linux.cpp
@@ -504,7 +504,6 @@
 
     SkFontDescriptor descriptor(stream);
     const char* familyName = descriptor.getFamilyName();
-    const char* typefaceName = descriptor.getFontFileName();
     const SkTypeface::Style style = descriptor.getStyle();
 
     const uint32_t customFontDataLength = stream->readPackedUInt();
@@ -600,4 +599,3 @@
     stream->unref();
     return face;
 }
-
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index 7d12f53..79c5541 100755
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -35,5 +35,3 @@
 #else
     #include "SkFontHost_mac_atsui.cpp"
 #endif
-
-
diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
index 527b981..ceb9a04 100644
--- a/src/ports/SkFontHost_mac_coretext.cpp
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -1524,11 +1524,14 @@
     info->fDescent = (int16_t) CTFontGetDescent(ctFont);
     info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
     CGRect bbox = CTFontGetBoundingBox(ctFont);
-    info->fBBox = SkIRect::MakeLTRB(
-            CGToScalar(CGRectGetMinX_inline(bbox)),   // Left
-            CGToScalar(CGRectGetMaxY_inline(bbox)),   // Top
-            CGToScalar(CGRectGetMaxX_inline(bbox)),   // Right
-            CGToScalar(CGRectGetMinY_inline(bbox)));  // Bottom
+
+    SkRect r;
+    r.set( CGToScalar(CGRectGetMinX_inline(bbox)),   // Left
+           CGToScalar(CGRectGetMaxY_inline(bbox)),   // Top
+           CGToScalar(CGRectGetMaxX_inline(bbox)),   // Right
+           CGToScalar(CGRectGetMinY_inline(bbox)));  // Bottom
+
+    r.roundOut(&(info->fBBox));
 
     // Figure out a good guess for StemV - Min width of i, I, !, 1.
     // This probably isn't very good with an italic font.
diff --git a/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp
index 21eda8e..6475c09 100644
--- a/src/ports/SkFontHost_none.cpp
+++ b/src/ports/SkFontHost_none.cpp
@@ -74,5 +74,3 @@
 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
     return 0;
 }
-
-
diff --git a/src/ports/SkFontHost_simple.cpp b/src/ports/SkFontHost_simple.cpp
index ac3c713..101d576 100644
--- a/src/ports/SkFontHost_simple.cpp
+++ b/src/ports/SkFontHost_simple.cpp
@@ -642,4 +642,3 @@
     stream->unref();
     return face;
 }
-
diff --git a/src/ports/SkFontHost_tables.cpp b/src/ports/SkFontHost_tables.cpp
index 9878119..38d2e60 100644
--- a/src/ports/SkFontHost_tables.cpp
+++ b/src/ports/SkFontHost_tables.cpp
@@ -203,4 +203,3 @@
     }
     return 0;
 }
-
diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp
index 67ee5c4..1f257eb 100644
--- a/src/ports/SkFontHost_win_dw.cpp
+++ b/src/ports/SkFontHost_win_dw.cpp
@@ -769,9 +769,7 @@
 
     SkVector vecs[1] = { { advanceX, 0 } };
     SkMatrix mat;
-    mat.setAll(fRec.fPost2x2[0][0], fRec.fPost2x2[0][1], 0,
-               fRec.fPost2x2[1][0], fRec.fPost2x2[1][1], 0,
-               0, 0, SkScalarToPersp(SK_Scalar1));
+    fRec.getMatrixFrom2x2(&mat);
     mat.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
 
     glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX);
@@ -1037,6 +1035,10 @@
                                        FALSE, //rtl
                                        geometryToPath.get()),
          "Could not create glyph outline.");
+
+    SkMatrix mat;
+    fRec.getMatrixFrom2x2(&mat);
+    path->transform(mat);
 }
 
 void SkFontHost::Serialize(const SkTypeface* rawFace, SkWStream* stream) {
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index 4ed7f5e..26a61ca 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -8,7 +8,6 @@
 #include "SkTypes.h"
 
 #include "SkBitmapProcShader.h"
-#include "SkImageRef_ashmem.h"
 #include "SkMallocPixelRef.h"
 #include "SkPathEffect.h"
 #include "SkPixelRef.h"
@@ -17,6 +16,7 @@
 #include "Sk1DPathEffect.h"
 #include "Sk2DPathEffect.h"
 #include "SkAvoidXfermode.h"
+#include "SkBicubicImageFilter.h"
 #include "SkBitmapSource.h"
 #include "SkBlendImageFilter.h"
 #include "SkBlurDrawLooper.h"
@@ -31,6 +31,7 @@
 #include "SkCornerPathEffect.h"
 #include "SkDashPathEffect.h"
 #include "SkDiscretePathEffect.h"
+#include "SkDisplacementMapEffect.h"
 #include "SkEmptyShader.h"
 #include "SkEmbossMaskFilter.h"
 #include "SkFlattenable.h"
@@ -52,6 +53,7 @@
 void SkFlattenable::InitializeFlattenables() {
 
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBicubicImageFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapSource)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlendImageFilter)
@@ -66,6 +68,7 @@
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter)
diff --git a/src/ports/SkHarfBuzzFont.cpp b/src/ports/SkHarfBuzzFont.cpp
index 12f37f5..b5cd0f7 100644
--- a/src/ports/SkHarfBuzzFont.cpp
+++ b/src/ports/SkHarfBuzzFont.cpp
@@ -185,4 +185,3 @@
     SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer);
     return HB_Err_Ok;
 }
-
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
index afe4cf4..f8ec108 100644
--- a/src/ports/SkImageDecoder_CG.cpp
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -219,4 +219,3 @@
     }
     return SkNEW_ARGS(SkImageEncoder_CG, (t));
 }
-
diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
index d83611f..4b869dd 100644
--- a/src/ports/SkImageDecoder_WIC.cpp
+++ b/src/ports/SkImageDecoder_WIC.cpp
@@ -19,6 +19,15 @@
 #include "SkTScopedComPtr.h"
 #include "SkUnPreMultiply.h"
 
+//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
+//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
+//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
+//Undo this #define if it has been done so that we link against the symbols
+//we intended to link against on all SDKs.
+#if defined(CLSID_WICImagingFactory)
+#undef CLSID_WICImagingFactory
+#endif
+
 class SkImageDecoder_WIC : public SkImageDecoder {
 protected:
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
@@ -325,4 +334,3 @@
     }
     return SkNEW_ARGS(SkImageEncoder_WIC, (t));
 }
-
diff --git a/src/ports/SkMemory_malloc.cpp b/src/ports/SkMemory_malloc.cpp
index 44e43ad..73b5607 100644
--- a/src/ports/SkMemory_malloc.cpp
+++ b/src/ports/SkMemory_malloc.cpp
@@ -49,4 +49,3 @@
     }
     return p;
 }
-
diff --git a/src/ports/SkMemory_mozalloc.cpp b/src/ports/SkMemory_mozalloc.cpp
index 1f16ee5..2c049b2 100644
--- a/src/ports/SkMemory_mozalloc.cpp
+++ b/src/ports/SkMemory_mozalloc.cpp
@@ -37,4 +37,3 @@
 void* sk_malloc_flags(size_t size, unsigned flags) {
     return (flags & SK_MALLOC_THROW) ? moz_xmalloc(size) : moz_malloc(size);
 }
-
diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp
index 9100f6d..1d27c6b 100644
--- a/src/ports/SkOSFile_stdio.cpp
+++ b/src/ports/SkOSFile_stdio.cpp
@@ -41,6 +41,16 @@
     return f;
 }
 
+char* sk_fgets(char* str, int size, SkFILE* f) {
+    return ::fgets(str, size, (FILE *)f);
+}
+
+
+int sk_feof(SkFILE *f) {
+    // no :: namespace qualifier because it breaks android
+    return feof((FILE *)f);
+}
+
 size_t sk_fgetsize(SkFILE* f)
 {
     SkASSERT(f);
diff --git a/src/ports/SkThread_pthread.cpp b/src/ports/SkThread_pthread.cpp
index b430dd9..370f673 100644
--- a/src/ports/SkThread_pthread.cpp
+++ b/src/ports/SkThread_pthread.cpp
@@ -218,4 +218,3 @@
 void SkTLS::PlatformSetSpecific(void* ptr) {
     (void)pthread_setspecific(gSkTLSKey, ptr);
 }
-
diff --git a/src/ports/SkXMLParser_expat.cpp b/src/ports/SkXMLParser_expat.cpp
index 8c1c2bf..afc9f79 100644
--- a/src/ports/SkXMLParser_expat.cpp
+++ b/src/ports/SkXMLParser_expat.cpp
@@ -138,4 +138,3 @@
     if (str)
         str->set(XML_ErrorString((XML_Error) error));
 }
-
diff --git a/src/ports/SkXMLParser_tinyxml.cpp b/src/ports/SkXMLParser_tinyxml.cpp
index de1fbe5..f357592 100644
--- a/src/ports/SkXMLParser_tinyxml.cpp
+++ b/src/ports/SkXMLParser_tinyxml.cpp
@@ -85,4 +85,3 @@
     if (str)
         str->set("GetNativeErrorString not implemented for TinyXml");
 }
-
diff --git a/src/ports/SkXMLPullParser_expat.cpp b/src/ports/SkXMLPullParser_expat.cpp
index 5fcdff3..1d1615b 100644
--- a/src/ports/SkXMLPullParser_expat.cpp
+++ b/src/ports/SkXMLPullParser_expat.cpp
@@ -211,4 +211,3 @@
     }
     return fCurr.fEventType;
 }
-
diff --git a/src/sfnt/SkOTTable_glyf.h b/src/sfnt/SkOTTable_glyf.h
index 30c99f6..ac34d7b 100644
--- a/src/sfnt/SkOTTable_glyf.h
+++ b/src/sfnt/SkOTTable_glyf.h
@@ -40,7 +40,7 @@
         void advance(uint16_t num) {
             fLocaPtr.shortOffset += num << fLocaFormat;
             fCurrentGlyphOffset = fLocaFormat ? SkEndian_SwapBE32(*fLocaPtr.longOffset)
-                                              : SkEndian_SwapBE16(*fLocaPtr.shortOffset) << 1;
+                                              : uint32_t(SkEndian_SwapBE16(*fLocaPtr.shortOffset) << 1);
         }
         const SkOTTableGlyphData* next() {
             uint32_t previousGlyphOffset = fCurrentGlyphOffset;
diff --git a/src/svg/SkSVGElements.cpp b/src/svg/SkSVGElements.cpp
index 66dcd4e..a1bd100 100644
--- a/src/svg/SkSVGElements.cpp
+++ b/src/svg/SkSVGElements.cpp
@@ -84,5 +84,3 @@
 void SkSVGElement::write(SkSVGParser& , SkString& ) {
     SkASSERT(0);
 }
-
-
diff --git a/src/svg/SkSVGFilter.h b/src/svg/SkSVGFilter.h
index 5891237..f615bd0 100644
--- a/src/svg/SkSVGFilter.h
+++ b/src/svg/SkSVGFilter.h
@@ -25,4 +25,3 @@
 };
 
 #endif // SkSVGFilter_DEFINEDRITED;
-
diff --git a/src/svg/SkSVGGradient.cpp b/src/svg/SkSVGGradient.cpp
index d5f7639..bbcca18 100644
--- a/src/svg/SkSVGGradient.cpp
+++ b/src/svg/SkSVGGradient.cpp
@@ -112,4 +112,3 @@
     parser.fSuppressPaint = false;
     parser.fHead = saveHead;
 }
-
diff --git a/src/svg/SkSVGPaintState.cpp b/src/svg/SkSVGPaintState.cpp
index db30900..5db624d 100644
--- a/src/svg/SkSVGPaintState.cpp
+++ b/src/svg/SkSVGPaintState.cpp
@@ -452,4 +452,3 @@
     SkSVGPaint* next = (*head)->fNext;
     *head = next;
 }
-
diff --git a/src/text/SkTextLayout.cpp b/src/text/SkTextLayout.cpp
index 4d87ffe..4e531cf 100644
--- a/src/text/SkTextLayout.cpp
+++ b/src/text/SkTextLayout.cpp
@@ -78,4 +78,3 @@
 
 void SkTextLayout::draw(SkCanvas* canvas) {
 }
-
diff --git a/src/utils/SkBase64.cpp b/src/utils/SkBase64.cpp
index 8e1f223..11b647f 100644
--- a/src/utils/SkBase64.cpp
+++ b/src/utils/SkBase64.cpp
@@ -183,5 +183,3 @@
     }
 }
 #endif
-
-
diff --git a/src/utils/SkBoundaryPatch.cpp b/src/utils/SkBoundaryPatch.cpp
index afc76b5..fd1545d 100644
--- a/src/utils/SkBoundaryPatch.cpp
+++ b/src/utils/SkBoundaryPatch.cpp
@@ -79,4 +79,3 @@
     SkEvalCubicAt(&fPts[e * 3], t, &loc, NULL, NULL);
     return loc;
 }
-
diff --git a/src/utils/SkCamera.cpp b/src/utils/SkCamera.cpp
index 11e10f6..a7c0b14 100644
--- a/src/utils/SkCamera.cpp
+++ b/src/utils/SkCamera.cpp
@@ -423,4 +423,3 @@
     this->getMatrix(&matrix);
     canvas->concat(matrix);
 }
-
diff --git a/src/utils/SkCountdown.cpp b/src/utils/SkCountdown.cpp
index 5b476cc..98b3545 100644
--- a/src/utils/SkCountdown.cpp
+++ b/src/utils/SkCountdown.cpp
@@ -30,4 +30,3 @@
     }
     fReady.unlock();
 }
-
diff --git a/src/utils/SkCubicInterval.cpp b/src/utils/SkCubicInterval.cpp
index 904f26b..566023a 100644
--- a/src/utils/SkCubicInterval.cpp
+++ b/src/utils/SkCubicInterval.cpp
@@ -65,4 +65,3 @@
     y2 *= 3;
     return eval_cubic(y1, y2 - 2*y1, y1 - y2 + SK_Scalar1, t);
 }
-
diff --git a/src/utils/SkCullPoints.cpp b/src/utils/SkCullPoints.cpp
index e76679d..efb94f4 100644
--- a/src/utils/SkCullPoints.cpp
+++ b/src/utils/SkCullPoints.cpp
@@ -217,4 +217,3 @@
     SkRect r = SkRect::MakeXYWH(x - half, y - half, one, one);
     return SkHitTestPath(path, r, hires);
 }
-
diff --git a/src/utils/SkDebugTrace.h b/src/utils/SkDebugTrace.h
index 447418e..5ec3e1c 100644
--- a/src/utils/SkDebugTrace.h
+++ b/src/utils/SkDebugTrace.h
@@ -22,5 +22,3 @@
   SkDebugf("Trace: %s (%s=%s, %s=%s)\n", event, name1, value1, name2, value2)
 
 #endif
-
-
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index a1e32bc..5119729 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -1,6 +1,6 @@
 
 /*
- * Copyright 2012 Google Inc.
+ * Copyright 2013 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -14,13 +14,14 @@
 #include "SkDrawFilter.h"
 #include "SkGPipe.h"
 #include "SkPaint.h"
+#include "SkPaintPriv.h"
 #include "SkRRect.h"
 #include "SkShader.h"
 
 enum {
     // Deferred canvas will auto-flush when recording reaches this limit
     kDefaultMaxRecordingStorageBytes = 64*1024*1024,
-    kDeferredCanvasBitmapSizeThreshold = ~0, // Disables this feature
+    kDeferredCanvasBitmapSizeThreshold = ~0U, // Disables this feature
 };
 
 enum PlaybackMode {
@@ -54,75 +55,6 @@
 }
 }
 
-namespace {
-
-bool isPaintOpaque(const SkPaint* paint,
-                   const SkBitmap* bmpReplacesShader = NULL) {
-    // TODO: SkXfermode should have a virtual isOpaque method, which would
-    // make it possible to test modes that do not have a Coeff representation.
-
-    if (!paint) {
-        return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
-    }
-
-    SkXfermode::Coeff srcCoeff, dstCoeff;
-    if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
-        if (SkXfermode::kDA_Coeff == srcCoeff || SkXfermode::kDC_Coeff == srcCoeff ||
-            SkXfermode::kIDA_Coeff == srcCoeff || SkXfermode::kIDC_Coeff == srcCoeff) {
-            return false;
-        }
-        switch (dstCoeff) {
-        case SkXfermode::kZero_Coeff:
-            return true;
-        case SkXfermode::kISA_Coeff:
-            if (paint->getAlpha() != 255) {
-                break;
-            }
-            if (bmpReplacesShader) {
-                if (!bmpReplacesShader->isOpaque()) {
-                    break;
-                }
-            } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
-                break;
-            }
-            if (paint->getColorFilter() &&
-                ((paint->getColorFilter()->getFlags() &
-                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
-                break;
-            }
-            return true;
-        case SkXfermode::kSA_Coeff:
-            if (paint->getAlpha() != 0) {
-                break;
-            }
-            if (paint->getColorFilter() &&
-                ((paint->getColorFilter()->getFlags() &
-                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
-                break;
-            }
-            return true;
-        case SkXfermode::kSC_Coeff:
-            if (paint->getColor() != 0) { // all components must be 0
-                break;
-            }
-            if (bmpReplacesShader || paint->getShader()) {
-                break;
-            }
-            if (paint->getColorFilter() && (
-                (paint->getColorFilter()->getFlags() &
-                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
-                break;
-            }
-            return true;
-        default:
-            break;
-        }
-    }
-    return false;
-}
-
-} // unnamed namespace
-
 //-----------------------------------------------------------------------------
 // DeferredPipeController
 //-----------------------------------------------------------------------------
@@ -320,8 +252,10 @@
 
 DeferredDevice::DeferredDevice(
     SkDevice* immediateDevice, SkDeferredCanvas::NotificationClient* notificationClient) :
-    SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
-             immediateDevice->height(), immediateDevice->isOpaque())
+    SkDevice(SkBitmap::kNo_Config,
+             immediateDevice->width(), immediateDevice->height(),
+             immediateDevice->isOpaque(),
+             immediateDevice->getDeviceProperties())
     , fRecordingCanvas(NULL)
     , fFreshFrame(true)
     , fPreviousStorageAllocated(0)
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
index 2722fb4..46c4141 100644
--- a/src/utils/SkDumpCanvas.cpp
+++ b/src/utils/SkDumpCanvas.cpp
@@ -5,7 +5,10 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkDumpCanvas.h"
+
+#ifdef SK_DEVELOPER
 #include "SkPicture.h"
 #include "SkPixelRef.h"
 #include "SkRRect.h"
@@ -130,31 +133,6 @@
     return gPMNames[pm];
 }
 
-static const char* toString(SkBitmap::Config config) {
-    static const char* gConfigNames[] = {
-        "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888", "RLE"
-    };
-    return gConfigNames[config];
-}
-
-static void toString(const SkBitmap& bm, SkString* str) {
-    str->appendf("bitmap:[%d %d] %s", bm.width(), bm.height(),
-                toString(bm.config()));
-
-    SkPixelRef* pr = bm.pixelRef();
-    if (NULL == pr) {
-        // show null or the explicit pixel address (rare)
-        str->appendf(" pixels:%p", bm.getPixels());
-    } else {
-        const char* uri = pr->getURI();
-        if (uri) {
-            str->appendf(" uri:\"%s\"", uri);
-        } else {
-            str->appendf(" pixelref:%p", pr);
-        }
-    }
-}
-
 static void toString(const void* text, size_t byteLen, SkPaint::TextEncoding enc,
                      SkString* str) {
     // FIXME: this code appears to be untested - and probably unused - and probably wrong
@@ -273,14 +251,14 @@
 
 bool SkDumpCanvas::concat(const SkMatrix& matrix) {
     SkString str;
-    matrix.toDumpString(&str);
+    matrix.toString(&str);
     this->dump(kMatrix_Verb, NULL, "concat(%s)", str.c_str());
     return this->INHERITED::concat(matrix);
 }
 
 void SkDumpCanvas::setMatrix(const SkMatrix& matrix) {
     SkString str;
-    matrix.toDumpString(&str);
+    matrix.toString(&str);
     this->dump(kMatrix_Verb, NULL, "setMatrix(%s)", str.c_str());
     this->INHERITED::setMatrix(matrix);
 }
@@ -362,7 +340,7 @@
 void SkDumpCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
                                const SkPaint* paint) {
     SkString str;
-    toString(bitmap, &str);
+    bitmap.toString(&str);
     this->dump(kDrawBitmap_Verb, paint, "drawBitmap(%s %g %g)", str.c_str(),
                SkScalarToFloat(x), SkScalarToFloat(y));
 }
@@ -370,7 +348,7 @@
 void SkDumpCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                                         const SkRect& dst, const SkPaint* paint) {
     SkString bs, rs;
-    toString(bitmap, &bs);
+    bitmap.toString(&bs);
     toString(dst, &rs);
     // show the src-rect only if its not everything
     if (src && (src->fLeft > 0 || src->fTop > 0 ||
@@ -388,8 +366,8 @@
 void SkDumpCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
                                      const SkPaint* paint) {
     SkString bs, ms;
-    toString(bitmap, &bs);
-    m.toDumpString(&ms);
+    bitmap.toString(&bs);
+    m.toString(&ms);
     this->dump(kDrawBitmap_Verb, paint, "drawBitmapMatrix(%s %s)",
                bs.c_str(), ms.c_str());
 }
@@ -397,7 +375,7 @@
 void SkDumpCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
                                const SkPaint* paint) {
     SkString str;
-    toString(bitmap, &str);
+    bitmap.toString(&str);
     this->dump(kDrawBitmap_Verb, paint, "drawSprite(%s %d %d)", str.c_str(),
                x, y);
 }
@@ -525,4 +503,4 @@
 
 SkDebugfDumper::SkDebugfDumper() : INHERITED(dumpToDebugf, NULL) {}
 
-
+#endif
diff --git a/src/utils/SkInterpolator.cpp b/src/utils/SkInterpolator.cpp
index 2ef2702..2853b07 100644
--- a/src/utils/SkInterpolator.cpp
+++ b/src/utils/SkInterpolator.cpp
@@ -81,7 +81,7 @@
         if (offsetTime >= endTime) {
             SkScalar fraction = SkScalarFraction(fRepeat);
             offsetTime = fraction == 0 && fRepeat > 0 ? totalTime :
-                SkScalarMulFloor(fraction, totalTime);
+                (SkMSec) SkScalarMulFloor(fraction, totalTime);
             result = kFreezeEnd_Result;
         } else {
             int mirror = fFlags & kMirror;
@@ -329,4 +329,3 @@
 }
 
 #endif
-
diff --git a/src/utils/SkJSON.cpp b/src/utils/SkJSON.cpp
index 06989f3..9b12208 100644
--- a/src/utils/SkJSON.cpp
+++ b/src/utils/SkJSON.cpp
@@ -632,6 +632,3 @@
         prev = dup_string(str);
     }
 }
-
-
-
diff --git a/src/utils/SkLayer.cpp b/src/utils/SkLayer.cpp
index 54f1840..a96d921 100644
--- a/src/utils/SkLayer.cpp
+++ b/src/utils/SkLayer.cpp
@@ -230,4 +230,3 @@
         }
     }
 }
-
diff --git a/src/utils/SkMD5.cpp b/src/utils/SkMD5.cpp
new file mode 100644
index 0000000..725ae55
--- /dev/null
+++ b/src/utils/SkMD5.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The following code is based on the description in RFC 1321.
+ * http://www.ietf.org/rfc/rfc1321.txt
+ */
+
+#include "SkTypes.h"
+#include "SkMD5.h"
+#include <string.h>
+
+/** MD5 basic transformation. Transforms state based on block. */
+static void transform(uint32_t state[4], const uint8_t block[64]);
+
+/** Encodes input into output (4 little endian 32 bit values). */
+static void encode(uint8_t output[16], const uint32_t input[4]);
+
+/** Encodes input into output (little endian 64 bit value). */
+static void encode(uint8_t output[8], const uint64_t input);
+
+/** Decodes input (4 little endian 32 bit values) into storage, if required. */
+static const uint32_t* decode(uint32_t storage[16], const uint8_t input[64]);
+
+SkMD5::SkMD5() : byteCount(0) {
+    // These are magic numbers from the specification.
+    this->state[0] = 0x67452301;
+    this->state[1] = 0xefcdab89;
+    this->state[2] = 0x98badcfe;
+    this->state[3] = 0x10325476;
+}
+
+void SkMD5::update(const uint8_t* input, size_t inputLength) {
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int bufferAvailable = 64 - bufferIndex;
+
+    unsigned int inputIndex;
+    if (inputLength >= bufferAvailable) {
+        if (bufferIndex) {
+            memcpy(&this->buffer[bufferIndex], input, bufferAvailable);
+            transform(this->state, this->buffer);
+            inputIndex = bufferAvailable;
+        } else {
+            inputIndex = 0;
+        }
+
+        for (; inputIndex + 63 < inputLength; inputIndex += 64) {
+            transform(this->state, &input[inputIndex]);
+        }
+
+        bufferIndex = 0;
+    } else {
+        inputIndex = 0;
+    }
+
+    memcpy(&this->buffer[bufferIndex], &input[inputIndex], inputLength - inputIndex);
+
+    this->byteCount += inputLength;
+}
+
+void SkMD5::finish(Digest& digest) {
+    // Get the number of bits before padding.
+    uint8_t bits[8];
+    encode(bits, this->byteCount << 3);
+
+    // Pad out to 56 mod 64.
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int paddingLength = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
+    static uint8_t PADDING[64] = {
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    };
+    this->update(PADDING, paddingLength);
+
+    // Append length (length before padding, will cause final update).
+    this->update(bits, 8);
+
+    // Write out digest.
+    encode(digest.data, this->state);
+
+#if defined(SK_MD5_CLEAR_DATA)
+    // Clear state.
+    memset(this, 0, sizeof(*this));
+#endif
+}
+
+struct F { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    //return (x & y) | ((~x) & z);
+    return ((y ^ z) & x) ^ z; //equivelent but faster
+}};
+
+struct G { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    return (x & z) | (y & (~z));
+    //return ((x ^ y) & z) ^ y; //equivelent but slower
+}};
+
+struct H { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    return x ^ y ^ z;
+}};
+
+struct I { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    return y ^ (x | (~z));
+}};
+
+/** Rotates x left n bits. */
+static inline uint32_t rotate_left(uint32_t x, uint8_t n) {
+    return (x << n) | (x >> (32 - n));
+}
+
+template <typename T>
+static inline void operation(T operation, uint32_t& a, uint32_t b, uint32_t c, uint32_t d,
+                             uint32_t x, uint8_t s, uint32_t t) {
+    a = b + rotate_left(a + operation(b, c, d) + x + t, s);
+}
+
+static void transform(uint32_t state[4], const uint8_t block[64]) {
+    uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+
+    uint32_t storage[16];
+    const uint32_t* X = decode(storage, block);
+
+    // Round 1
+    operation(F(), a, b, c, d, X[ 0],  7, 0xd76aa478); // 1
+    operation(F(), d, a, b, c, X[ 1], 12, 0xe8c7b756); // 2
+    operation(F(), c, d, a, b, X[ 2], 17, 0x242070db); // 3
+    operation(F(), b, c, d, a, X[ 3], 22, 0xc1bdceee); // 4
+    operation(F(), a, b, c, d, X[ 4],  7, 0xf57c0faf); // 5
+    operation(F(), d, a, b, c, X[ 5], 12, 0x4787c62a); // 6
+    operation(F(), c, d, a, b, X[ 6], 17, 0xa8304613); // 7
+    operation(F(), b, c, d, a, X[ 7], 22, 0xfd469501); // 8
+    operation(F(), a, b, c, d, X[ 8],  7, 0x698098d8); // 9
+    operation(F(), d, a, b, c, X[ 9], 12, 0x8b44f7af); // 10
+    operation(F(), c, d, a, b, X[10], 17, 0xffff5bb1); // 11
+    operation(F(), b, c, d, a, X[11], 22, 0x895cd7be); // 12
+    operation(F(), a, b, c, d, X[12],  7, 0x6b901122); // 13
+    operation(F(), d, a, b, c, X[13], 12, 0xfd987193); // 14
+    operation(F(), c, d, a, b, X[14], 17, 0xa679438e); // 15
+    operation(F(), b, c, d, a, X[15], 22, 0x49b40821); // 16
+
+    // Round 2
+    operation(G(), a, b, c, d, X[ 1],  5, 0xf61e2562); // 17
+    operation(G(), d, a, b, c, X[ 6],  9, 0xc040b340); // 18
+    operation(G(), c, d, a, b, X[11], 14, 0x265e5a51); // 19
+    operation(G(), b, c, d, a, X[ 0], 20, 0xe9b6c7aa); // 20
+    operation(G(), a, b, c, d, X[ 5],  5, 0xd62f105d); // 21
+    operation(G(), d, a, b, c, X[10],  9,  0x2441453); // 22
+    operation(G(), c, d, a, b, X[15], 14, 0xd8a1e681); // 23
+    operation(G(), b, c, d, a, X[ 4], 20, 0xe7d3fbc8); // 24
+    operation(G(), a, b, c, d, X[ 9],  5, 0x21e1cde6); // 25
+    operation(G(), d, a, b, c, X[14],  9, 0xc33707d6); // 26
+    operation(G(), c, d, a, b, X[ 3], 14, 0xf4d50d87); // 27
+    operation(G(), b, c, d, a, X[ 8], 20, 0x455a14ed); // 28
+    operation(G(), a, b, c, d, X[13],  5, 0xa9e3e905); // 29
+    operation(G(), d, a, b, c, X[ 2],  9, 0xfcefa3f8); // 30
+    operation(G(), c, d, a, b, X[ 7], 14, 0x676f02d9); // 31
+    operation(G(), b, c, d, a, X[12], 20, 0x8d2a4c8a); // 32
+
+    // Round 3
+    operation(H(), a, b, c, d, X[ 5],  4, 0xfffa3942); // 33
+    operation(H(), d, a, b, c, X[ 8], 11, 0x8771f681); // 34
+    operation(H(), c, d, a, b, X[11], 16, 0x6d9d6122); // 35
+    operation(H(), b, c, d, a, X[14], 23, 0xfde5380c); // 36
+    operation(H(), a, b, c, d, X[ 1],  4, 0xa4beea44); // 37
+    operation(H(), d, a, b, c, X[ 4], 11, 0x4bdecfa9); // 38
+    operation(H(), c, d, a, b, X[ 7], 16, 0xf6bb4b60); // 39
+    operation(H(), b, c, d, a, X[10], 23, 0xbebfbc70); // 40
+    operation(H(), a, b, c, d, X[13],  4, 0x289b7ec6); // 41
+    operation(H(), d, a, b, c, X[ 0], 11, 0xeaa127fa); // 42
+    operation(H(), c, d, a, b, X[ 3], 16, 0xd4ef3085); // 43
+    operation(H(), b, c, d, a, X[ 6], 23,  0x4881d05); // 44
+    operation(H(), a, b, c, d, X[ 9],  4, 0xd9d4d039); // 45
+    operation(H(), d, a, b, c, X[12], 11, 0xe6db99e5); // 46
+    operation(H(), c, d, a, b, X[15], 16, 0x1fa27cf8); // 47
+    operation(H(), b, c, d, a, X[ 2], 23, 0xc4ac5665); // 48
+
+    // Round 4
+    operation(I(), a, b, c, d, X[ 0],  6, 0xf4292244); // 49
+    operation(I(), d, a, b, c, X[ 7], 10, 0x432aff97); // 50
+    operation(I(), c, d, a, b, X[14], 15, 0xab9423a7); // 51
+    operation(I(), b, c, d, a, X[ 5], 21, 0xfc93a039); // 52
+    operation(I(), a, b, c, d, X[12],  6, 0x655b59c3); // 53
+    operation(I(), d, a, b, c, X[ 3], 10, 0x8f0ccc92); // 54
+    operation(I(), c, d, a, b, X[10], 15, 0xffeff47d); // 55
+    operation(I(), b, c, d, a, X[ 1], 21, 0x85845dd1); // 56
+    operation(I(), a, b, c, d, X[ 8],  6, 0x6fa87e4f); // 57
+    operation(I(), d, a, b, c, X[15], 10, 0xfe2ce6e0); // 58
+    operation(I(), c, d, a, b, X[ 6], 15, 0xa3014314); // 59
+    operation(I(), b, c, d, a, X[13], 21, 0x4e0811a1); // 60
+    operation(I(), a, b, c, d, X[ 4],  6, 0xf7537e82); // 61
+    operation(I(), d, a, b, c, X[11], 10, 0xbd3af235); // 62
+    operation(I(), c, d, a, b, X[ 2], 15, 0x2ad7d2bb); // 63
+    operation(I(), b, c, d, a, X[ 9], 21, 0xeb86d391); // 64
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+#if defined(SK_MD5_CLEAR_DATA)
+    // Clear sensitive information.
+    if (X == &storage) {
+        memset(storage, 0, sizeof(storage));
+    }
+#endif
+}
+
+static void encode(uint8_t output[16], const uint32_t input[4]) {
+    for (size_t i = 0, j = 0; i < 4; i++, j += 4) {
+        output[j  ] = (uint8_t) (input[i]        & 0xff);
+        output[j+1] = (uint8_t)((input[i] >>  8) & 0xff);
+        output[j+2] = (uint8_t)((input[i] >> 16) & 0xff);
+        output[j+3] = (uint8_t)((input[i] >> 24) & 0xff);
+    }
+}
+
+static void encode(uint8_t output[8], const uint64_t input) {
+    output[0] = (uint8_t) (input        & 0xff);
+    output[1] = (uint8_t)((input >>  8) & 0xff);
+    output[2] = (uint8_t)((input >> 16) & 0xff);
+    output[3] = (uint8_t)((input >> 24) & 0xff);
+    output[4] = (uint8_t)((input >> 32) & 0xff);
+    output[5] = (uint8_t)((input >> 40) & 0xff);
+    output[6] = (uint8_t)((input >> 48) & 0xff);
+    output[7] = (uint8_t)((input >> 56) & 0xff);
+}
+
+static inline bool is_aligned(const void *pointer, size_t byte_count) {
+    return reinterpret_cast<uintptr_t>(pointer) % byte_count == 0;
+}
+
+static const uint32_t* decode(uint32_t storage[16], const uint8_t input[64]) {
+#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_FAST_UNALIGNED_ACCESS)
+   return reinterpret_cast<const uint32_t*>(input);
+#else
+#if defined(SK_CPU_LENDIAN)
+    if (is_aligned(input, 4)) {
+        return reinterpret_cast<const uint32_t*>(input);
+    }
+#endif
+    for (size_t i = 0, j = 0; j < 64; i++, j += 4) {
+        storage[i] =  ((uint32_t)input[j  ])        |
+                     (((uint32_t)input[j+1]) <<  8) |
+                     (((uint32_t)input[j+2]) << 16) |
+                     (((uint32_t)input[j+3]) << 24);
+    }
+    return storage;
+#endif
+}
diff --git a/src/utils/SkMD5.h b/src/utils/SkMD5.h
new file mode 100644
index 0000000..6fbdb46
--- /dev/null
+++ b/src/utils/SkMD5.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMD5_DEFINED
+#define SkMD5_DEFINED
+
+#include "SkTypes.h"
+#include "SkEndian.h"
+#include "SkStream.h"
+
+//The following macros can be defined to affect the MD5 code generated.
+//SK_MD5_CLEAR_DATA causes all intermediate state to be overwritten with 0's.
+//SK_CPU_LENDIAN allows 32 bit <=> 8 bit conversions without copies (if alligned).
+//SK_CPU_FAST_UNALIGNED_ACCESS allows 32 bit <=> 8 bit conversions without copies if SK_CPU_LENDIAN.
+
+class SkMD5 : SkWStream {
+public:
+    SkMD5();
+
+    /** Processes input, adding it to the digest.
+     *  Note that this treats the buffer as a series of uint8_t values.
+     */
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE {
+        update(reinterpret_cast<const uint8_t*>(buffer), size);
+        return true;
+    }
+
+    /** Processes input, adding it to the digest. Calling this after finish is undefined. */
+    void update(const uint8_t* input, size_t length);
+
+    struct Digest {
+        uint8_t data[16];
+    };
+
+    /** Computes and returns the digest. */
+    void finish(Digest& digest);
+
+private:
+    // number of bytes, modulo 2^64
+    uint64_t byteCount;
+
+    // state (ABCD)
+    uint32_t state[4];
+
+    // input buffer
+    uint8_t buffer[64];
+};
+
+#endif
diff --git a/src/utils/SkMatrix44.cpp b/src/utils/SkMatrix44.cpp
index e899cf8..92c8715 100644
--- a/src/utils/SkMatrix44.cpp
+++ b/src/utils/SkMatrix44.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,18 +5,8 @@
  * found in the LICENSE file.
  */
 
-
-
 #include "SkMatrix44.h"
 
-SkMatrix44::SkMatrix44(const SkMatrix44& src) {
-    memcpy(this, &src, sizeof(src));
-}
-
-SkMatrix44::SkMatrix44(const SkMatrix44& a, const SkMatrix44& b) {
-    this->setConcat(a, b);
-}
-
 static inline bool eq4(const SkMScalar* SK_RESTRICT a,
                       const SkMScalar* SK_RESTRICT b) {
     return (a[0] == b[0]) & (a[1] == b[1]) & (a[2] == b[2]) & (a[3] == b[3]);
@@ -371,11 +360,14 @@
     SkMScalar storage[16];
     SkMScalar* result = useStorage ? storage : &fMat[0][0];
 
+    // Both matrices are at most scale+translate
     if (bits_isonly(a_mask | b_mask, kScale_Mask | kTranslate_Mask)) {
-        sk_bzero(result, sizeof(storage));
         result[0] = a.fMat[0][0] * b.fMat[0][0];
+        result[1] = result[2] = result[3] = result[4] = 0;
         result[5] = a.fMat[1][1] * b.fMat[1][1];
+        result[6] = result[7] = result[8] = result[9] = 0;
         result[10] = a.fMat[2][2] * b.fMat[2][2];
+        result[11] = 0;
         result[12] = a.fMat[0][0] * b.fMat[3][0] + a.fMat[3][0];
         result[13] = a.fMat[1][1] * b.fMat[3][1] + a.fMat[3][1];
         result[14] = a.fMat[2][2] * b.fMat[3][2] + a.fMat[3][2];
diff --git a/src/utils/SkMeshUtils.cpp b/src/utils/SkMeshUtils.cpp
index 7970ba1..3857dc9 100644
--- a/src/utils/SkMeshUtils.cpp
+++ b/src/utils/SkMeshUtils.cpp
@@ -100,4 +100,3 @@
                              idx.indices(), idx.indexCount(), p);
     }
 }
-
diff --git a/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp
index d59d559..bfc7a7a 100644
--- a/src/utils/SkNWayCanvas.cpp
+++ b/src/utils/SkNWayCanvas.cpp
@@ -311,5 +311,3 @@
     }
     return this->INHERITED::setDrawFilter(filter);
 }
-
-
diff --git a/src/utils/SkOSFile.cpp b/src/utils/SkOSFile.cpp
index 1fec35a..478a0cc 100644
--- a/src/utils/SkOSFile.cpp
+++ b/src/utils/SkOSFile.cpp
@@ -227,4 +227,3 @@
 }
 
 #endif
-
diff --git a/src/utils/SkParseColor.cpp b/src/utils/SkParseColor.cpp
index db47aae..becad2c 100644
--- a/src/utils/SkParseColor.cpp
+++ b/src/utils/SkParseColor.cpp
@@ -537,4 +537,3 @@
 //  SkASSERT(result == ((0xFF << 24) | (71 << 16) | (162 << 8) | (253 << 0)));
 }
 #endif
-
diff --git a/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp
index 18ea5e0..8c7a4d0 100644
--- a/src/utils/SkParsePath.cpp
+++ b/src/utils/SkParsePath.cpp
@@ -243,4 +243,3 @@
         }
     }
 }
-
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
index 46ca6d6..de4b440 100644
--- a/src/utils/SkPictureUtils.cpp
+++ b/src/utils/SkPictureUtils.cpp
@@ -11,6 +11,7 @@
 #include "SkDevice.h"
 #include "SkPixelRef.h"
 #include "SkShader.h"
+#include "SkRRect.h"
 
 class PixelRefSet {
 public:
@@ -58,7 +59,11 @@
         SkShader* shader = paint.getShader();
         if (shader) {
             SkBitmap bm;
-            if (shader->asABitmap(&bm, NULL, NULL)) {
+            // Check whether the shader is a gradient in order to short-circuit
+            // call to asABitmap to prevent generation of bitmaps from
+            // gradient shaders, which implement asABitmap.
+            if (SkShader::kNone_GradientType == shader->asAGradient(NULL) &&
+                shader->asABitmap(&bm, NULL, NULL)) {
                 fPRSet->add(bm.pixelRef());
             }
         }
@@ -84,7 +89,11 @@
                             const SkPoint[], const SkPaint& paint) SK_OVERRIDE {
         this->addBitmapFromPaint(paint);
     }
-    virtual void drawRect(const SkDraw&, const SkRect& r,
+    virtual void drawRect(const SkDraw&, const SkRect&,
+                          const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawOval(const SkDraw&, const SkRect&,
                           const SkPaint& paint) SK_OVERRIDE {
         this->addBitmapFromPaint(paint);
     }
@@ -173,6 +182,10 @@
                           bool doAA) SK_OVERRIDE {
         return this->INHERITED::clipRect(path.getBounds(), op, false);
     }
+    virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op,
+                           bool doAA) SK_OVERRIDE {
+        return this->INHERITED::clipRect(rrect.getBounds(), op, false);
+    }
 
 private:
     typedef SkCanvas INHERITED;
@@ -210,4 +223,3 @@
     }
     return data;
 }
-
diff --git a/src/utils/SkProxyCanvas.cpp b/src/utils/SkProxyCanvas.cpp
index 4af509d..057f1df 100644
--- a/src/utils/SkProxyCanvas.cpp
+++ b/src/utils/SkProxyCanvas.cpp
@@ -165,4 +165,3 @@
 SkDrawFilter* SkProxyCanvas::setDrawFilter(SkDrawFilter* filter) {
     return fProxy->setDrawFilter(filter);
 }
-
diff --git a/src/utils/SkRTConf.cpp b/src/utils/SkRTConf.cpp
new file mode 100644
index 0000000..c701552
--- /dev/null
+++ b/src/utils/SkRTConf.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRTConf.h"
+#include "SkOSFile.h"
+
+SkRTConfRegistry::SkRTConfRegistry(): fConfs(100) {
+
+    SkFILE *fp = sk_fopen(configFileLocation(), kRead_SkFILE_Flag);
+
+    if (!fp) {
+        return;
+    }
+
+    char line[1024];
+
+    while (!sk_feof(fp)) {
+
+        if (!sk_fgets(line, sizeof(line), fp)) {
+            break;
+        }
+
+        char *commentptr = strchr(line, '#');
+        if (commentptr == line) {
+            continue;
+        }
+        if (NULL != commentptr) {
+            *commentptr = '\0';
+        }
+
+        char sep[] = " \t\r\n";
+
+        char *keyptr = strtok(line, sep);
+        if (!keyptr) {
+            continue;
+        }
+
+        char *valptr = strtok(NULL, sep);
+        if (!valptr) {
+            continue;
+        }
+
+        SkString* key = new SkString(keyptr);
+        SkString* val = new SkString(valptr);
+
+        fConfigFileKeys.append(1, &key);
+        fConfigFileValues.append(1, &val);
+    }
+    sk_fclose(fp);
+}
+
+const char *SkRTConfRegistry::configFileLocation() const {
+    return "skia.conf"; // for now -- should probably do something fancier like home directories or whatever.
+}
+
+// dump all known runtime config options to the file with their default values.
+// to trigger this, make a config file of zero size.
+void SkRTConfRegistry::possiblyDumpFile() const {
+    const char *path = configFileLocation();
+    SkFILE *fp = sk_fopen(path, kRead_SkFILE_Flag);
+    if (!fp) {
+        return;
+    }
+    size_t configFileSize = sk_fgetsize(fp);
+    if (configFileSize == 0) {
+        printAll(path);
+    }
+    sk_fclose(fp);
+}
+
+// Run through every provided configuration option and print a warning if the user hasn't
+// declared a correponding configuration object somewhere.
+void SkRTConfRegistry::validate() const {
+    for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
+        if (fConfs.find(fConfigFileKeys[i]->c_str())) {
+            SkDebugf("WARNING: You have config value %s in your configuration file, but I've never heard of that.\n", fConfigFileKeys[i]->c_str());
+        }
+    }
+}
+
+void SkRTConfRegistry::printAll(const char *fname) const {
+    SkWStream *o;
+
+    if (NULL != fname) {
+        o = new SkFILEWStream(fname);
+    } else {
+        o = new SkDebugWStream();
+    }
+
+    ConfMap::Iter iter(fConfs);
+    SkTDArray<SkRTConfBase *> *confArray;
+
+    while (iter.next(&confArray)) {
+        if (confArray->getAt(0)->isDefault()) {
+            o->writeText("# ");
+        }
+        confArray->getAt(0)->print(o);
+        o->newline();
+    }
+
+    delete o;
+}
+
+void SkRTConfRegistry::printNonDefault(const char *fname) const {
+    SkWStream *o;
+
+    if (NULL != fname) {
+        o = new SkFILEWStream(fname);
+    } else {
+        o = new SkDebugWStream();
+    }
+    ConfMap::Iter iter(fConfs);
+    SkTDArray<SkRTConfBase *> *confArray;
+
+    while (iter.next(&confArray)) {
+        if (!confArray->getAt(0)->isDefault()) {
+            confArray->getAt(0)->print(o);
+            o->newline();
+        }
+    }
+
+    delete o;
+}
+
+// register a configuration variable after its value has been set by the parser.
+// we maintain a vector of these things instead of just a single one because the
+// user might set the value after initialization time and we need to have
+// all the pointers lying around, not just one.
+void SkRTConfRegistry::registerConf(SkRTConfBase *conf) {
+    SkTDArray<SkRTConfBase *> *confArray;
+    if (fConfs.find(conf->getName(), &confArray)) {
+        if (!conf->equals(confArray->getAt(0))) {
+            SkDebugf("WARNING: Skia config \"%s\" was registered more than once in incompatible ways.\n", conf->getName());
+        } else {
+            confArray->append(1, &conf);
+        }
+    } else {
+        confArray = new SkTDArray<SkRTConfBase *>;
+        confArray->append(1, &conf);
+        fConfs.set(conf->getName(),confArray);
+    }
+}
+
+template <typename T> T doParse(const char *s, bool *success ) {
+    SkDebugf("WARNING: Invoked non-specialized doParse function...\n");
+    if (success) {
+        *success = false;
+    }
+    return (T) 0;
+}
+
+template<> bool doParse<bool>(const char *s, bool *success) {
+    if (success) {
+        *success = true;
+    }
+    if (!strcmp(s,"1") || !strcmp(s,"true")) {
+        return true;
+    }
+    if (!strcmp(s,"0") || !strcmp(s,"false")) {
+        return false;
+    }
+    if (success) {
+        *success = false;
+    }
+    return false;
+}
+
+template<> const char * doParse<const char *>(const char * s, bool *success) {
+    if (success) {
+        *success = true;
+    }
+    return s;
+}
+
+template<> int doParse<int>(const char * s, bool *success) {
+    return atoi(s);
+}
+
+template<> unsigned int doParse<unsigned int>(const char * s, bool *success) {
+    return (unsigned int) atoi(s);
+}
+
+template<> float doParse<float>(const char * s, bool *success) {
+    return (float) atof(s);
+}
+
+template<> double doParse<double>(const char * s, bool *success) {
+    return atof(s);
+}
+
+static inline void str_replace(char *s, char search, char replace) {
+    for (char *ptr = s ; *ptr ; ptr++) {
+        if (*ptr == search) {
+            *ptr = replace;
+        }
+    }
+}
+
+template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
+    SkString *str = NULL;
+
+    for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
+        if (fConfigFileKeys[i]->equals(name)) {
+            str = fConfigFileValues[i];
+        }
+    }
+
+    SkString environment_variable("skia.");
+    environment_variable.append(name);
+
+    const char *environment_value = getenv(environment_variable.c_str());
+    if (environment_value) {
+        str->set(environment_value);
+    } else {
+        // apparently my shell doesn't let me have environment variables that
+        // have periods in them, so also let the user substitute underscores.
+        SkString underscore_environment_variable("skia_");
+        char *underscore_name = SkStrDup(name);
+        str_replace(underscore_name,'.','_');
+        underscore_environment_variable.append(underscore_name);
+        sk_free(underscore_name);
+        environment_value = getenv(underscore_environment_variable.c_str());
+        if (environment_value) {
+            str->set(environment_value);
+        }
+    }
+
+    if (!str) {
+        return false;
+    }
+
+    bool success;
+    T new_value = doParse<T>(str->c_str(),&success);
+    if (success) {
+        *value = new_value;
+    } else {
+        SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n", str->c_str(), name);
+    }
+    return success;
+}
+
+// need to explicitly instantiate the parsing function for every config type we might have...
+
+template bool SkRTConfRegistry::parse(const char *name, bool *value);
+template bool SkRTConfRegistry::parse(const char *name, int *value);
+template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
+template bool SkRTConfRegistry::parse(const char *name, float *value);
+template bool SkRTConfRegistry::parse(const char *name, double *value);
+template bool SkRTConfRegistry::parse(const char *name, const char **value);
+
+template <typename T> void SkRTConfRegistry::set(const char *name, T value) {
+
+    SkTDArray<SkRTConfBase *> *confArray;
+    if (!fConfs.find(name, &confArray)) {
+        SkDebugf("WARNING: Attempting to set configuration value \"%s\", but I've never heard of that.\n", name);
+        return;
+    }
+
+    for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
+        // static_cast here is okay because there's only one kind of child class.
+        SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
+
+        if (concrete) {
+            concrete->set(value);
+        }
+    }
+}
+
+template void SkRTConfRegistry::set(const char *name, bool value);
+template void SkRTConfRegistry::set(const char *name, int value);
+template void SkRTConfRegistry::set(const char *name, unsigned int value);
+template void SkRTConfRegistry::set(const char *name, float value);
+template void SkRTConfRegistry::set(const char *name, double value);
+template void SkRTConfRegistry::set(const char *name, char * value);
+
+SkRTConfRegistry &skRTConfRegistry() {
+    static SkRTConfRegistry r;
+    return r;
+}
diff --git a/src/utils/SkSHA1.cpp b/src/utils/SkSHA1.cpp
new file mode 100644
index 0000000..1abac06
--- /dev/null
+++ b/src/utils/SkSHA1.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The following code is based on the description in RFC 3174.
+ * http://www.ietf.org/rfc/rfc3174.txt
+ */
+
+#include "SkTypes.h"
+#include "SkSHA1.h"
+#include <string.h>
+
+/** SHA1 basic transformation. Transforms state based on block. */
+static void transform(uint32_t state[5], const uint8_t block[64]);
+
+/** Encodes input into output (5 big endian 32 bit values). */
+static void encode(uint8_t output[20], const uint32_t input[5]);
+
+/** Encodes input into output (big endian 64 bit value). */
+static void encode(uint8_t output[8], const uint64_t input);
+
+SkSHA1::SkSHA1() : byteCount(0) {
+    // These are magic numbers from the specification. The first four are the same as MD5.
+    this->state[0] = 0x67452301;
+    this->state[1] = 0xefcdab89;
+    this->state[2] = 0x98badcfe;
+    this->state[3] = 0x10325476;
+    this->state[4] = 0xc3d2e1f0;
+}
+
+void SkSHA1::update(const uint8_t* input, size_t inputLength) {
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int bufferAvailable = 64 - bufferIndex;
+
+    unsigned int inputIndex;
+    if (inputLength >= bufferAvailable) {
+        if (bufferIndex) {
+            memcpy(&this->buffer[bufferIndex], input, bufferAvailable);
+            transform(this->state, this->buffer);
+            inputIndex = bufferAvailable;
+        } else {
+            inputIndex = 0;
+        }
+
+        for (; inputIndex + 63 < inputLength; inputIndex += 64) {
+            transform(this->state, &input[inputIndex]);
+        }
+
+        bufferIndex = 0;
+    } else {
+        inputIndex = 0;
+    }
+
+    memcpy(&this->buffer[bufferIndex], &input[inputIndex], inputLength - inputIndex);
+
+    this->byteCount += inputLength;
+}
+
+void SkSHA1::finish(Digest& digest) {
+    // Get the number of bits before padding.
+    uint8_t bits[8];
+    encode(bits, this->byteCount << 3);
+
+    // Pad out to 56 mod 64.
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int paddingLength = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
+    static uint8_t PADDING[64] = {
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    };
+    this->update(PADDING, paddingLength);
+
+    // Append length (length before padding, will cause final update).
+    this->update(bits, 8);
+
+    // Write out digest.
+    encode(digest.data, this->state);
+
+#if defined(SK_SHA1_CLEAR_DATA)
+    // Clear state.
+    memset(this, 0, sizeof(*this));
+#endif
+}
+
+struct F1 { uint32_t operator()(uint32_t B, uint32_t C, uint32_t D) {
+    return (B & C) | ((~B) & D);
+    //return D ^ (B & (C ^ D));
+    //return (B & C) ^ ((~B) & D);
+    //return (B & C) + ((~B) & D);
+    //return _mm_or_ps(_mm_andnot_ps(B, D), _mm_and_ps(B, C)); //SSE2
+    //return vec_sel(D, C, B); //PPC
+}};
+
+struct F2 { uint32_t operator()(uint32_t B, uint32_t C, uint32_t D) {
+    return B ^ C ^ D;
+}};
+
+struct F3 { uint32_t operator()(uint32_t B, uint32_t C, uint32_t D) {
+    return (B & C) | (B & D) | (C & D);
+    //return (B & C) | (D & (B | C));
+    //return (B & C) | (D & (B ^ C));
+    //return (B & C) + (D & (B ^ C));
+    //return (B & C) ^ (B & D) ^ (C & D);
+}};
+
+/** Rotates x left n bits. */
+static inline uint32_t rotate_left(uint32_t x, uint8_t n) {
+    return (x << n) | (x >> (32 - n));
+}
+
+template <typename T>
+static inline void operation(T operation,
+                             uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E,
+                             uint32_t w, uint32_t k) {
+    E += rotate_left(A, 5) + operation(B, C, D) + w + k;
+    B = rotate_left(B, 30);
+}
+
+static void transform(uint32_t state[5], const uint8_t block[64]) {
+    uint32_t A = state[0], B = state[1], C = state[2], D = state[3], E = state[4];
+
+    // Round constants defined in SHA-1.
+    static const uint32_t K[] = {
+        0x5A827999, //sqrt(2) * 2^30
+        0x6ED9EBA1, //sqrt(3) * 2^30
+        0x8F1BBCDC, //sqrt(5) * 2^30
+        0xCA62C1D6, //sqrt(10) * 2^30
+    };
+
+    uint32_t W[80];
+
+    // Initialize the array W.
+    size_t i = 0;
+    for (size_t j = 0; i < 16; ++i, j += 4) {
+        W[i] = (((uint32_t)block[j  ]) << 24) |
+               (((uint32_t)block[j+1]) << 16) |
+               (((uint32_t)block[j+2]) <<  8) |
+               (((uint32_t)block[j+3])      );
+    }
+    for (; i < 80; ++i) {
+       W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
+       //The following is equivelent and speeds up SSE implementations, but slows non-SSE.
+       //W[i] = rotate_left(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32], 2);
+    }
+
+    // Round 1
+    operation(F1(), A, B, C, D, E, W[ 0], K[0]);
+    operation(F1(), E, A, B, C, D, W[ 1], K[0]);
+    operation(F1(), D, E, A, B, C, W[ 2], K[0]);
+    operation(F1(), C, D, E, A, B, W[ 3], K[0]);
+    operation(F1(), B, C, D, E, A, W[ 4], K[0]);
+    operation(F1(), A, B, C, D, E, W[ 5], K[0]);
+    operation(F1(), E, A, B, C, D, W[ 6], K[0]);
+    operation(F1(), D, E, A, B, C, W[ 7], K[0]);
+    operation(F1(), C, D, E, A, B, W[ 8], K[0]);
+    operation(F1(), B, C, D, E, A, W[ 9], K[0]);
+    operation(F1(), A, B, C, D, E, W[10], K[0]);
+    operation(F1(), E, A, B, C, D, W[11], K[0]);
+    operation(F1(), D, E, A, B, C, W[12], K[0]);
+    operation(F1(), C, D, E, A, B, W[13], K[0]);
+    operation(F1(), B, C, D, E, A, W[14], K[0]);
+    operation(F1(), A, B, C, D, E, W[15], K[0]);
+    operation(F1(), E, A, B, C, D, W[16], K[0]);
+    operation(F1(), D, E, A, B, C, W[17], K[0]);
+    operation(F1(), C, D, E, A, B, W[18], K[0]);
+    operation(F1(), B, C, D, E, A, W[19], K[0]);
+
+    // Round 2
+    operation(F2(), A, B, C, D, E, W[20], K[1]);
+    operation(F2(), E, A, B, C, D, W[21], K[1]);
+    operation(F2(), D, E, A, B, C, W[22], K[1]);
+    operation(F2(), C, D, E, A, B, W[23], K[1]);
+    operation(F2(), B, C, D, E, A, W[24], K[1]);
+    operation(F2(), A, B, C, D, E, W[25], K[1]);
+    operation(F2(), E, A, B, C, D, W[26], K[1]);
+    operation(F2(), D, E, A, B, C, W[27], K[1]);
+    operation(F2(), C, D, E, A, B, W[28], K[1]);
+    operation(F2(), B, C, D, E, A, W[29], K[1]);
+    operation(F2(), A, B, C, D, E, W[30], K[1]);
+    operation(F2(), E, A, B, C, D, W[31], K[1]);
+    operation(F2(), D, E, A, B, C, W[32], K[1]);
+    operation(F2(), C, D, E, A, B, W[33], K[1]);
+    operation(F2(), B, C, D, E, A, W[34], K[1]);
+    operation(F2(), A, B, C, D, E, W[35], K[1]);
+    operation(F2(), E, A, B, C, D, W[36], K[1]);
+    operation(F2(), D, E, A, B, C, W[37], K[1]);
+    operation(F2(), C, D, E, A, B, W[38], K[1]);
+    operation(F2(), B, C, D, E, A, W[39], K[1]);
+
+    // Round 3
+    operation(F3(), A, B, C, D, E, W[40], K[2]);
+    operation(F3(), E, A, B, C, D, W[41], K[2]);
+    operation(F3(), D, E, A, B, C, W[42], K[2]);
+    operation(F3(), C, D, E, A, B, W[43], K[2]);
+    operation(F3(), B, C, D, E, A, W[44], K[2]);
+    operation(F3(), A, B, C, D, E, W[45], K[2]);
+    operation(F3(), E, A, B, C, D, W[46], K[2]);
+    operation(F3(), D, E, A, B, C, W[47], K[2]);
+    operation(F3(), C, D, E, A, B, W[48], K[2]);
+    operation(F3(), B, C, D, E, A, W[49], K[2]);
+    operation(F3(), A, B, C, D, E, W[50], K[2]);
+    operation(F3(), E, A, B, C, D, W[51], K[2]);
+    operation(F3(), D, E, A, B, C, W[52], K[2]);
+    operation(F3(), C, D, E, A, B, W[53], K[2]);
+    operation(F3(), B, C, D, E, A, W[54], K[2]);
+    operation(F3(), A, B, C, D, E, W[55], K[2]);
+    operation(F3(), E, A, B, C, D, W[56], K[2]);
+    operation(F3(), D, E, A, B, C, W[57], K[2]);
+    operation(F3(), C, D, E, A, B, W[58], K[2]);
+    operation(F3(), B, C, D, E, A, W[59], K[2]);
+
+    // Round 4
+    operation(F2(), A, B, C, D, E, W[60], K[3]);
+    operation(F2(), E, A, B, C, D, W[61], K[3]);
+    operation(F2(), D, E, A, B, C, W[62], K[3]);
+    operation(F2(), C, D, E, A, B, W[63], K[3]);
+    operation(F2(), B, C, D, E, A, W[64], K[3]);
+    operation(F2(), A, B, C, D, E, W[65], K[3]);
+    operation(F2(), E, A, B, C, D, W[66], K[3]);
+    operation(F2(), D, E, A, B, C, W[67], K[3]);
+    operation(F2(), C, D, E, A, B, W[68], K[3]);
+    operation(F2(), B, C, D, E, A, W[69], K[3]);
+    operation(F2(), A, B, C, D, E, W[70], K[3]);
+    operation(F2(), E, A, B, C, D, W[71], K[3]);
+    operation(F2(), D, E, A, B, C, W[72], K[3]);
+    operation(F2(), C, D, E, A, B, W[73], K[3]);
+    operation(F2(), B, C, D, E, A, W[74], K[3]);
+    operation(F2(), A, B, C, D, E, W[75], K[3]);
+    operation(F2(), E, A, B, C, D, W[76], K[3]);
+    operation(F2(), D, E, A, B, C, W[77], K[3]);
+    operation(F2(), C, D, E, A, B, W[78], K[3]);
+    operation(F2(), B, C, D, E, A, W[79], K[3]);
+
+    state[0] += A;
+    state[1] += B;
+    state[2] += C;
+    state[3] += D;
+    state[4] += E;
+
+#if defined(SK_SHA1_CLEAR_DATA)
+    // Clear sensitive information.
+    memset(W, 0, sizeof(W));
+#endif
+}
+
+static void encode(uint8_t output[20], const uint32_t input[5]) {
+    for (size_t i = 0, j = 0; i < 5; i++, j += 4) {
+        output[j  ] = (uint8_t)((input[i] >> 24) & 0xff);
+        output[j+1] = (uint8_t)((input[i] >> 16) & 0xff);
+        output[j+2] = (uint8_t)((input[i] >>  8) & 0xff);
+        output[j+3] = (uint8_t)((input[i]      ) & 0xff);
+    }
+}
+
+static void encode(uint8_t output[8], const uint64_t input) {
+    output[0] = (uint8_t)((input >> 56) & 0xff);
+    output[1] = (uint8_t)((input >> 48) & 0xff);
+    output[2] = (uint8_t)((input >> 40) & 0xff);
+    output[3] = (uint8_t)((input >> 32) & 0xff);
+    output[4] = (uint8_t)((input >> 24) & 0xff);
+    output[5] = (uint8_t)((input >> 16) & 0xff);
+    output[6] = (uint8_t)((input >>  8) & 0xff);
+    output[7] = (uint8_t)((input      ) & 0xff);
+}
diff --git a/src/utils/SkSHA1.h b/src/utils/SkSHA1.h
new file mode 100644
index 0000000..10bbc43
--- /dev/null
+++ b/src/utils/SkSHA1.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSHA1_DEFINED
+#define SkSHA1_DEFINED
+
+#include "SkTypes.h"
+#include "SkEndian.h"
+#include "SkStream.h"
+
+//The following macros can be defined to affect the SHA1 code generated.
+//SK_SHA1_CLEAR_DATA causes all intermediate state to be overwritten with 0's.
+//SK_CPU_BENDIAN allows 32 bit <=> 8 bit conversions without copies (if alligned).
+//SK_CPU_FAST_UNALIGNED_ACCESS allows 32 bit <=> 8 bit conversions without copies if SK_CPU_BENDIAN.
+
+class SkSHA1 : SkWStream {
+public:
+    SkSHA1();
+
+    /** Processes input, adding it to the digest.
+     *  Note that this treats the buffer as a series of uint8_t values.
+     */
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE {
+        update(reinterpret_cast<const uint8_t*>(buffer), size);
+        return true;
+    }
+
+    /** Processes input, adding it to the digest. Calling this after finish is undefined. */
+    void update(const uint8_t* input, size_t length);
+
+    struct Digest {
+        uint8_t data[20];
+    };
+
+    /** Computes and returns the digest. */
+    void finish(Digest& digest);
+
+private:
+    // number of bytes, modulo 2^64
+    uint64_t byteCount;
+
+    // state (ABCDE)
+    uint32_t state[5];
+
+    // input buffer
+    uint8_t buffer[64];
+};
+
+#endif
diff --git a/src/utils/mac/SkCreateCGImageRef.cpp b/src/utils/mac/SkCreateCGImageRef.cpp
index abad8cb..e9e1107 100644
--- a/src/utils/mac/SkCreateCGImageRef.cpp
+++ b/src/utils/mac/SkCreateCGImageRef.cpp
@@ -238,4 +238,3 @@
     output->swap(bitmap);
     return true;
 }
-
diff --git a/src/utils/mac/SkStream_mac.cpp b/src/utils/mac/SkStream_mac.cpp
index 5b3fe6b..afb87fb 100644
--- a/src/utils/mac/SkStream_mac.cpp
+++ b/src/utils/mac/SkStream_mac.cpp
@@ -55,5 +55,3 @@
     rec.releaseInfo = release_info_proc;
     return CGDataProviderCreateSequential(stream, &rec);
 }
-
-
diff --git a/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp
index ffc410a..c10fe3e 100644
--- a/src/views/SkBGViewArtist.cpp
+++ b/src/views/SkBGViewArtist.cpp
@@ -28,4 +28,3 @@
 {
     SkPaint_Inflate(&fPaint, dom, node);
 }
-
diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp
index 1513170..52a0c4d 100644
--- a/src/views/SkEvent.cpp
+++ b/src/views/SkEvent.cpp
@@ -506,4 +506,3 @@
         evt = next;
     }
 }
-
diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
index 7a305f2..344da0b 100644
--- a/src/views/SkParsePaint.cpp
+++ b/src/views/SkParsePaint.cpp
@@ -107,4 +107,3 @@
     if (shader)
         paint->setShader(shader)->unref();
 }
-
diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp
index edb78bd..03e28eb 100644
--- a/src/views/SkProgressView.cpp
+++ b/src/views/SkProgressView.cpp
@@ -130,4 +130,3 @@
         fOffShader = inflate_shader(s);
     (void)dom.findBool(node, "do-interp", &fDoInterp);
 }
-
diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp
index e4fbb80..9a3a352 100644
--- a/src/views/SkStackViewLayout.cpp
+++ b/src/views/SkStackViewLayout.cpp
@@ -271,4 +271,3 @@
     this->INHERITED::onInflate(dom, node);
     (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
 }
-
diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp
index e1b1662..8ede870 100644
--- a/src/views/SkTagList.cpp
+++ b/src/views/SkTagList.cpp
@@ -60,4 +60,3 @@
         rec = next;
     }
 }
-
diff --git a/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp
index 78828b6..b5dab09 100644
--- a/src/views/SkTextBox.cpp
+++ b/src/views/SkTextBox.cpp
@@ -257,4 +257,3 @@
     SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
     return this->countLines() * spacing;
 }
-
diff --git a/src/views/SkTouchGesture.cpp b/src/views/SkTouchGesture.cpp
index afda8c1..3becccd 100644
--- a/src/views/SkTouchGesture.cpp
+++ b/src/views/SkTouchGesture.cpp
@@ -325,5 +325,3 @@
     fLastUpP.set(x, y);
     return found;
 }
-
-
diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp
index 94eb490..0c6c0de 100644
--- a/src/views/SkView.cpp
+++ b/src/views/SkView.cpp
@@ -372,13 +372,12 @@
     }
 }
 
-SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
-{
+SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y, unsigned modi) {
     if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
         return NULL;
     }
 
-    if (this->onSendClickToChildren(x, y)) {
+    if (this->onSendClickToChildren(x, y, modi)) {
         F2BIter    iter(this);
         SkView*    child;
 
@@ -389,7 +388,7 @@
                 continue;
             }
 
-            Click* click = child->findClickHandler(p.fX, p.fY);
+            Click* click = child->findClickHandler(p.fX, p.fY, modi);
 
             if (click) {
                 return click;
@@ -397,10 +396,10 @@
         }
     }
 
-    return this->onFindClickHandler(x, y);
+    return this->onFindClickHandler(x, y, modi);
 }
 
-void SkView::DoClickDown(Click* click, int x, int y)
+void SkView::DoClickDown(Click* click, int x, int y, unsigned modi)
 {
     SkASSERT(click);
 
@@ -420,10 +419,11 @@
     click->fPrev = click->fCurr = click->fOrig;
 
     click->fState = Click::kDown_State;
+    click->fModifierKeys = modi;
     target->onClick(click);
 }
 
-void SkView::DoClickMoved(Click* click, int x, int y)
+void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi)
 {
     SkASSERT(click);
 
@@ -443,10 +443,11 @@
     }
 
     click->fState = Click::kMoved_State;
+    click->fModifierKeys = modi;
     target->onClick(click);
 }
 
-void SkView::DoClickUp(Click* click, int x, int y)
+void SkView::DoClickUp(Click* click, int x, int y, unsigned modi)
 {
     SkASSERT(click);
 
@@ -466,6 +467,7 @@
     }
 
     click->fState = Click::kUp_State;
+    click->fModifierKeys = modi;
     target->onClick(click);
 }
 
@@ -489,11 +491,11 @@
 
 void SkView::onSizeChange() {}
 
-bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
+bool SkView::onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi) {
     return true;
 }
 
-SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) {
     return NULL;
 }
 
diff --git a/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp
index 4d7de04..59cc481 100644
--- a/src/views/SkViewInflate.cpp
+++ b/src/views/SkViewInflate.cpp
@@ -143,4 +143,3 @@
         SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
 }
 #endif
-
diff --git a/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp
index ba40c1e..0348fd7 100644
--- a/src/views/SkViewPriv.cpp
+++ b/src/views/SkViewPriv.cpp
@@ -101,4 +101,3 @@
 
     return obj;
 }
-
diff --git a/src/views/SkViewPriv.h b/src/views/SkViewPriv.h
index 8a9c0b7..487e379 100644
--- a/src/views/SkViewPriv.h
+++ b/src/views/SkViewPriv.h
@@ -42,4 +42,3 @@
 };
 
 #endif
-
diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp
index 1693a31..202bf52 100644
--- a/src/views/SkWidgets.cpp
+++ b/src/views/SkWidgets.cpp
@@ -558,4 +558,3 @@
 }
 
 #endif
-
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
index b02631d..7331976 100644
--- a/src/views/SkWindow.cpp
+++ b/src/views/SkWindow.cpp
@@ -358,12 +358,13 @@
     return false;
 }
 
-bool SkWindow::handleClick(int x, int y, Click::State state, void *owner) {
-    return this->onDispatchClick(x, y, state, owner);
+bool SkWindow::handleClick(int x, int y, Click::State state, void *owner,
+                           unsigned modifierKeys) {
+    return this->onDispatchClick(x, y, state, owner, modifierKeys);
 }
 
 bool SkWindow::onDispatchClick(int x, int y, Click::State state,
-        void* owner) {
+                               void* owner, unsigned modifierKeys) {
     bool handled = false;
 
     // First, attempt to find an existing click with this owner.
@@ -382,25 +383,25 @@
                 fClicks.remove(index);
             }
             Click* click = this->findClickHandler(SkIntToScalar(x),
-                    SkIntToScalar(y));
+                                                  SkIntToScalar(y), modifierKeys);
 
             if (click) {
                 click->fOwner = owner;
                 *fClicks.append() = click;
-                SkView::DoClickDown(click, x, y);
+                SkView::DoClickDown(click, x, y, modifierKeys);
                 handled = true;
             }
             break;
         }
         case Click::kMoved_State:
             if (index != -1) {
-                SkView::DoClickMoved(fClicks[index], x, y);
+                SkView::DoClickMoved(fClicks[index], x, y, modifierKeys);
                 handled = true;
             }
             break;
         case Click::kUp_State:
             if (index != -1) {
-                SkView::DoClickUp(fClicks[index], x, y);
+                SkView::DoClickUp(fClicks[index], x, y, modifierKeys);
                 delete fClicks[index];
                 fClicks.remove(index);
                 handled = true;
@@ -412,4 +413,3 @@
     }
     return handled;
 }
-
diff --git a/src/views/animated/SkImageView.cpp b/src/views/animated/SkImageView.cpp
index a75aa73..2956729 100644
--- a/src/views/animated/SkImageView.cpp
+++ b/src/views/animated/SkImageView.cpp
@@ -300,4 +300,3 @@
     }
     return true;
 }
-
diff --git a/src/views/animated/SkScrollBarView.cpp b/src/views/animated/SkScrollBarView.cpp
index 0d93775..80ee49f 100644
--- a/src/views/animated/SkScrollBarView.cpp
+++ b/src/views/animated/SkScrollBarView.cpp
@@ -143,4 +143,3 @@
 //    e.setS32("hideBar", hideBar);
     fAnim.doUserEvent(e);
 }
-
diff --git a/src/views/animated/SkStaticTextView.cpp b/src/views/animated/SkStaticTextView.cpp
index 199b2fe..0e4cad6 100644
--- a/src/views/animated/SkStaticTextView.cpp
+++ b/src/views/animated/SkStaticTextView.cpp
@@ -189,4 +189,3 @@
     }
 }
 }
-
diff --git a/src/views/ios/SkOSWindow_iOS.mm b/src/views/ios/SkOSWindow_iOS.mm
index 6fac765..94579aa 100755
--- a/src/views/ios/SkOSWindow_iOS.mm
+++ b/src/views/ios/SkOSWindow_iOS.mm
@@ -38,10 +38,6 @@
     return this->INHERITED::onEvent(evt);
 }
 
-bool SkOSWindow::onDispatchClick(int x, int y, Click::State state, void* owner) {
-    return this->INHERITED::onDispatchClick(x, y, state, owner);
-}
-
 void SkOSWindow::onSetTitle(const char title[]) {
     [(SkUIView*)fHWND setSkTitle:title];
 }
diff --git a/src/views/mac/SkNSView.mm b/src/views/mac/SkNSView.mm
index ffa9d7c..fc82ac4 100644
--- a/src/views/mac/SkNSView.mm
+++ b/src/views/mac/SkNSView.mm
@@ -192,35 +192,68 @@
  //     unichar c = [[event characters] characterAtIndex:0];
 }
 
+static const struct {
+    unsigned    fNSModifierMask;
+    unsigned    fSkModifierMask;
+} gModifierMasks[] = {
+    { NSAlphaShiftKeyMask,  kShift_SkModifierKey },
+    { NSShiftKeyMask,       kShift_SkModifierKey },
+    { NSControlKeyMask,     kControl_SkModifierKey },
+    { NSAlternateKeyMask,   kOption_SkModifierKey },
+    { NSCommandKeyMask,     kCommand_SkModifierKey },
+};
+
+static unsigned convertNSModifiersToSk(NSUInteger nsModi) {
+    unsigned skModi = 0;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gModifierMasks); ++i) {
+        if (nsModi & gModifierMasks[i].fNSModifierMask) {
+            skModi |= gModifierMasks[i].fSkModifierMask;
+        }
+    }
+    return skModi;
+}
+
 - (void)mouseDown:(NSEvent *)event {
     NSPoint p = [event locationInWindow];
+    unsigned modi = convertNSModifiersToSk([event modifierFlags]);
+
     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
         NSPoint loc = [self convertPoint:p fromView:nil];
-        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kDown_State, self);
+        fWind->handleClick((int) loc.x, (int) loc.y,
+                           SkView::Click::kDown_State, self, modi);
     }
 }
 
 - (void)mouseDragged:(NSEvent *)event {
     NSPoint p = [event locationInWindow];
+    unsigned modi = convertNSModifiersToSk([event modifierFlags]);
+
     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
         NSPoint loc = [self convertPoint:p fromView:nil];
-        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kMoved_State, self);
+        fWind->handleClick((int) loc.x, (int) loc.y,
+                           SkView::Click::kMoved_State, self, modi);
     }
 }
 
 - (void)mouseMoved:(NSEvent *)event {
     NSPoint p = [event locationInWindow];
+    unsigned modi = convertNSModifiersToSk([event modifierFlags]);
+    
     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
         NSPoint loc = [self convertPoint:p fromView:nil];
-        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kMoved_State, self);
+        fWind->handleClick((int) loc.x, (int) loc.y,
+                           SkView::Click::kMoved_State, self, modi);
     }
 }
 
 - (void)mouseUp:(NSEvent *)event {
     NSPoint p = [event locationInWindow];
+    unsigned modi = convertNSModifiersToSk([event modifierFlags]);
+    
     if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
         NSPoint loc = [self convertPoint:p fromView:nil];
-        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kUp_State, self);
+        fWind->handleClick((int) loc.x, (int) loc.y,
+                           SkView::Click::kUp_State, self, modi);
     }
 }
 
diff --git a/src/views/mac/SkOSWindow_Mac.mm b/src/views/mac/SkOSWindow_Mac.mm
index a288ae3..01c8677 100644
--- a/src/views/mac/SkOSWindow_Mac.mm
+++ b/src/views/mac/SkOSWindow_Mac.mm
@@ -48,8 +48,9 @@
     return this->INHERITED::onEvent(evt);
 }
 
-bool SkOSWindow::onDispatchClick(int x, int y, Click::State state, void* owner) {
-    return this->INHERITED::onDispatchClick(x, y, state, owner);
+bool SkOSWindow::onDispatchClick(int x, int y, Click::State state, void* owner,
+                                 unsigned modi) {
+    return this->INHERITED::onDispatchClick(x, y, state, owner, modi);
 }
 
 void SkOSWindow::onSetTitle(const char title[]) {
diff --git a/src/views/mac/SkOptionsTableView.mm b/src/views/mac/SkOptionsTableView.mm
index 302e66e..eaa3e4c 100644
--- a/src/views/mac/SkOptionsTableView.mm
+++ b/src/views/mac/SkOptionsTableView.mm
@@ -55,10 +55,15 @@
     }
 }
 
-- (void)updateMenu:(SkOSMenu*)menu {
+- (void)updateMenu:(const SkOSMenu*)menu {
     // the first menu is always assumed to be the static, the second is 
     // repopulated every time over and over again 
-    int menuIndex = fMenus->find(menu);
+
+    // seems pretty weird that we have to get rid of the const'ness here,
+    // but trying to propagate the const'ness through all the way to the fMenus
+    // vector was a non-starter.
+
+    int menuIndex = fMenus->find(const_cast<SkOSMenu *>(menu));
     if (menuIndex >= 0 && menuIndex < fMenus->count()) {
         NSUInteger first = 0;
         for (NSInteger i = 0; i < menuIndex; ++i) {
diff --git a/src/views/sdl/SkOSWindow_SDL.cpp b/src/views/sdl/SkOSWindow_SDL.cpp
index 4edc5ac..2a1fae2 100644
--- a/src/views/sdl/SkOSWindow_SDL.cpp
+++ b/src/views/sdl/SkOSWindow_SDL.cpp
@@ -224,4 +224,3 @@
         SDL_SetTimer(delay, timer_callback);
     }
 }
-
diff --git a/src/views/unix/SkOSWindow_Unix.cpp b/src/views/unix/SkOSWindow_Unix.cpp
index 46a2b3d..7da04bd 100644
--- a/src/views/unix/SkOSWindow_Unix.cpp
+++ b/src/views/unix/SkOSWindow_Unix.cpp
@@ -150,6 +150,26 @@
     XFlush(fUnixWindow.fDisplay);
 }
 
+static unsigned getModi(const XEvent& evt) {
+    static const struct {
+        unsigned    fXMask;
+        unsigned    fSkMask;
+    } gModi[] = {
+        // X values found by experiment. Is there a better way?
+        { 1,    kShift_SkModifierKey },
+        { 4,    kControl_SkModifierKey },
+        { 8,    kOption_SkModifierKey },
+    };
+
+    unsigned modi = 0;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gModi); ++i) {
+        if (evt.xkey.state & gModi[i].fXMask) {
+            modi |= gModi[i].fSkMask;
+        }
+    }
+    return modi;
+}
+
 void SkOSWindow::loop() {
     Display* dsp = fUnixWindow.fDisplay;
     if (NULL == dsp) {
@@ -171,14 +191,17 @@
                 break;
             case ButtonPress:
                 if (evt.xbutton.button == Button1)
-                    this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kDown_State);
+                    this->handleClick(evt.xbutton.x, evt.xbutton.y,
+                                SkView::Click::kDown_State, NULL, getModi(evt));
                 break;
             case ButtonRelease:
                 if (evt.xbutton.button == Button1)
-                    this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kUp_State);
+                    this->handleClick(evt.xbutton.x, evt.xbutton.y,
+                                  SkView::Click::kUp_State, NULL, getModi(evt));
                 break;
             case MotionNotify:
-                this->handleClick(evt.xmotion.x, evt.xmotion.y, SkView::Click::kMoved_State);
+                this->handleClick(evt.xmotion.x, evt.xmotion.y,
+                               SkView::Click::kMoved_State, NULL, getModi(evt));
                 break;
             case KeyPress: {
                 KeySym keysym = XkbKeycodeToKeysym(dsp, evt.xkey.keycode, 0, 0);
diff --git a/src/views/win/SkOSWindow_win.cpp b/src/views/win/SkOSWindow_win.cpp
index 150be6a..f2d6311 100644
--- a/src/views/win/SkOSWindow_win.cpp
+++ b/src/views/win/SkOSWindow_win.cpp
@@ -103,6 +103,10 @@
     return kNONE_SkKey;
 }
 
+static unsigned getModifiers(UINT message) {
+    return 0;   // TODO
+}
+
 bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
     switch (message) {
         case WM_KEYDOWN: {
@@ -146,15 +150,18 @@
         } break;
 
         case WM_LBUTTONDOWN:
-            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kDown_State);
+            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
+                              Click::kDown_State, NULL, getModifiers(message));
             return true;
 
         case WM_MOUSEMOVE:
-            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kMoved_State);
+            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
+                              Click::kMoved_State, NULL, getModifiers(message));
             return true;
 
         case WM_LBUTTONUP:
-            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kUp_State);
+            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
+                              Click::kUp_State, NULL, getModifiers(message));
             return true;
 
         case WM_EVENT_CALLBACK:
@@ -531,7 +538,7 @@
         if (false == bResult) {
             return false;
         }
-        const GrGLInterface* intf = GrGLCreateANGLEInterface();
+        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
 
         if (intf) {
             ANGLE_GL_CALL(intf, ClearStencil(0));
@@ -541,7 +548,7 @@
         }
     }
     if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
-        const GrGLInterface* intf = GrGLCreateANGLEInterface();
+        SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
 
         if (intf ) {
             ANGLE_GL_CALL(intf, Viewport(0, 0, SkScalarRound(this->width()),
@@ -566,7 +573,7 @@
 }
 
 void SkOSWindow::presentANGLE() {
-    const GrGLInterface* intf = GrGLCreateANGLEInterface();
+    SkAutoTUnref<const GrGLInterface> intf(GrGLCreateANGLEInterface());
 
     if (intf) {
         ANGLE_GL_CALL(intf, Flush());
diff --git a/src/xml/SkBML_XMLParser.cpp b/src/xml/SkBML_XMLParser.cpp
index dbdfe4b..c0a5af5 100644
--- a/src/xml/SkBML_XMLParser.cpp
+++ b/src/xml/SkBML_XMLParser.cpp
@@ -15,7 +15,7 @@
 static uint8_t rbyte(SkStream& s)
 {
     uint8_t b;
-    size_t size = s.read(&b, 1);
+    SkDEBUGCODE(size_t size = ) s.read(&b, 1);
     SkASSERT(size == 1);
     return b;
 }
@@ -179,6 +179,3 @@
     SkXMLParserWriter writer(&output);
     Read(s, writer);
 }
-
-
-
diff --git a/src/xml/SkDOM.cpp b/src/xml/SkDOM.cpp
index bdfdd86..9854608 100644
--- a/src/xml/SkDOM.cpp
+++ b/src/xml/SkDOM.cpp
@@ -501,4 +501,3 @@
 }
 
 #endif
-
diff --git a/src/xml/SkJS.cpp b/src/xml/SkJS.cpp
index f2e7a83..8167c9c 100644
--- a/src/xml/SkJS.cpp
+++ b/src/xml/SkJS.cpp
@@ -226,4 +226,3 @@
     SkASSERT(success);
 }
 #endifASSERT(success);
-
diff --git a/src/xml/SkXMLPullParser.cpp b/src/xml/SkXMLPullParser.cpp
index 4080aeb..ed04228 100644
--- a/src/xml/SkXMLPullParser.cpp
+++ b/src/xml/SkXMLPullParser.cpp
@@ -136,4 +136,3 @@
     // TODO: std 5 entities here
     return false;
 }
-
diff --git a/src/xml/SkXMLWriter.cpp b/src/xml/SkXMLWriter.cpp
index 451d1d5..2ff47ea 100644
--- a/src/xml/SkXMLWriter.cpp
+++ b/src/xml/SkXMLWriter.cpp
@@ -331,4 +331,3 @@
 }
 
 #endif
-
diff --git a/tests/AtomicTest.cpp b/tests/AtomicTest.cpp
index 5275e84..a9ab8d2 100644
--- a/tests/AtomicTest.cpp
+++ b/tests/AtomicTest.cpp
@@ -58,4 +58,3 @@
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("AtomicAdd", AtomicAddTestClass, test_atomicAddTests)
-
diff --git a/tests/ClipCacheTest.cpp b/tests/ClipCacheTest.cpp
index 1aeebb8..c80ba63 100644
--- a/tests/ClipCacheTest.cpp
+++ b/tests/ClipCacheTest.cpp
@@ -155,14 +155,14 @@
 
     // check that the set took
     check_state(reporter, cache, clip1, texture1, bound1);
-    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
 
     // push the state
     cache.push();
 
     // verify that the pushed state is initially empty
     check_state(reporter, cache, emptyClip, NULL, emptyBound);
-    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
 
     // modify the new state
     GrIRect bound2;
@@ -180,8 +180,8 @@
 
     // check that the changes took
     check_state(reporter, cache, clip2, texture2, bound2);
-    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
 
     // check to make sure canReuse works
     REPORTER_ASSERT(reporter, cache.canReuse(clip2.getTopmostGenID(), bound2));
@@ -192,16 +192,16 @@
 
     // verify that the old state is restored
     check_state(reporter, cache, clip1, texture1, bound1);
-    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
 
     // manually clear the state
     cache.reset();
 
     // verify it is now empty
     check_state(reporter, cache, emptyClip, NULL, emptyBound);
-    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
 
     // pop again - so there is no state
     cache.pop();
@@ -211,8 +211,8 @@
     // only do in release since it generates asserts in debug
     check_state(reporter, cache, emptyClip, NULL, emptyBound);
 #endif
-    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index d4391d7..4709d22 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -758,7 +758,7 @@
 }
 
 // This can assist with debugging the clip stack reduction code when the test below fails.
-static void print_clip(const SkClipStack::Element& element) {
+static inline void print_clip(const SkClipStack::Element& element) {
     static const char* kOpStrs[] = {
         "DF",
         "IS",
diff --git a/tests/ColorTest.cpp b/tests/ColorTest.cpp
index a038a06..8985dbd 100644
--- a/tests/ColorTest.cpp
+++ b/tests/ColorTest.cpp
@@ -16,7 +16,7 @@
 #define GetPackedG16As32(packed)    (SkGetPackedG16(dc) << (8 - SK_G16_BITS))
 #define GetPackedB16As32(packed)    (SkGetPackedB16(dc) << (8 - SK_B16_BITS))
 
-static bool S32A_D565_Blend_0(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+static inline bool S32A_D565_Blend_0(SkPMColor sc, uint16_t dc, U8CPU alpha) {
     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);
@@ -30,7 +30,7 @@
     return false;
 }
 
-static bool S32A_D565_Blend_01(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+static inline bool S32A_D565_Blend_01(SkPMColor sc, uint16_t dc, U8CPU alpha) {
     unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
     unsigned dr = SkMulS16(SkGetPackedR32(sc), alpha) + SkMulS16(SkGetPackedR16(dc) << 3, dst_scale);
     unsigned dg = SkMulS16(SkGetPackedG32(sc), alpha) + SkMulS16(SkGetPackedG16(dc) << 2, dst_scale);
@@ -44,7 +44,7 @@
     return false;
 }
 
-static bool S32A_D565_Blend_02(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+static inline bool S32A_D565_Blend_02(SkPMColor sc, uint16_t dc, U8CPU alpha) {
     unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
     unsigned dr = SkMulS16(SkGetPackedR32(sc), alpha) + SkMulS16(GetPackedR16As32(dc), dst_scale);
     unsigned dg = SkMulS16(SkGetPackedG32(sc), alpha) + SkMulS16(GetPackedG16As32(dc), dst_scale);
@@ -62,7 +62,7 @@
     return false;
 }
 
-static bool S32A_D565_Blend_1(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+static inline bool S32A_D565_Blend_1(SkPMColor sc, uint16_t dc, U8CPU alpha) {
     unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
     unsigned dr = (SkMulS16(SkGetPackedR32(sc), alpha) >> 3) + SkMulS16(SkGetPackedR16(dc), dst_scale);
     unsigned dg = (SkMulS16(SkGetPackedG32(sc), alpha) >> 2) + SkMulS16(SkGetPackedG16(dc), dst_scale);
@@ -76,11 +76,11 @@
     return false;
 }
 
-static int SkDiv65025Round(int x) {
+static inline int SkDiv65025Round(int x) {
     return (x + 65025/2) / 65025;
 //    return x / 65025;
 }
-static bool S32A_D565_Blend_2(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+static inline bool S32A_D565_Blend_2(SkPMColor sc, uint16_t dc, U8CPU alpha) {
     unsigned dst_scale = 255*255 - SkGetPackedA32(sc) * alpha;
     alpha *= 255;
     unsigned dr = (SkGetPackedR32(sc) >> 3) * alpha + SkGetPackedR16(dc) * dst_scale;
@@ -95,7 +95,7 @@
     return false;
 }
 
-static void test_565blend(skiatest::Reporter* reporter) {
+static inline void test_565blend(skiatest::Reporter* reporter) {
     int total_failures = 0;
     for (int global_alpha = 0; global_alpha <= 255; ++global_alpha) {
         int failures = 0;
@@ -118,7 +118,7 @@
     SkDebugf("total failures %d\n", total_failures);
 }
 
-static void test_premul(skiatest::Reporter* reporter) {
+static inline void test_premul(skiatest::Reporter* reporter) {
     for (int a = 0; a <= 255; a++) {
         for (int x = 0; x <= 255; x++) {
             SkColor c0 = SkColorSetARGB(a, x, x, x);
@@ -162,7 +162,7 @@
 }
 */
 
-static void test_fast_interp(skiatest::Reporter* reporter) {
+static inline void test_fast_interp(skiatest::Reporter* reporter) {
     SkRandom r;
 
     U8CPU a0 = 0;
diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp
index d6ba775..8c002c8 100644
--- a/tests/DataRefTest.cpp
+++ b/tests/DataRefTest.cpp
@@ -54,8 +54,8 @@
     SkDataSet::Iter iter(ds);
     int index = 0;
     for (; !iter.done(); iter.next()) {
-        const char* name = iter.key();
-        SkData* data = iter.value();
+//        const char* name = iter.key();
+//        SkData* data = iter.value();
 //        SkDebugf("[%d] %s:%s\n", index, name, (const char*)data->bytes());
         index += 1;
     }
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
index 1e7725d..8a834be 100644
--- a/tests/DeferredCanvasTest.cpp
+++ b/tests/DeferredCanvasTest.cpp
@@ -400,7 +400,7 @@
         }
     }
     // All cached resources should be evictable since last canvas call was flush()
-    canvas.freeMemoryIfPossible(~0);
+    canvas.freeMemoryIfPossible(~0U);
     REPORTER_ASSERT(reporter, 0 == canvas.storageAllocatedForRecording());
 }
 
diff --git a/tests/DrawBitmapRectTest.cpp b/tests/DrawBitmapRectTest.cpp
index f74199e..a56396c 100644
--- a/tests/DrawBitmapRectTest.cpp
+++ b/tests/DrawBitmapRectTest.cpp
@@ -9,8 +9,94 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkShader.h"
+#include "SkRandom.h"
+#include "SkMatrixUtils.h"
 
-#ifdef SK_SCALAR_IS_FLOAT
+static void rand_matrix(SkMatrix* mat, SkRandom& rand, unsigned mask) {
+    mat->setIdentity();
+    if (mask & SkMatrix::kTranslate_Mask) {
+        mat->postTranslate(rand.nextSScalar1(), rand.nextSScalar1());
+    }
+    if (mask & SkMatrix::kScale_Mask) {
+        mat->postScale(rand.nextSScalar1(), rand.nextSScalar1());
+    }
+    if (mask & SkMatrix::kAffine_Mask) {
+        mat->postRotate(rand.nextSScalar1() * 360);
+    }
+    if (mask & SkMatrix::kPerspective_Mask) {
+        mat->setPerspX(rand.nextSScalar1());
+        mat->setPerspY(rand.nextSScalar1());
+    }
+}
+
+static void rand_size(SkISize* size, SkRandom& rand) {
+    size->set(rand.nextU() & 0xFFFF, rand.nextU() & 0xFFFF);
+}
+
+static bool treat_as_sprite(const SkMatrix& mat, const SkISize& size,
+                            unsigned bits) {
+    return SkTreatAsSprite(mat, size.width(), size.height(), bits);
+}
+
+static void test_treatAsSprite(skiatest::Reporter* reporter) {
+    const unsigned bilerBits = kSkSubPixelBitsForBilerp;
+
+    SkMatrix mat;
+    SkISize  size;
+    SkRandom rand;
+
+    // assert: translate-only no-filter can always be treated as sprite
+    for (int i = 0; i < 1000; ++i) {
+        rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask);
+        for (int j = 0; j < 1000; ++j) {
+            rand_size(&size, rand);
+            REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, 0));
+        }
+    }
+
+    // assert: rotate/perspect is never treated as sprite
+    for (int i = 0; i < 1000; ++i) {
+        rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask);
+        for (int j = 0; j < 1000; ++j) {
+            rand_size(&size, rand);
+            REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, 0));
+            REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+        }
+    }
+
+    size.set(500, 600);
+
+    const SkScalar tooMuchSubpixel = SkFloatToScalar(100.1f);
+    mat.setTranslate(tooMuchSubpixel, 0);
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+    mat.setTranslate(0, tooMuchSubpixel);
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar tinySubPixel = SkFloatToScalar(100.02f);
+    mat.setTranslate(tinySubPixel, 0);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+    mat.setTranslate(0, tinySubPixel);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar twoThirds = SK_Scalar1 * 2 / 3;
+    const SkScalar bigScale = SkScalarDiv(size.width() + twoThirds, size.width());
+    mat.setScale(bigScale, bigScale);
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, false));
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar oneThird = SK_Scalar1 / 3;
+    const SkScalar smallScale = SkScalarDiv(size.width() + oneThird, size.width());
+    mat.setScale(smallScale, smallScale);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false));
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar oneFortyth = SK_Scalar1 / 40;
+    const SkScalar tinyScale = SkScalarDiv(size.width() + oneFortyth, size.width());
+    mat.setScale(tinyScale, tinyScale);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false));
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+}
+
 static void assert_ifDrawnTo(skiatest::Reporter* reporter,
                              const SkBitmap& bm, bool shouldBeDrawn) {
     for (int y = 0; y < bm.height(); ++y) {
@@ -74,7 +160,6 @@
 
     assert_ifDrawnTo(reporter, dev, shouldBeDrawn);
 }
-#endif
 
 /*
  *  Original bug was asserting that the matrix-proc had generated a (Y) value
@@ -181,6 +266,8 @@
 
     test_nan_antihair(reporter);
     test_giantrepeat_crbug118018(reporter);
+
+    test_treatAsSprite(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/DrawPathTest.cpp b/tests/DrawPathTest.cpp
index e8cfa54..3a238db 100644
--- a/tests/DrawPathTest.cpp
+++ b/tests/DrawPathTest.cpp
@@ -46,6 +46,9 @@
 //
 // http://code.google.com/p/chromium/issues/detail?id=131181
 //
+
+// we're not calling this test anymore; is that for a reason?
+
 static void test_crbug131181(skiatest::Reporter*) {
     /*
      fX = 18.8943768,
@@ -257,7 +260,7 @@
 
     SkPath filteredPath;
     SkStrokeRec rec(paint);
-    REPORTER_ASSERT(reporter, !dash.filterPath(&filteredPath, path, &rec));
+    REPORTER_ASSERT(reporter, !dash.filterPath(&filteredPath, path, &rec, NULL));
     REPORTER_ASSERT(reporter, filteredPath.isEmpty());
 }
 
@@ -269,7 +272,8 @@
     test_crbug_140642(reporter);
     test_crbug_140803(reporter);
     test_inversepathwithclip(reporter);
-//    test_crbug131181(reporter);
+    // why?
+    if (false) test_crbug131181(reporter);
     test_infinite_dash(reporter);
     test_crbug_165432(reporter);
 }
diff --git a/tests/FontHostStreamTest.cpp b/tests/FontHostStreamTest.cpp
index eb301e3..bbf1a03 100644
--- a/tests/FontHostStreamTest.cpp
+++ b/tests/FontHostStreamTest.cpp
@@ -94,7 +94,7 @@
         const SkFontID typefaceID = SkTypeface::UniqueID(origTypeface);
         SkStream* fontData = SkFontHost::OpenStream(typefaceID);
         SkTypeface* streamTypeface = SkTypeface::CreateFromStream(fontData);
-        paint.setTypeface(streamTypeface)->unref();
+        SkSafeUnref(paint.setTypeface(streamTypeface));
         drawBG(&streamCanvas);
         streamCanvas.drawPosText("A", 1, &point, paint);
 
diff --git a/tests/FontHostTest.cpp b/tests/FontHostTest.cpp
index 879fdd0..4cd7812 100644
--- a/tests/FontHostTest.cpp
+++ b/tests/FontHostTest.cpp
@@ -41,8 +41,8 @@
 }
 
 static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) {
-    SkFontID fontID = face->uniqueID();
     if (false) { // avoid bit rot, suppress warning
+        SkFontID fontID = face->uniqueID();
         REPORTER_ASSERT(reporter, fontID);
     }
 
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index 46d0820..3106377 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -31,13 +31,13 @@
     return r->nextF() > .5f;
 }
 
-const GrEffect* create_random_effect(SkRandom* random,
-                                     GrContext* context,
-                                     GrTexture* dummyTextures[]) {
+const GrEffectRef* create_random_effect(SkRandom* random,
+                                        GrContext* context,
+                                        GrTexture* dummyTextures[]) {
 
     SkRandom sk_random;
     sk_random.setSeed(random->nextU());
-    GrEffect* effect = GrEffectTestFactory::CreateStage(&sk_random, context, dummyTextures);
+    GrEffectRef* effect = GrEffectTestFactory::CreateStage(&sk_random, context, dummyTextures);
     GrAssert(effect);
     return effect;
 }
@@ -79,7 +79,7 @@
         pdesc.fFirstCoverageStage = random_int(&random, GrDrawState::kNumStages);
 
         pdesc.fVertexLayout |= random_bool(&random) ?
-                                    GrDrawTarget::kCoverage_VertexLayoutBit :
+                                    GrDrawState::kCoverage_VertexLayoutBit :
                                     0;
 
 #if GR_GL_EXPERIMENTAL_GS
@@ -89,7 +89,7 @@
 
         bool edgeAA = random_bool(&random);
         if (edgeAA) {
-            pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
+            pdesc.fVertexLayout |= GrDrawState::kEdge_VertexLayoutBit;
             if (this->getCaps().shaderDerivativeSupport()) {
                 pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
                 pdesc.fDiscardIfOutsideEdge = random.nextBool();
@@ -114,21 +114,22 @@
                 // use separate tex coords?
                 if (random_bool(&random)) {
                     int t = random_int(&random, GrDrawState::kMaxTexCoords);
-                    pdesc.fVertexLayout |= StageTexCoordVertexLayoutBit(s, t);
+                    pdesc.fVertexLayout |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
                 }
                 // use text-formatted verts?
                 if (random_bool(&random)) {
-                    pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
+                    pdesc.fVertexLayout |= GrDrawState::kTextFormat_VertexLayoutBit;
                 }
 
                 GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()};
-                SkAutoTUnref<const GrEffect> effect(create_random_effect(&random,
+                SkAutoTUnref<const GrEffectRef> effect(create_random_effect(&random,
                                                                             getContext(),
                                                                             dummyTextures));
                 stages[s].setEffect(effect.get());
                 if (NULL != stages[s].getEffect()) {
                     pdesc.fEffectKeys[s] =
-                        stages[s].getEffect()->getFactory().glEffectKey(stages[s], this->glCaps());
+                        (*stages[s].getEffect())->getFactory().glEffectKey(stages[s],
+                                                                           this->glCaps());
                 }
             }
         }
@@ -168,12 +169,10 @@
 void forceLinking() {
     SkLightingImageFilter::CreateDistantLitDiffuse(SkPoint3(0,0,0), 0, 0, 0);
     SkMagnifierImageFilter mag(SkRect::MakeWH(SK_Scalar1, SK_Scalar1), SK_Scalar1);
-    GrEffectStage dummyStage;
-    GrConfigConversionEffect::InstallEffect(NULL,
-                                            false,
-                                            GrConfigConversionEffect::kNone_PMConversion,
-                                            SkMatrix::I(),
-                                            &dummyStage);
+    GrConfigConversionEffect::Create(NULL,
+                                     false,
+                                     GrConfigConversionEffect::kNone_PMConversion,
+                                     SkMatrix::I());
     SkScalar matrix[20];
     SkColorMatrixFilter cmf(matrix);
 }
diff --git a/tests/GrMemoryPoolTest.cpp b/tests/GrMemoryPoolTest.cpp
index 3f00f67..8660ea2 100644
--- a/tests/GrMemoryPoolTest.cpp
+++ b/tests/GrMemoryPoolTest.cpp
@@ -50,7 +50,7 @@
     static A* Create(SkRandom* r);
 
     static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(0 == GetInstanceCount());
 #endif
         GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
@@ -58,7 +58,7 @@
     }
 
     static void ResetAllocator() {
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(0 == GetInstanceCount());
 #endif
         gPool.reset(NULL);
@@ -233,7 +233,7 @@
                 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
                 delete rec.fInstance;
             }
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
             REPORTER_ASSERT(reporter, !A::GetInstanceCount());
 #endif
         }
diff --git a/tests/GradientTest.cpp b/tests/GradientTest.cpp
index 007caa3..e60c9ed 100644
--- a/tests/GradientTest.cpp
+++ b/tests/GradientTest.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
+#include "SkDevice.h"
 #include "SkTemplates.h"
 #include "SkShader.h"
 #include "SkColorShader.h"
@@ -126,9 +127,39 @@
     REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar)));
 }
 
+// Ensure that repeated color gradients behave like drawing a single color
+static void TestConstantGradient(skiatest::Reporter* reporter) {
+    const SkPoint pts[] = {
+        { 0, 0 },
+        { SkIntToScalar(10), 0 }
+    };
+    SkColor colors[] = { SK_ColorBLUE, SK_ColorBLUE };
+    const SkScalar pos[] = { 0, SK_Scalar1 };
+    SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts,
+                                                            colors,
+                                                            pos,
+                                                            2,
+                                                            SkShader::kClamp_TileMode));
+    SkBitmap outBitmap;
+    outBitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 1);
+    outBitmap.allocPixels();
+    SkPaint paint;
+    paint.setShader(s.get());
+    SkDevice device(outBitmap);
+    SkCanvas canvas(&device);
+    canvas.drawPaint(paint);
+    SkAutoLockPixels alp(outBitmap);
+    for (int i = 0; i < 10; i++) {
+        // The following is commented out because it currently fails
+        // Related bug: https://code.google.com/p/skia/issues/detail?id=1098
+
+        // REPORTER_ASSERT(reporter, SK_ColorBLUE == outBitmap.getColor(i, 0));
+    }
+}
+
 typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&);
 
-static void TestGradients(skiatest::Reporter* reporter) {
+static void TestGradientShaders(skiatest::Reporter* reporter) {
     static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
     static const SkScalar gPos[] = { 0, SK_ScalarHalf, SK_Scalar1 };
     static const SkPoint gPts[] = {
@@ -160,5 +191,9 @@
     }
 }
 
+static void TestGradients(skiatest::Reporter* reporter) {
+    TestGradientShaders(reporter);
+    TestConstantGradient(reporter);
+}
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Gradients", TestGradientsClass, TestGradients)
diff --git a/tests/InfRectTest.cpp b/tests/InfRectTest.cpp
index 808bcee..4d957dc 100644
--- a/tests/InfRectTest.cpp
+++ b/tests/InfRectTest.cpp
@@ -15,22 +15,24 @@
 }
 #endif
 
+struct RectCenter {
+    SkIRect  fRect;
+    SkIPoint fCenter;
+};
+
 static void test_center(skiatest::Reporter* reporter) {
-    static const struct {
-        SkIRect  fRect;
-        SkIPoint fCenter;
-    } data[] = {
+    static const RectCenter gData[] = {
         { { 0, 0, 0, 0 }, { 0, 0 } },
         { { 0, 0, 1, 1 }, { 0, 0 } },
         { { -1, -1, 0, 0 }, { -1, -1 } },
         { { 0, 0, 10, 7 }, { 5, 3 } },
         { { 0, 0, 11, 6 }, { 5, 3 } },
     };
-    for (size_t index = 0; index < SK_ARRAY_COUNT(data); ++index) {
+    for (size_t index = 0; index < SK_ARRAY_COUNT(gData); ++index) {
         REPORTER_ASSERT(reporter,
-                        data[index].fRect.centerX() == data[index].fCenter.x());
+                        gData[index].fRect.centerX() == gData[index].fCenter.x());
         REPORTER_ASSERT(reporter,
-                        data[index].fRect.centerY() == data[index].fCenter.y());
+                        gData[index].fRect.centerY() == gData[index].fCenter.y());
     }
 
     SkRandom rand;
diff --git a/tests/LListTest.cpp b/tests/LListTest.cpp
index 89c4971..ab36ef5 100644
--- a/tests/LListTest.cpp
+++ b/tests/LListTest.cpp
@@ -16,7 +16,7 @@
     }
     bool operator== (const ListElement& other) { return fID == other.fID; }
 
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
     // Make the instance count available publicly.
     static int InstanceCount() { return GetInstanceCount(); }
 #endif
@@ -135,7 +135,7 @@
         Iter iter3;
         Iter iter4;
 
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(0 == ListElement::InstanceCount());
 #endif
 
@@ -151,7 +151,7 @@
         // Create two identical lists, one by appending to head and the other to the tail.
         list1.addToHead(ListElement(1));
         list2.addToTail(ListElement(1));
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(2 == ListElement::InstanceCount());
 #endif
         iter1.init(list1, Iter::kHead_IterStart);
@@ -178,7 +178,7 @@
         iter4.init(list2, Iter::kTail_IterStart);
         list2.addToHead(ListElement(2));
 
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(3 == ListElement::InstanceCount());
 #endif
 
@@ -189,14 +189,14 @@
         REPORTER_ASSERT(reporter, list1 != list2);
         list1.addToHead(ListElement(2));
         REPORTER_ASSERT(reporter, list1 == list2);
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(4 == ListElement::InstanceCount());
 #endif
         REPORTER_ASSERT(reporter, !list1.isEmpty());
 
         list1.reset();
         list2.reset();
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(0 == ListElement::InstanceCount());
 #endif
         REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty());
@@ -301,12 +301,12 @@
                 --count;
             }
             REPORTER_ASSERT(reporter, count == list1.count());
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
             SkASSERT(count == ListElement::InstanceCount());
 #endif
         }
         list1.reset();
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
         SkASSERT(0 == ListElement::InstanceCount());
 #endif
     }
diff --git a/tests/MD5Test.cpp b/tests/MD5Test.cpp
new file mode 100644
index 0000000..9b9e756
--- /dev/null
+++ b/tests/MD5Test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 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 "SkMD5.h"
+
+static bool digests_equal(const SkMD5::Digest& expectedDigest, const SkMD5::Digest& computedDigest) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(expectedDigest.data); ++i) {
+        if (expectedDigest.data[i] != computedDigest.data[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static void md5_test(const char* string, const SkMD5::Digest& expectedDigest, skiatest::Reporter* reporter) {
+    size_t len = strlen(string);
+
+    // All at once
+    {
+        SkMD5 context;
+        context.update(reinterpret_cast<const uint8_t*>(string), len);
+        SkMD5::Digest digest;
+        context.finish(digest);
+
+        REPORTER_ASSERT(reporter, digests_equal(expectedDigest, digest));
+    }
+
+    // One byte at a time.
+    {
+        SkMD5 context;
+        const uint8_t* data = reinterpret_cast<const uint8_t*>(string);
+        const uint8_t* end = reinterpret_cast<const uint8_t*>(string + len);
+        for (; data < end; ++data) {
+            context.update(data, 1);
+        }
+        SkMD5::Digest digest;
+        context.finish(digest);
+
+        REPORTER_ASSERT(reporter, digests_equal(expectedDigest, digest));
+    }
+}
+
+static struct MD5Test {
+    const char* message;
+    SkMD5::Digest digest;
+} md5_tests[] = {
+    // Reference tests from RFC1321 Section A.5 ( http://www.ietf.org/rfc/rfc1321.txt )
+    { "", {{ 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e }} },
+    { "a", {{ 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 }} },
+    { "abc", {{ 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 }} },
+    { "message digest", {{ 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 }} },
+    { "abcdefghijklmnopqrstuvwxyz", {{ 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b }} },
+    { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", {{ 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f }} },
+    { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", {{ 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a }} },
+};
+
+static void TestMD5(skiatest::Reporter* reporter) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(md5_tests); ++i) {
+        md5_test(md5_tests[i].message, md5_tests[i].digest, reporter);
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("MD5", MD5TestClass, TestMD5)
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
index c0babee..7fc53a9 100644
--- a/tests/MathTest.cpp
+++ b/tests/MathTest.cpp
@@ -591,7 +591,7 @@
     test_blend(reporter);
 #endif
 
-//    test_floor(reporter);
+    if (false) test_floor(reporter);
 
     // disable for now
     if (false) test_blend31();  // avoid bit rot, suppress warning
diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp
index a680b44..269e359 100644
--- a/tests/Matrix44Test.cpp
+++ b/tests/Matrix44Test.cpp
@@ -25,7 +25,7 @@
     const SkScalar tolerance = SK_Scalar1 / 1024;
 #endif
 
-    return SkScalarAbs(a - b) <= tolerance;
+    return SkTAbs<SkMScalar>(a - b) <= tolerance;
 }
 
 template <typename T> void assert16(skiatest::Reporter* reporter, const T data[],
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 894278e..56292dc 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -216,106 +216,76 @@
     }
 }
 
-// This function is extracted from src/gpu/SkGpuDevice.cpp,
-// in order to make sure this function works correctly.
-static bool isSimilarityTransformation(const SkMatrix& matrix,
-                                       SkScalar tol = SK_ScalarNearlyZero) {
-    if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) {
-        return true;
-    }
-    if (matrix.hasPerspective()) {
-        return false;
-    }
-
-    SkScalar mx = matrix.get(SkMatrix::kMScaleX);
-    SkScalar sx = matrix.get(SkMatrix::kMSkewX);
-    SkScalar my = matrix.get(SkMatrix::kMScaleY);
-    SkScalar sy = matrix.get(SkMatrix::kMSkewY);
-
-    if (mx == 0 && sx == 0 && my == 0 && sy == 0) {
-        return false;
-    }
-
-    // it has scales or skews, but it could also be rotation, check it out.
-    SkVector vec[2];
-    vec[0].set(mx, sx);
-    vec[1].set(sy, my);
-
-    return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
-           SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
-                SkScalarSquare(tol));
-}
-
-static void test_matrix_is_similarity_transform(skiatest::Reporter* reporter) {
+static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
     SkMatrix mat;
 
     // identity
     mat.setIdentity();
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // translation only
     mat.reset();
     mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // scale with same size
     mat.reset();
     mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // scale with one negative
     mat.reset();
     mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // scale with different size
     mat.reset();
     mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // scale with same size at a pivot point
     mat.reset();
     mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
                  SkIntToScalar(2), SkIntToScalar(2));
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // scale with different size at a pivot point
     mat.reset();
     mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
                  SkIntToScalar(2), SkIntToScalar(2));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // skew with same size
     mat.reset();
     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // skew with different size
     mat.reset();
     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // skew with same size at a pivot point
     mat.reset();
     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
                 SkIntToScalar(2), SkIntToScalar(2));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // skew with different size at a pivot point
     mat.reset();
     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
                 SkIntToScalar(2), SkIntToScalar(2));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // perspective x
     mat.reset();
     mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // perspective y
     mat.reset();
     mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
 #if SK_SCALAR_IS_FLOAT
     /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
@@ -331,7 +301,7 @@
     for (int angle = 0; angle < 360; ++angle) {
         mat.reset();
         mat.setRotate(SkIntToScalar(angle));
-        REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+        REPORTER_ASSERT(reporter, mat.isSimilarity());
     }
 
     // see if there are any accumulated precision issues
@@ -339,40 +309,40 @@
     for (int i = 1; i < 360; i++) {
         mat.postRotate(SkIntToScalar(1));
     }
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // rotate + translate
     mat.reset();
     mat.setRotate(SkIntToScalar(30));
     mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // rotate + uniform scale
     mat.reset();
     mat.setRotate(SkIntToScalar(30));
     mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 
     // rotate + non-uniform scale
     mat.reset();
     mat.setRotate(SkIntToScalar(30));
     mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 #endif
 
     // all zero
     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // all zero except perspective
     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
 
     // scales zero, only skews
     mat.setAll(0, SK_Scalar1, 0,
                SK_Scalar1, 0, 0,
                0, 0, SkMatrix::I()[8]);
-    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
 }
 
 static void TestMatrix(skiatest::Reporter* reporter) {
@@ -384,13 +354,13 @@
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
 
-    mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
+    mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
     REPORTER_ASSERT(reporter, mat.invert(&inverse));
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
     test_flatten(reporter, mat);
 
-    mat.setScale(SK_Scalar1/2, SK_Scalar1/2);
+    mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
     REPORTER_ASSERT(reporter, mat.invert(&inverse));
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
@@ -407,6 +377,13 @@
     test_flatten(reporter, mat);
     test_flatten(reporter, iden2);
 
+    mat.setScale(0, SK_Scalar1);
+    REPORTER_ASSERT(reporter, !mat.invert(NULL));
+    REPORTER_ASSERT(reporter, !mat.invert(&inverse));
+    mat.setScale(SK_Scalar1, 0);
+    REPORTER_ASSERT(reporter, !mat.invert(NULL));
+    REPORTER_ASSERT(reporter, !mat.invert(&inverse));
+
     // rectStaysRect test
     {
         static const struct {
@@ -486,7 +463,7 @@
 #endif
 
     test_matrix_max_stretch(reporter);
-    test_matrix_is_similarity_transform(reporter);
+    test_matrix_is_similarity(reporter);
     test_matrix_recttorect(reporter);
 }
 
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 5cbb905..6e7d616 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -8,9 +8,11 @@
 
 
 #include "Test.h"
+#include "SkCanvas.h"
 #include "SkData.h"
 #include "SkFlate.h"
 #include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
 #include "SkPDFStream.h"
 #include "SkPDFTypes.h"
 #include "SkScalar.h"
@@ -94,11 +96,9 @@
 
 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.
+    SkAutoTUnref<SkMemoryStream> streamData(new SkMemoryStream(
+        streamBytes, strlen(streamBytes), true));
+    SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
     SimpleCheckObjectOutput(
         reporter, stream.get(),
         "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
@@ -114,8 +114,7 @@
                               "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.
+        SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
 
         SkDynamicMemoryWStream compressedByteStream;
         SkFlate::Deflate(streamData2.get(), &compressedByteStream);
@@ -146,13 +145,11 @@
 
 static void TestCatalog(skiatest::Reporter* reporter) {
     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);
-    int2->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int3 = new SkPDFInt(3);
-    int3->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int1Again(int1.get());
+    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
+    SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
+    SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3));
+    int1.get()->ref();
+    SkAutoTUnref<SkPDFInt> int1Again(int1.get());
 
     catalog.addObject(int1.get(), false);
     catalog.addObject(int2.get(), false);
@@ -173,12 +170,9 @@
 }
 
 static void TestObjectRef(skiatest::Reporter* reporter) {
-    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
-    int1->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
-    int2->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get());
-    int2ref->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
+    SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
+    SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
 
     SkPDFCatalog catalog((SkPDFDocument::Flags)0);
     catalog.addObject(int1.get(), false);
@@ -195,16 +189,11 @@
 }
 
 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.
+    SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict());
+    SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict());
+    SkAutoTUnref<SkPDFInt> int33(new SkPDFInt(33));
+    SkAutoTUnref<SkPDFDict> stubResource(new SkPDFDict());
+    SkAutoTUnref<SkPDFInt> int44(new SkPDFInt(44));
 
     stub->insert("Value", int33.get());
     stubResource->insert("InnerValue", int44.get());
@@ -230,90 +219,97 @@
                                             buffer.getOffset()));
 }
 
+// This test used to assert without the fix submitted for
+// http://code.google.com/p/skia/issues/detail?id=1083.
+// SKP files might have invalid glyph ids. This test ensures they are ignored,
+// and there is no assert on input data in Debug mode.
+static void test_issue1083(skiatest::Reporter* reporter) {
+    SkISize pageSize = SkISize::Make(100, 100);
+    SkPDFDevice* dev = new SkPDFDevice(pageSize, pageSize, SkMatrix::I());
+
+    SkCanvas c(dev);
+    SkPaint paint;
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    uint16_t glyphID = 65000;
+    c.drawText(&glyphID, 2, 0, 0, paint);
+
+    SkPDFDocument doc;
+    doc.appendPage(dev);
+
+    SkDynamicMemoryWStream stream;
+    doc.emitPDF(&stream);
+}
+
 static void TestPDFPrimitives(skiatest::Reporter* reporter) {
-    SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
-    int42->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
     SimpleCheckObjectOutput(reporter, int42.get(), "42");
 
-    SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
-    realHalf->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf));
     SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
 
 #if defined(SK_SCALAR_IS_FLOAT)
-    SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75f);
-    bigScalar->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f));
 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
     SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
 #else
     SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
 
-    SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
-    biggerScalar->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1));
     SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
 
-    SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
-    smallestScalar->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536));
     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.
+    SkAutoTUnref<SkPDFString> stringSimple(
+        new SkPDFString("test ) string ( foo"));
     SimpleCheckObjectOutput(reporter, stringSimple.get(),
                             "(test \\) string \\( foo)");
-    SkRefPtr<SkPDFString> stringComplex =
-        new SkPDFString("\ttest ) string ( foo");
-    stringComplex->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFString> stringComplex(
+        new SkPDFString("\ttest ) string ( foo"));
     SimpleCheckObjectOutput(reporter, stringComplex.get(),
                             "<0974657374202920737472696E67202820666F6F>");
 
-    SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
-    name->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab"));
     const char expectedResult[] = "/Test#20name#09with#23tab";
     CheckObjectOutput(reporter, name.get(), expectedResult,
                       strlen(expectedResult), false, false);
 
-    SkRefPtr<SkPDFName> escapedName = new SkPDFName("A#/%()<>[]{}B");
-    escapedName->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> escapedName(new SkPDFName("A#/%()<>[]{}B"));
     const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB";
     CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected,
                       strlen(escapedNameExpected), false, false);
 
     // Test that we correctly handle characters with the high-bit set.
     const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
-    SkRefPtr<SkPDFName> highBitName = new SkPDFName((const char*)highBitCString);
-    highBitName->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> highBitName(
+        new SkPDFName((const char*)highBitCString));
     const char highBitExpectedResult[] = "/#DE#ADbe#EF";
     CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
                       strlen(highBitExpectedResult), false, false);
 
-    SkRefPtr<SkPDFArray> array = new SkPDFArray;
-    array->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> array(new SkPDFArray);
     SimpleCheckObjectOutput(reporter, array.get(), "[]");
     array->append(int42.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[42]");
     array->append(realHalf.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
-    SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0);
-    int0->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0));
     array->append(int0.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
-    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
-    int1->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
     array->setAt(0, int1.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
 
-    SkRefPtr<SkPDFDict> dict = new SkPDFDict;
-    dict->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
     SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
-    SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
-    n1->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
     dict->insert(n1.get(), int42.get());
     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.
+    SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2"));
+    SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3"));
     dict->insert(n2.get(), realHalf.get());
     dict->insert(n3.get(), array.get());
     SimpleCheckObjectOutput(reporter, dict.get(),
@@ -326,6 +322,8 @@
     TestObjectRef(reporter);
 
     TestSubstitute(reporter);
+
+    test_issue1083(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 6f9a9e6..d4442cb 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -31,6 +31,108 @@
     return SkSurface::NewRaster(info);
 }
 
+static void build_path_170666(SkPath& path) {
+    path.moveTo(17.9459f, 21.6344f);
+    path.lineTo(139.545f, -47.8105f);
+    path.lineTo(139.545f, -47.8105f);
+    path.lineTo(131.07f, -47.3888f);
+    path.lineTo(131.07f, -47.3888f);
+    path.lineTo(122.586f, -46.9532f);
+    path.lineTo(122.586f, -46.9532f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(18085.1f, 31390.5f);
+    path.lineTo(18085.1f, 31390.5f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(17955, 31460.3f);
+    path.lineTo(17955, 31460.3f);
+    path.lineTo(17963.5f, 31459.9f);
+    path.lineTo(17963.5f, 31459.9f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(17.9551f, 21.6205f);
+    path.lineTo(17.9551f, 21.6205f);
+    path.lineTo(9.47091f, 22.0561f);
+    path.lineTo(9.47091f, 22.0561f);
+    path.lineTo(17.9459f, 21.6344f);
+    path.lineTo(17.9459f, 21.6344f);
+    path.close();path.moveTo(0.995934f, 22.4779f);
+    path.lineTo(0.986725f, 22.4918f);
+    path.lineTo(0.986725f, 22.4918f);
+    path.lineTo(17955, 31460.4f);
+    path.lineTo(17955, 31460.4f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(18093.6f, 31390.1f);
+    path.lineTo(18093.6f, 31390.1f);
+    path.lineTo(18093.6f, 31390);
+    path.lineTo(18093.6f, 31390);
+    path.lineTo(139.555f, -47.8244f);
+    path.lineTo(139.555f, -47.8244f);
+    path.lineTo(122.595f, -46.9671f);
+    path.lineTo(122.595f, -46.9671f);
+    path.lineTo(0.995934f, 22.4779f);
+    path.lineTo(0.995934f, 22.4779f);
+    path.close();
+    path.moveTo(5.43941f, 25.5223f);
+    path.lineTo(798267, -28871.1f);
+    path.lineTo(798267, -28871.1f);
+    path.lineTo(3.12512e+06f, -113102);
+    path.lineTo(3.12512e+06f, -113102);
+    path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
+    path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
+    path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
+    path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
+    path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
+    path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
+    path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
+    path.lineTo(2.78271e+08f, -1.00733e+07f);
+    path.lineTo(2.78271e+08f, -1.00733e+07f);
+    path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
+    path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
+    path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
+    path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
+    path.lineTo(2.77473e+08f, -1.00444e+07f);
+    path.lineTo(2.77473e+08f, -1.00444e+07f);
+    path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
+    path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
+    path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
+    path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
+    path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
+    path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
+    path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
+    path.lineTo(798284, -28872);
+    path.lineTo(798284, -28872);
+    path.lineTo(22.4044f, 24.6677f);
+    path.lineTo(22.4044f, 24.6677f);
+    path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
+    path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
+    path.close();
+}
+
+static void build_path_simple_170666(SkPath& path) {
+    path.moveTo(126.677f, 24.1591f);
+    path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
+}
+
+// This used to assert in the SK_DEBUG build, as the clip step would fail with
+// too-few interations in our cubic-line intersection code. That code now runs
+// 24 interations (instead of 16).
+static void test_crbug_170666(skiatest::Reporter* reporter) {
+    SkPath path;
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
+
+    build_path_simple_170666(path);
+    surface->getCanvas()->drawPath(path, paint);
+
+    build_path_170666(path);
+    surface->getCanvas()->drawPath(path, paint);
+}
+
 // Make sure we stay non-finite once we get there (unless we reset or rewind).
 static void test_addrect_isfinite(skiatest::Reporter* reporter) {
     SkPath path;
@@ -52,6 +154,45 @@
     REPORTER_ASSERT(reporter, path.isFinite());
 }
 
+static void build_big_path(SkPath* path, bool reducedCase) {
+    if (reducedCase) {
+        path->moveTo(577330, 1971.72f);
+        path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
+    } else {
+        path->moveTo(60.1631f, 7.70567f);
+        path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
+        path->lineTo(577379, 1977.77f);
+        path->quadTo(577364, 1979.57f, 577325, 1980.26f);
+        path->quadTo(577286, 1980.95f, 577245, 1980.13f);
+        path->quadTo(577205, 1979.3f, 577187, 1977.45f);
+        path->quadTo(577168, 1975.6f, 577183, 1973.8f);
+        path->quadTo(577198, 1972, 577238, 1971.31f);
+        path->quadTo(577277, 1970.62f, 577317, 1971.45f);
+        path->quadTo(577330, 1971.72f, 577341, 1972.11f);
+        path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
+        path->moveTo(306.718f, -32.912f);
+        path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
+    }
+}
+
+static void test_clipped_cubic(skiatest::Reporter* reporter) {
+    SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
+
+    // This path used to assert, because our cubic-chopping code incorrectly
+    // moved control points after the chop. This test should be run in SK_DEBUG
+    // mode to ensure that we no long assert.
+    SkPath path;
+    for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
+        build_big_path(&path, SkToBool(doReducedCase));
+
+        SkPaint paint;
+        for (int doAA = 0; doAA <= 1; ++doAA) {
+            paint.setAntiAlias(SkToBool(doAA));
+            surface->getCanvas()->drawPath(path, paint);
+        }
+    }
+}
+
 // Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
 // which triggered an assert, from a tricky cubic. This test replicates that
 // example, so we can ensure that we handle it (in SkEdge.cpp), and don't
@@ -167,7 +308,7 @@
 
         make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
 
-#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
         REPORTER_ASSERT(reporter, temp.isConvex());
 #endif
     }
@@ -196,7 +337,7 @@
 
         make_arb_round_rect(&temp, r, 0, 0);
 
-#ifdef SK_REDEFINE_ROOT2OVER2_TO_MAKE_ARCTOS_CONVEX
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
         SkRect result;
         REPORTER_ASSERT(reporter, temp.isRect(&result));
         REPORTER_ASSERT(reporter, r == result);
@@ -239,6 +380,7 @@
 
 static void test_path_isfinite(skiatest::Reporter* reporter) {
     const SkScalar inf = SK_ScalarInfinity;
+    const SkScalar negInf = SK_ScalarNegativeInfinity;
     const SkScalar nan = SK_ScalarNaN;
 
     SkPath path;
@@ -252,7 +394,7 @@
     REPORTER_ASSERT(reporter, path.isFinite());
 
     path.reset();
-    path.moveTo(inf, -inf);
+    path.moveTo(inf, negInf);
     REPORTER_ASSERT(reporter, !path.isFinite());
 
     path.reset();
@@ -2213,6 +2355,8 @@
     test_arb_round_rect_is_convex(reporter);
     test_arb_zero_rad_round_rect_is_rect(reporter);
     test_addrect_isfinite(reporter);
+    test_clipped_cubic(reporter);
+    test_crbug_170666(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index d14cf98..f6a18c1 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -11,6 +11,7 @@
 #include "SkPaint.h"
 #include "SkPicture.h"
 #include "SkRandom.h"
+#include "SkRRect.h"
 #include "SkShader.h"
 #include "SkStream.h"
 
@@ -54,6 +55,10 @@
     SkPaint paint;
     paint.setShader(s)->unref();
     canvas->drawRect(r, paint);
+    canvas->drawOval(r, paint);
+    SkRRect rr;
+    rr.setRectXY(r, 10, 10);
+    canvas->drawRRect(rr, paint);
 }
 
 // Return a picture with the bitmaps drawn at the specified positions.
@@ -130,10 +135,10 @@
             // the color is transparent, meaning no bitmap was drawn in that
             // pixel.
             if (pmc) {
-                int index = SkGetPackedR32(pmc);
+                uint32_t index = SkGetPackedR32(pmc);
                 SkASSERT(SkGetPackedG32(pmc) == index);
                 SkASSERT(SkGetPackedB32(pmc) == index);
-                SkASSERT(index < count);
+                SkASSERT(static_cast<int>(index) < count);
                 bitarray |= 1 << index;
             }
         }
@@ -390,6 +395,28 @@
     REPORTER_ASSERT(reporter, picture1->equals(picture2));
 }
 
+static void test_clone_empty(skiatest::Reporter* reporter) {
+    // This is a regression test for crbug.com/172062
+    // Before the fix, we used to crash accessing a null pointer when we
+    // had a picture with no paints. This test passes by not crashing.
+    {
+        SkPicture picture;
+        picture.beginRecording(1, 1);
+        picture.endRecording();
+        SkPicture* destPicture = picture.clone();
+        REPORTER_ASSERT(reporter, NULL != destPicture);
+        destPicture->unref();
+    }
+    {
+        // Test without call to endRecording
+        SkPicture picture;
+        picture.beginRecording(1, 1);
+        SkPicture* destPicture = picture.clone();
+        REPORTER_ASSERT(reporter, NULL != destPicture);
+        destPicture->unref();
+    }
+}
+
 static void TestPicture(skiatest::Reporter* reporter) {
 #ifdef SK_DEBUG
     test_deleting_empty_playback();
@@ -400,6 +427,7 @@
     test_peephole(reporter);
     test_gatherpixelrefs(reporter);
     test_bitmap_with_encoded_data(reporter);
+    test_clone_empty(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/PremulAlphaRoundTripTest.cpp b/tests/PremulAlphaRoundTripTest.cpp
index 17fb42a..8321c94 100644
--- a/tests/PremulAlphaRoundTripTest.cpp
+++ b/tests/PremulAlphaRoundTripTest.cpp
@@ -110,4 +110,3 @@
 
 #include "TestClassDef.h"
 DEFINE_GPUTESTCLASS("PremulAlphaRoundTripTest", PremulAlphaRoundTripTestClass, PremulAlphaRoundTripTest)
-
diff --git a/tests/QuickRejectTest.cpp b/tests/QuickRejectTest.cpp
index 0000833..9d87bff 100644
--- a/tests/QuickRejectTest.cpp
+++ b/tests/QuickRejectTest.cpp
@@ -29,6 +29,12 @@
         return false;
     }
 
+#ifdef SK_DEVELOPER
+    virtual void toString(SkString* str) const SK_OVERRIDE {
+        str->append("TestLooper:");
+    }
+#endif
+
     SK_DECLARE_UNFLATTENABLE_OBJECT()
 };
 
diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp
index 8b47906..a33e819 100644
--- a/tests/ReadPixelsTest.cpp
+++ b/tests/ReadPixelsTest.cpp
@@ -398,4 +398,3 @@
 
 #include "TestClassDef.h"
 DEFINE_GPUTESTCLASS("ReadPixels", ReadPixelsTestClass, ReadPixelsTest)
-
diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp
index 13f2fc4..d81287b 100644
--- a/tests/Reader32Test.cpp
+++ b/tests/Reader32Test.cpp
@@ -85,4 +85,3 @@
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Reader32", Reader32Class, Tests)
-
diff --git a/tests/RoundRectTest.cpp b/tests/RoundRectTest.cpp
index e88ed8a..a8387d5 100644
--- a/tests/RoundRectTest.cpp
+++ b/tests/RoundRectTest.cpp
@@ -11,10 +11,31 @@
 static const SkScalar kWidth = 100.0f;
 static const SkScalar kHeight = 100.0f;
 
+static void test_inset(skiatest::Reporter* reporter) {
+    SkRRect rr, rr2;
+    SkRect r = { 0, 0, 100, 100 };
+
+    rr.setRect(r);
+    rr.inset(-20, -20, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isRect());
+
+    rr.inset(20, 20, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isRect());
+
+    rr.inset(r.width()/2, r.height()/2, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isEmpty());
+
+    rr.setRectXY(r, 20, 20);
+    rr.inset(19, 19, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isSimple());
+    rr.inset(20, 20, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isRect());
+}
+
 // Test out the basic API entry points
 static void test_round_rect_basic(skiatest::Reporter* reporter) {
     // Test out initialization methods
-    SkPoint zeroPt = { 0.0, 0.0 };
+    SkPoint zeroPt = { 0, 0 };
     SkRRect empty;
 
     empty.setEmpty();
@@ -302,6 +323,7 @@
     test_round_rect_ovals(reporter);
     test_round_rect_general(reporter);
     test_round_rect_iffy_parameters(reporter);
+    test_inset(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/SHA1Test.cpp b/tests/SHA1Test.cpp
new file mode 100644
index 0000000..dde828c
--- /dev/null
+++ b/tests/SHA1Test.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 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 "SkSHA1.h"
+
+static bool digests_equal(const SkSHA1::Digest& expectedDigest, const SkSHA1::Digest& computedDigest) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(expectedDigest.data); ++i) {
+        if (expectedDigest.data[i] != computedDigest.data[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static struct SHA1Test {
+    const char* message;
+    const unsigned long int repeatCount;
+    const SkSHA1::Digest digest;
+} sha1_tests[] = {
+    // Reference tests from RFC3174 Section 7.3 ( http://www.ietf.org/rfc/rfc3174.txt )
+    { "abc", 1, {{ 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }} },
+    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, {{ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }} },
+    { "a", 1000000, {{ 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F }} },
+    { "0123456701234567012345670123456701234567012345670123456701234567", 10, {{ 0xDE, 0xA3, 0x56, 0xA2, 0xCD, 0xDD, 0x90, 0xC7, 0xA7, 0xEC, 0xED, 0xC5, 0xEB, 0xB5, 0x63, 0x93, 0x4F, 0x46, 0x04, 0x52 }} },
+
+    // Reference tests from running GNU sha1sum on test input
+    { "The quick brown fox jumps over the lazy dog", 1, {{ 0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12 }} },
+    { "", 1, {{ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 }} },
+};
+
+static void sha1_test(const SHA1Test& test, skiatest::Reporter* reporter) {
+    size_t len = strlen(test.message);
+
+    SkSHA1 context;
+    for (unsigned long int i = 0; i < test.repeatCount; ++i) {
+        context.update(reinterpret_cast<const uint8_t*>(test.message), len);
+    }
+    SkSHA1::Digest digest;
+    context.finish(digest);
+
+    REPORTER_ASSERT(reporter, digests_equal(test.digest, digest));
+}
+
+static void TestSHA1(skiatest::Reporter* reporter) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(sha1_tests); ++i) {
+        sha1_test(sha1_tests[i], reporter);
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("SHA1", SHA1TestClass, TestSHA1)
diff --git a/tests/ScalarTest.cpp b/tests/ScalarTest.cpp
index 801872e..8a16645 100644
--- a/tests/ScalarTest.cpp
+++ b/tests/ScalarTest.cpp
@@ -193,4 +193,3 @@
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Scalar", TestScalarClass, TestScalar)
-
diff --git a/tests/SortTest.cpp b/tests/SortTest.cpp
index 4783ba9..28c6e68 100644
--- a/tests/SortTest.cpp
+++ b/tests/SortTest.cpp
@@ -22,34 +22,41 @@
 }
 
 static void check_sort(skiatest::Reporter* reporter, const char label[],
-                       const int array[], int n) {
-    for (int j = 1; j < n; j++) {
-        if (array[j-1] > array[j]) {
+                       const int array[], const int reference[], int n) {
+    for (int j = 0; j < n; ++j) {
+        if (array[j] != reference[j]) {
             SkString str;
-           str.printf("%sSort [%d] failed %d %d", label, n,
-                      array[j-1], array[j]);
+            str.printf("%sSort [%d] failed %d %d", label, n, array[j], reference[j]);
             reporter->reportFailed(str);
         }
     }
 }
 
 static void TestSort(skiatest::Reporter* reporter) {
-    int         array[500];
+    /** An array of random numbers to be sorted. */
+    int randomArray[500];
+    /** The reference sort of the random numbers. */
+    int sortedArray[SK_ARRAY_COUNT(randomArray)];
+    /** The random numbers are copied into this array, sorted by an SkSort,
+        then this array is compared against the reference sort. */
+    int workingArray[SK_ARRAY_COUNT(randomArray)];
     SkRandom    rand;
 
     for (int i = 0; i < 10000; i++) {
-        int count = rand.nextRangeU(1, SK_ARRAY_COUNT(array));
+        int count = rand.nextRangeU(1, SK_ARRAY_COUNT(randomArray));
+        rand_array(rand, randomArray, count);
 
-        rand_array(rand, array, count);
-        SkTHeapSort<int>(array, count);
-        check_sort(reporter, "Heap", array, count);
+        // Use qsort as the reference sort.
+        memcpy(sortedArray, randomArray, sizeof(randomArray));
+        qsort(sortedArray, count, sizeof(sortedArray[0]), compare_int);
 
-        rand_array(rand, array, count);
-        SkTQSort<int>(array, array + count - 1);
-        check_sort(reporter, "Quick", array, count);
-    }
-    if (false) { // avoid bit rot, suppress warning
-        compare_int(array, array);
+        memcpy(workingArray, randomArray, sizeof(randomArray));
+        SkTHeapSort<int>(workingArray, count);
+        check_sort(reporter, "Heap", workingArray, sortedArray, count);
+
+        memcpy(workingArray, randomArray, sizeof(randomArray));
+        SkTQSort<int>(workingArray, workingArray + count - 1);
+        check_sort(reporter, "Quick", workingArray, sortedArray, count);
     }
 }
 
diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp
index b5e3cd9..ed51aa7 100644
--- a/tests/StreamTest.cpp
+++ b/tests/StreamTest.cpp
@@ -130,10 +130,29 @@
     }
 }
 
+// Test that setting an SkMemoryStream to a NULL data does not result in a crash when calling
+// methods that access fData.
+static void TestDereferencingData(SkMemoryStream* memStream) {
+    memStream->read(NULL, 0);
+    memStream->getMemoryBase();
+    SkAutoDataUnref data(memStream->copyToData());
+}
+
+static void TestNullData() {
+    SkData* nullData = NULL;
+    SkMemoryStream memStream(nullData);
+    TestDereferencingData(&memStream);
+
+    memStream.setData(nullData);
+    TestDereferencingData(&memStream);
+
+}
+
 static void TestStreams(skiatest::Reporter* reporter) {
     TestRStream(reporter);
     TestWStream(reporter);
     TestPackedUInt(reporter);
+    TestNullData();
 }
 
 #include "TestClassDef.h"
diff --git a/tests/StrokeTest.cpp b/tests/StrokeTest.cpp
index ded1130..9336bed 100644
--- a/tests/StrokeTest.cpp
+++ b/tests/StrokeTest.cpp
@@ -61,4 +61,3 @@
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Stroke", TestStrokeClass, TestStroke)
-
diff --git a/tests/TLSTest.cpp b/tests/TLSTest.cpp
index 17f7dcb..38eb322 100644
--- a/tests/TLSTest.cpp
+++ b/tests/TLSTest.cpp
@@ -71,7 +71,7 @@
     // TODO: Disabled for now to work around
     // http://code.google.com/p/skia/issues/detail?id=619
     // ('flaky segfault in TLS test on Shuttle_Ubuntu12 buildbots')
-    //test_threads(&thread_main);
+    if( false ) test_threads(&thread_main);
 
     // Test to ensure that at thread destruction, TLS destructors
     // have been called.
diff --git a/tests/Test.cpp b/tests/Test.cpp
index 6953d94..de5e793 100644
--- a/tests/Test.cpp
+++ b/tests/Test.cpp
@@ -114,4 +114,3 @@
     return NULL;
 #endif
 }
-
diff --git a/tests/TriangulationTest.cpp b/tests/TriangulationTest.cpp
deleted file mode 100644
index 8d692c7..0000000
--- a/tests/TriangulationTest.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-
-/*
- * 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"
-
-static int GetIndexFromPoint(const SkPoint &pt,
-                             int numPts, const SkPoint *pts) {
-    for (int i = 0; i < numPts; ++i)
-        if (pt.fX == pts[i].fX && pt.fY == pts[i].fY)
-            return i;
-    return -1;
-}
-
-
-bool gPrintTriangles = false;   // Can we set this on the command line?
-
-static void PrintTriangles(const SkTDArray<SkPoint> &triangles,
-                           int numPts, const SkPoint *pts,
-                           skiatest::Reporter* reporter) {
-    if (gPrintTriangles) {
-        SkPoint *p = triangles.begin();
-        int n = triangles.count();
-        REPORTER_ASSERT(reporter, n % 3 == 0);
-        n /= 3;
-        printf("%d Triangles:\n{\n", n);
-        for (; n-- != 0; p += 3)
-            printf("    { {%.7g, %.7g}, {%.7g, %.7g}, {%.7g, %.7g} },  "
-                   "// { %2d, %2d, %2d }\n",
-                p[0].fX, p[0].fY,
-                p[1].fX, p[1].fY,
-                p[2].fX, p[2].fY,
-                GetIndexFromPoint(p[0], numPts, pts),
-                GetIndexFromPoint(p[1], numPts, pts),
-                GetIndexFromPoint(p[2], numPts, pts));
-        printf("}\n");
-    }
-}
-
-
-static bool CompareTriangleList(int numTriangles,
-                                const float refTriangles[][3][2],
-                                const SkTDArray<SkPoint> &triangles) {
-    if (triangles.count() != numTriangles * 3) {
-        printf("Expected %d triangles, not %d\n",
-               numTriangles, triangles.count() / 3);
-        return false;
-    }
-    int numErrors = 0;
-    for (int i = 0; i < numTriangles; ++i) {
-        const float *r = &refTriangles[i][0][0];
-        const SkScalar *t = &triangles[i * 3].fX;
-        bool equalTriangle = true;
-        for (int j = 6; j-- != 0; r++, t++)
-            if (SkFloatToScalar(*r) != *t)
-                equalTriangle = false;
-        if (equalTriangle == false) {
-            ++numErrors;
-            printf("Triangle %d differs\n", i);
-        }
-    }
-    if (numErrors > 0)
-        printf("%d triangles differ\n", numErrors);
-    return numErrors == 0;
-}
-
-
-#ifndef LEFT_HANDED_POLYGONS
-static const SkPoint star[] = {
-    // Outer contour is clockwise if Y is down, counterclockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 70), SkFloatToScalar(120)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 40)  },
-    // Inner contour is counterclockwise if Y is down, clockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  }
-};
-static const SkPoint plus[] = {
-    { SkFloatToScalar( 70), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 70), SkFloatToScalar(110)  },
-    { SkFloatToScalar( 50), SkFloatToScalar(110)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 10)  }
-};
-static const SkPoint zipper[] = {
-    { SkFloatToScalar( 10), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 20), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 20), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(140), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(140), SkFloatToScalar( 60)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 60)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 20)  },
-    { SkFloatToScalar(120), SkFloatToScalar( 20)  },
-    { SkFloatToScalar(120), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 60)  }
-};
-#else  // LEFT_HANDED_POLYGONS
-static const SkPoint star[] = {
-    // Outer contour is counterclockwise if Y is down, clockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 40)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 70), SkFloatToScalar(120)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 80)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 80)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 50)  },
-    // Inner contour is clockwise if Y is down, counterclockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  }
-};
-#endif  // LEFT_HANDED_POLYGONS
-#define kNumStarVertices 10
-#define kNumStarHoleVertices (sizeof(star) / sizeof(star[0]))
-
-
-// Star test
-static void TestStarTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { { 30,  20}, { 70,  40}, { 40,  50} },  // { 8, 9, 7 }
-        { {100,  50}, { 10,  80}, { 40,  50} },  // { 1, 6, 7 }
-        { {100,  50}, { 40,  50}, { 70,  40} },  // { 1, 7, 9 }
-        { {100,  50}, { 70,  40}, {110,  20} },  // { 1, 9, 0 }
-        { { 90,  80}, { 70, 120}, { 50,  80} },  // { 3, 4, 5 }
-        { {130,  80}, { 90,  80}, { 50,  80} },  // { 2, 3, 5 } degen
-        { {130,  80}, { 50,  80}, { 10,  80} },  // { 2, 5, 6 } degen
-        { {130,  80}, { 10,  80}, {100,  50} }   // { 2, 6, 1 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    bool success = SkConcaveToTriangles(kNumStarVertices, star, &triangles);
-    PrintTriangles(triangles, kNumStarVertices, star, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Star", success ? reporter->kPassed
-                                                 : reporter->kFailed);
-}
-
-
-// Star with hole test
-static void TestStarHoleTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { {100,  50}, { 80,  60}, { 70,  50} },  // {  1, 15, 16 }
-        { {100,  50}, { 70,  50}, {110,  20} },  // {  1, 16,  0 }
-        { { 30,  20}, { 70,  40}, { 40,  50} },  // {  8,  9,  7 }
-        { { 60,  70}, { 80,  70}, { 10,  80} },  // { 13, 14,  6 }
-        { { 60,  60}, { 60,  70}, { 10,  80} },  // { 12, 13,  6 }
-        { { 70,  50}, { 60,  60}, { 10,  80} },  // { 11, 12,  6 }
-        { { 70,  50}, { 10,  80}, { 40,  50} },  // { 11,  6,  7 }
-        { { 70,  50}, { 40,  50}, { 70,  40} },  // { 11,  7,  9 }
-        { { 70,  50}, { 70,  40}, {110,  20} },  // { 11,  9, 10 }
-        { { 90,  80}, { 70, 120}, { 50,  80} },  // {  3,  4,  5 }
-        { {130,  80}, { 90,  80}, { 50,  80} },  // {  2,  3,  5 } degen
-        { {130,  80}, { 50,  80}, { 10,  80} },  // {  2,  5,  6 } degen
-        { {130,  80}, { 10,  80}, { 80,  70} },  // {  2,  6, 14 }
-        { {130,  80}, { 80,  70}, { 80,  60} },  // {  2, 14, 15 }
-        { {130,  80}, { 80,  60}, {100,  50} }   // {  2, 15,  1 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    bool success = SkConcaveToTriangles(kNumStarHoleVertices, star, &triangles);
-    PrintTriangles(triangles, kNumStarHoleVertices, star, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Star With Hole", success ? reporter->kPassed
-                                                           : reporter->kFailed);
-}
-
-
-// Plus test
-static void TestPlusTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { { 50,  10}, { 70,  10}, { 50, 50} },  // { 11,  0, 10 }
-        { { 70,  50}, {110,  50}, { 10, 70} },  // {  1,  2,  8 }
-        { { 70,  50}, { 10,  70}, { 10, 50} },  // {  1,  8,  9 }
-        { { 70,  50}, { 10,  50}, { 50, 50} },  // {  1,  9, 10 }
-        { { 70,  50}, { 50,  50}, { 70, 10} },  // {  1, 10,  0 }
-        { { 70,  70}, { 50, 110}, { 50, 70} },  // {  4,  6,  7 }
-        { {110,  70}, { 70,  70}, { 50, 70} },  // {  3,  4,  7 }
-        { {110,  70}, { 50,  70}, { 10, 70} },  // {  3,  7,  8 }
-        { {110,  70}, { 10,  70}, {110, 50} },  // {  3,  8,  2 }
-        { { 70, 110}, { 50, 110}, { 70, 70} },  // {  5,  6,  4 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    const size_t numVertices = sizeof(plus) / sizeof(SkPoint);
-    bool success = SkConcaveToTriangles(numVertices, plus, &triangles);
-    PrintTriangles(triangles, numVertices, plus, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Plus", success ? reporter->kPassed
-                                                 : reporter->kFailed);
-}
-
-
-// Zipper test
-static void TestZipperTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { { 10, 10}, { 20, 10}, { 20, 50} },  // {  1,  2,  3 }
-        { { 20, 50}, { 30, 50}, { 10, 60} },  // {  3,  4,  0 }
-        { { 10, 10}, { 20, 50}, { 10, 60} },  // {  1,  3,  0 }
-        { { 30, 10}, { 60, 10}, { 40, 20} },  // {  5,  6, 26 }
-        { { 30, 10}, { 40, 20}, { 30, 50} },  // {  5, 26,  4 }
-        { { 40, 60}, { 10, 60}, { 30, 50} },  // { 27,  0,  4 }
-        { { 40, 60}, { 30, 50}, { 40, 20} },  // { 27,  4, 26 }
-        { { 60, 50}, { 70, 50}, { 50, 60} },  // {  7,  8, 24 }
-        { { 50, 20}, { 60, 50}, { 50, 60} },  // { 25,  7, 24 }
-        { { 50, 20}, { 40, 20}, { 60, 10} },  // { 25, 26,  6 }
-        { { 60, 50}, { 50, 20}, { 60, 10} },  // {  7, 25,  6 }
-        { { 70, 10}, {100, 10}, { 80, 20} },  // {  9, 10, 22 }
-        { { 70, 10}, { 80, 20}, { 70, 50} },  // {  9, 22,  8 }
-        { { 80, 60}, { 50, 60}, { 70, 50} },  // { 23, 24,  8 }
-        { { 80, 60}, { 70, 50}, { 80, 20} },  // { 23,  8, 22 }
-        { {100, 50}, {110, 50}, { 90, 60} },  // { 11, 12, 20 }
-        { { 90, 20}, {100, 50}, { 90, 60} },  // { 21, 11, 20 }
-        { { 90, 20}, { 80, 20}, {100, 10} },  // { 21, 22, 10 }
-        { {100, 50}, { 90, 20}, {100, 10} },  // { 11, 21, 10 }
-        { {110, 10}, {140, 10}, {120, 20} },  // { 13, 14, 18 }
-        { {110, 10}, {120, 20}, {110, 50} },  // { 13, 18, 12 }
-        { {120, 60}, { 90, 60}, {110, 50} },  // { 19, 20, 12 }
-        { {120, 60}, {110, 50}, {120, 20} },  // { 19, 12, 18 }
-        { {140, 60}, {130, 60}, {130, 20} },  // { 15, 16, 17 }
-        { {130, 20}, {120, 20}, {140, 10} },  // { 17, 18, 14 }
-        { {140, 60}, {130, 20}, {140, 10} },  // { 15, 17, 14 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    const size_t numVertices = sizeof(zipper) / sizeof(SkPoint);
-    bool success = SkConcaveToTriangles(numVertices, zipper, &triangles);
-    PrintTriangles(triangles, numVertices, zipper, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Zipper", success ? reporter->kPassed
-                                                   : reporter->kFailed);
-}
-
-
-static void TestTriangulation(skiatest::Reporter* reporter) {
-    TestStarTriangulation(reporter);
-    TestStarHoleTriangulation(reporter);
-    TestPlusTriangulation(reporter);
-    TestZipperTriangulation(reporter);
-}
-
-#include "TestClassDef.h"
-DEFINE_TESTCLASS("Triangulation", TriangulationTestClass, TestTriangulation)
diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp
index 1c2f870..7f27f7e 100644
--- a/tests/UtilsTest.cpp
+++ b/tests/UtilsTest.cpp
@@ -27,35 +27,6 @@
 
 SK_DEFINE_INST_COUNT(RefClass)
 
-static void test_refptr(skiatest::Reporter* reporter) {
-    RefClass* r0 = new RefClass(0);
-
-    SkRefPtr<RefClass> rc0;
-    REPORTER_ASSERT(reporter, rc0.get() == NULL);
-    REPORTER_ASSERT(reporter, !rc0);
-
-    SkRefPtr<RefClass> rc1;
-    REPORTER_ASSERT(reporter, rc0 == rc1);
-    REPORTER_ASSERT(reporter, rc0.get() != r0);
-
-    rc0 = r0;
-    REPORTER_ASSERT(reporter, rc0);
-    REPORTER_ASSERT(reporter, rc0 != rc1);
-    REPORTER_ASSERT(reporter, rc0.get() == r0);
-
-    rc1 = rc0;
-    REPORTER_ASSERT(reporter, rc1);
-    REPORTER_ASSERT(reporter, rc0 == rc1);
-    REPORTER_ASSERT(reporter, rc0.get() == r0);
-
-    rc0 = NULL;
-    REPORTER_ASSERT(reporter, rc0.get() == NULL);
-    REPORTER_ASSERT(reporter, !rc0);
-    REPORTER_ASSERT(reporter, rc0 != rc1);
-
-    r0->unref();
-}
-
 static void test_autounref(skiatest::Reporter* reporter) {
     RefClass obj(0);
     REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
@@ -178,7 +149,6 @@
 
     test_utf16(reporter);
     test_search(reporter);
-    test_refptr(reporter);
     test_autounref(reporter);
 }
 
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
index f5c4175..715d8f7 100644
--- a/tests/WritePixelsTest.cpp
+++ b/tests/WritePixelsTest.cpp
@@ -136,7 +136,7 @@
     static SkBitmap bmp;
     if (bmp.isNull()) {
         bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
-        bool alloc = bmp.allocPixels();
+        SkDEBUGCODE(bool alloc = ) bmp.allocPixels();
         SkASSERT(alloc);
         SkAutoLockPixels alp(bmp);
         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
index 4715f7a..33777a1 100644
--- a/tests/Writer32Test.cpp
+++ b/tests/Writer32Test.cpp
@@ -173,7 +173,6 @@
     // dynamic allocator
     {
         SkWriter32 writer(256 * 4);
-        REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
         test1(reporter, &writer);
 
         writer.reset();
@@ -183,13 +182,11 @@
         testWritePad(reporter, &writer);
     }
 
-    // single-block
+    // storage-block
     {
         SkWriter32 writer(0);
         uint32_t storage[256];
-        REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
         writer.reset(storage, sizeof(storage));
-        REPORTER_ASSERT(reporter, (void*)storage == writer.getSingleBlock());
         test1(reporter, &writer);
 
         writer.reset(storage, sizeof(storage));
@@ -197,6 +194,11 @@
 
         writer.reset(storage, sizeof(storage));
         testWritePad(reporter, &writer);
+
+        // try overflowing the storage-block
+        uint32_t smallStorage[8];
+        writer.reset(smallStorage, sizeof(smallStorage));
+        test2(reporter, &writer);
     }
 
     // small storage
@@ -227,4 +229,3 @@
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Writer32", Writer32Class, Tests)
-
diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp
index 5e944f7..8a1feae 100644
--- a/tests/skia_test.cpp
+++ b/tests/skia_test.cpp
@@ -84,7 +84,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
     gPrintInstCount = true;
 #endif
     SkGraphics::Init();
@@ -164,4 +164,3 @@
     return tool_main(argc, (char**) argv);
 }
 #endif
-
diff --git a/tools/PdfRenderer.cpp b/tools/PdfRenderer.cpp
index 8819266..9a4bd38 100644
--- a/tools/PdfRenderer.cpp
+++ b/tools/PdfRenderer.cpp
@@ -48,15 +48,10 @@
     }
 }
 
-bool PdfRenderer::write(const SkString& path) const {
+void PdfRenderer::write(SkWStream* stream) const {
     SkPDFDocument doc;
     doc.appendPage(fPDFDevice);
-    SkFILEWStream stream(path.c_str());
-    if (stream.isValid()) {
-        doc.emitPDF(&stream);
-        return true;
-    }
-    return false;
+    doc.emitPDF(stream);
 }
 
 void SimplePdfRenderer::render() {
diff --git a/tools/PdfRenderer.h b/tools/PdfRenderer.h
index bce6197..3524a9d 100644
--- a/tools/PdfRenderer.h
+++ b/tools/PdfRenderer.h
@@ -39,7 +39,7 @@
         , fPDFDevice(NULL)
         {}
 
-    bool write(const SkString& path) const;
+    void write(SkWStream* stream) const;
 
 protected:
     SkCanvas* setupCanvas();
diff --git a/tools/PictureBenchmark.cpp b/tools/PictureBenchmark.cpp
index bdf1306..f1be2aa 100644
--- a/tools/PictureBenchmark.cpp
+++ b/tools/PictureBenchmark.cpp
@@ -70,9 +70,8 @@
     // We throw this away to remove first time effects (such as paging in this program)
     fRenderer->setup();
     fRenderer->render(NULL);
-    fRenderer->resetState();
+    fRenderer->resetState(true);
 
-    BenchTimer* timer = this->setupTimer();
     bool usingGpu = false;
 #if SK_SUPPORT_GPU
     usingGpu = fRenderer->isUsingGpuDevice();
@@ -95,26 +94,55 @@
 
         int x, y;
         while (tiledRenderer->nextTile(x, y)) {
-            TimerData timerData(tiledRenderer->getPerIterTimeFormat(),
-                                tiledRenderer->getNormalTimeFormat());
+            // There are two timers, which will behave slightly differently:
+            // 1) longRunningTimer, along with perTileTimerData, will time how long it takes to draw
+            // one tile fRepeats times, and take the average. As such, it will not respect the
+            // logPerIter or printMin options, since it does not know the time per iteration. It
+            // will also be unable to call flush() for each tile.
+            // The goal of this timer is to make up for a system timer that is not precise enough to
+            // measure the small amount of time it takes to draw one tile once.
+            //
+            // 2) perTileTimer, along with perTileTimerData, will record each run separately, and
+            // then take the average. As such, it supports logPerIter and printMin options.
+            SkAutoTDelete<BenchTimer> longRunningTimer(this->setupTimer());
+            TimerData longRunningTimerData(tiledRenderer->getPerIterTimeFormat(),
+                                           tiledRenderer->getNormalTimeFormat());
+            SkAutoTDelete<BenchTimer> perTileTimer(this->setupTimer());
+            TimerData perTileTimerData(tiledRenderer->getPerIterTimeFormat(),
+                                       tiledRenderer->getNormalTimeFormat());
+            longRunningTimer->start();
             for (int i = 0; i < fRepeats; ++i) {
-                timer->start();
+                perTileTimer->start();
                 tiledRenderer->drawCurrentTile();
-                timer->truncatedEnd();
-                tiledRenderer->resetState();
-                timer->end();
-                timerData.appendTimes(timer, fRepeats - 1 == i);
+                perTileTimer->truncatedEnd();
+                tiledRenderer->resetState(false);
+                perTileTimer->end();
+                perTileTimerData.appendTimes(perTileTimer.get(), fRepeats - 1 == i);
             }
+            longRunningTimer->truncatedEnd();
+            tiledRenderer->resetState(true);
+            longRunningTimer->end();
+            longRunningTimerData.appendTimes(longRunningTimer.get(), true);
+
             SkString configName = tiledRenderer->getConfigName();
             configName.appendf(": tile [%i,%i] out of [%i,%i]", x, y, xTiles, yTiles);
-            SkString result = timerData.getResult(fLogPerIter, fPrintMin, fRepeats,
-                                                  configName.c_str(), fShowWallTime,
-                                                  fShowTruncatedWallTime, fShowCpuTime,
-                                                  fShowTruncatedCpuTime, usingGpu && fShowGpuTime);
+            SkString result = perTileTimerData.getResult(fLogPerIter, fPrintMin, fRepeats,
+                                                         configName.c_str(), fShowWallTime,
+                                                         fShowTruncatedWallTime, fShowCpuTime,
+                                                         fShowTruncatedCpuTime,
+                                                         usingGpu && fShowGpuTime);
             result.append("\n");
             this->logProgress(result.c_str());
+
+            configName.append(" <averaged>");
+            SkString longRunningResult = longRunningTimerData.getResult(false, false, fRepeats,
+                    configName.c_str(), fShowWallTime, fShowTruncatedWallTime,
+                    fShowCpuTime, fShowTruncatedCpuTime, usingGpu && fShowGpuTime);
+            longRunningResult.append("\n");
+            this->logProgress(longRunningResult.c_str());
         }
     } else {
+        SkAutoTDelete<BenchTimer> timer(this->setupTimer());
         TimerData timerData(fRenderer->getPerIterTimeFormat(), fRenderer->getNormalTimeFormat());
         for (int i = 0; i < fRepeats; ++i) {
             fRenderer->setup();
@@ -124,10 +152,10 @@
             timer->truncatedEnd();
 
             // Finishes gl context
-            fRenderer->resetState();
+            fRenderer->resetState(true);
             timer->end();
 
-            timerData.appendTimes(timer, fRepeats - 1 == i);
+            timerData.appendTimes(timer.get(), fRepeats - 1 == i);
         }
 
         SkString configName = fRenderer->getConfigName();
@@ -140,7 +168,6 @@
     }
 
     fRenderer->end();
-    SkDELETE(timer);
 }
 
 }
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index 61de8c6..cb2c3a4 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -131,7 +131,7 @@
 }
 
 void PictureRenderer::end() {
-    this->resetState();
+    this->resetState(true);
     SkSafeUnref(fPicture);
     fPicture = NULL;
     fCanvas.reset(NULL);
@@ -172,7 +172,7 @@
     }
 }
 
-void PictureRenderer::resetState() {
+void PictureRenderer::resetState(bool callFinish) {
 #if SK_SUPPORT_GPU
     if (this->isUsingGpuDevice()) {
         SkGLContext* glContext = fGrContextFactory.getGLContext(
@@ -184,14 +184,17 @@
         }
 
         fGrContext->flush();
-        SK_GL(*glContext, Finish());
+        if (callFinish) {
+            SK_GL(*glContext, Finish());
+        }
     }
 #endif
 }
 
 uint32_t PictureRenderer::recordFlags() {
-    return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
-        SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
+    return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
+        SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
+        SkPicture::kUsePathBoundsForClip_RecordingFlag;
 }
 
 /**
@@ -288,7 +291,7 @@
         *out = SkNEW(SkBitmap);
         setup_bitmap(*out, fPicture->width(), fPicture->height());
         fCanvas->readPixels(*out, 0, 0);
-    }    
+    }
     return true;
 }
 
@@ -315,7 +318,7 @@
     if (NULL != path) {
         return write(fCanvas, *path);
     }
-    
+
     if (NULL != out) {
         *out = SkNEW(SkBitmap);
         setup_bitmap(*out, fPicture->width(), fPicture->height());
@@ -522,8 +525,8 @@
         }
         if (NULL != out) {
             if (fCanvas->readPixels(&bitmap, 0, 0)) {
-                bitmapCopySubset(bitmap, *out, fTileRects[i].left(),
-                                 fTileRects[i].top());
+                bitmapCopySubset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
+                                 SkScalarFloorToInt(fTileRects[i].top()));
             } else {
                 success = false;
             }
@@ -594,9 +597,9 @@
         SkBitmap bitmap;
         if (fBitmap != NULL) {
             // All tiles are the same size.
-            setup_bitmap(&bitmap, fRects[0].width(), fRects[0].height());
+            setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
         }
-        
+
         for (int i = fStart; i < fEnd; i++) {
             DrawTileToCanvas(fCanvas, fRects[i], fClone);
             if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
@@ -608,8 +611,8 @@
             if (fBitmap != NULL) {
                 if (fCanvas->readPixels(&bitmap, 0, 0)) {
                     SkAutoLockPixels alp(*fBitmap);
-                    bitmapCopySubset(bitmap, fBitmap, fRects[i].left(),
-                                     fRects[i].top());
+                    bitmapCopySubset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
+                                     SkScalarFloorToInt(fRects[i].top()));
                 } else {
                     *fSuccess = false;
                     // If one tile fails to read pixels, do not continue drawing the rest.
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
index 5d6c516..df38faa 100644
--- a/tools/PictureRenderer.h
+++ b/tools/PictureRenderer.h
@@ -110,7 +110,12 @@
      */
     virtual TiledPictureRenderer* getTiledRenderer() { return NULL; }
 
-    void resetState();
+    /**
+     * Resets the GPU's state. Does nothing if the backing is raster. For a GPU renderer, calls
+     * flush, and calls finish if callFinish is true.
+     * @param callFinish Whether to call finish.
+     */
+    void resetState(bool callFinish);
 
     void setDeviceType(SkDeviceTypes deviceType) {
         fDeviceType = deviceType;
@@ -184,10 +189,10 @@
         , fBBoxHierarchyType(kNone_BBoxHierarchyType)
         , fGridWidth(0)
         , fGridHeight(0)
-        , fScaleFactor(SK_Scalar1)
 #if SK_SUPPORT_GPU
         , fGrContext(fGrContextFactory.get(GrContextFactory::kNative_GLContextType))
 #endif
+        , fScaleFactor(SK_Scalar1)
         {
             sk_bzero(fDrawFilters, sizeof(fDrawFilters));
             fViewport.set(0, 0);
diff --git a/tools/bench_pictures.cfg b/tools/bench_pictures.cfg
index c64a686..661af52 100644
--- a/tools/bench_pictures.cfg
+++ b/tools/bench_pictures.cfg
@@ -55,11 +55,11 @@
   TiledBitmapConfig(1024, 64),
 
   # Different bounding box heirarchies, for different modes.
-  RecordRTreeConfig(          DEFAULT_TILE_X, DEFAULT_TILE_Y),
-  RecordGridConfig(           DEFAULT_TILE_X, DEFAULT_TILE_Y),
-  PlaybackCreationRTreeConfig(DEFAULT_TILE_X, DEFAULT_TILE_Y),
-  PlaybackCreationGridConfig( DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  RecordRTreeConfig(),
+  PlaybackCreationRTreeConfig(),
   TileRTreeConfig(            DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  RecordGridConfig(           DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  PlaybackCreationGridConfig( DEFAULT_TILE_X, DEFAULT_TILE_Y),
   TileGridConfig(             DEFAULT_TILE_X, DEFAULT_TILE_Y),
 ]
 
@@ -73,8 +73,8 @@
 
   configs = [
     # Record
-    RecordConfig(scale=str(scale)),
-    RecordRTreeConfig(tile_x, tile_y, scale=str(scale)),
+    RecordConfig(     scale=str(scale)),
+    RecordRTreeConfig(scale=str(scale)),
     RecordGridConfig( tile_x, tile_y, scale=str(scale)),
 
     # Tiled playback
@@ -85,7 +85,6 @@
     # Viewport playback
     ViewportBitmapConfig(viewport_x, viewport_y, scale=str(scale)),
     ViewportRTreeConfig( viewport_x, viewport_y, scale=str(scale)),
-    ViewportGridConfig(  viewport_x, viewport_y, scale=str(scale)),
   ]
 
   if do_gpu:
@@ -107,11 +106,11 @@
   'debug': [TiledBitmapConfig(DEFAULT_TILE_X, DEFAULT_TILE_Y)],
   'default': default_configs,
   'no_gpu': [cfg for cfg in default_configs if cfg['device'] != 'gpu'],
-  'nexus_s':      AndroidConfigList((256, 256), 0.4897, [],  (800,  480),
+  'nexus_s':      AndroidConfigList((256, 256), 0.4897, [],  (480,  800),
                                     do_gpu=False),
-  'nexus_4':      AndroidConfigList((256, 256), 0.8163, [],  (1280, 768)),
-  'nexus_7':      AndroidConfigList((256, 256), 0.8163, [2], (1280, 800)),
-  'nexus_10':     AndroidConfigList((512, 512), 1.6326, [],  (2560, 1600)),
-  'galaxy_nexus': AndroidConfigList((256, 256), 0.8163, [],  (1280, 800)),
-  'xoom':         AndroidConfigList((256, 256), 0.8163, [],  (1200, 800)),
+  'xoom':         AndroidConfigList((256, 256), 1.2244, [],  (1200, 800)),
+  'galaxy_nexus': AndroidConfigList((256, 256), 0.8163, [],  (800,  1280)),
+  'nexus_4':      AndroidConfigList((256, 256), 0.7836, [],  (768,  1280)),
+  'nexus_7':      AndroidConfigList((256, 256), 1.3061, [2], (1280, 800)),
+  'nexus_10':     AndroidConfigList((512, 512), 2.6122, [],  (2560, 1600)),
 }
\ No newline at end of file
diff --git a/tools/bench_pictures_cfg_helper.py b/tools/bench_pictures_cfg_helper.py
index 654bad6..65d63a4 100644
--- a/tools/bench_pictures_cfg_helper.py
+++ b/tools/bench_pictures_cfg_helper.py
@@ -13,6 +13,13 @@
   return config
 
 
+def TileArgs(tile_x, tile_y):
+  return {'mode': ['tile', str(tile_x), str(tile_y)],
+          # TODO(borenet): Turn this back on once the parser is working.
+          #'timeIndividualTiles': True
+          }
+
+
 def BitmapConfig(**kwargs):
   return Config(device='bitmap', **kwargs)
 
@@ -22,11 +29,11 @@
 
 
 def TiledBitmapConfig(tile_x, tile_y, **kwargs):
-  return BitmapConfig(mode=['tile', str(tile_x), str(tile_y)], **kwargs)
+  return BitmapConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
 
 
 def TiledGPUConfig(tile_x, tile_y, **kwargs):
-  return GPUConfig(mode=['tile', str(tile_x), str(tile_y)], **kwargs)
+  return GPUConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
 
 
 def ViewportBitmapConfig(viewport_x, viewport_y, **kwargs):
@@ -38,8 +45,8 @@
 
 
 def ViewportRTreeConfig(viewport_x, viewport_y, **kwargs):
-  return RTreeConfig(viewport_x, viewport_y, mode='simple',
-                     viewport=[str(viewport_x), str(viewport_y)], **kwargs)
+  return RTreeConfig(mode='simple', viewport=[str(viewport_x), str(viewport_y)],
+                     **kwargs)
 
 
 def ViewportGridConfig(viewport_x, viewport_y, **kwargs):
@@ -64,9 +71,8 @@
                            **kwargs)
 
 
-def RTreeConfig(tile_x, tile_y, mode, **kwargs):
-  return BitmapConfig(mode=mode, bbh=['rtree', str(tile_x), str(tile_y)],
-                      **kwargs)
+def RTreeConfig(**kwargs):
+  return BitmapConfig(bbh='rtree', **kwargs)
 
 
 def GridConfig(tile_x, tile_y, mode, **kwargs):
@@ -74,18 +80,16 @@
                       **kwargs)
 
 
-def RecordRTreeConfig(tile_x, tile_y, **kwargs):
-  return RTreeConfig(tile_x=tile_x, tile_y=tile_y, mode='record', **kwargs)
+def RecordRTreeConfig(**kwargs):
+  return RTreeConfig(mode='record', **kwargs)
 
 
-def PlaybackCreationRTreeConfig(tile_x, tile_y, **kwargs):
-  return RTreeConfig(tile_x=tile_x, tile_y=tile_y, mode='playbackCreation',
-                     **kwargs)
+def PlaybackCreationRTreeConfig(**kwargs):
+  return RTreeConfig(mode='playbackCreation', **kwargs)
 
 
 def TileRTreeConfig(tile_x, tile_y, **kwargs):
-  return RTreeConfig(tile_x=tile_x, tile_y=tile_y,
-                     mode=['tile', str(tile_x), str(tile_y)], **kwargs)
+  return RTreeConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
 
 
 def RecordGridConfig(tile_x, tile_y, **kwargs):
@@ -97,5 +101,5 @@
 
 
 def TileGridConfig(tile_x, tile_y, **kwargs):
-  return GridConfig(tile_x, tile_y, mode=['tile', str(tile_x), str(tile_y)],
-                    **kwargs)
\ No newline at end of file
+  return GridConfig(tile_x, tile_y,
+                    **dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
\ No newline at end of file
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
index 65fc42a..b485aff 100644
--- a/tools/bench_pictures_main.cpp
+++ b/tools/bench_pictures_main.cpp
@@ -27,6 +27,7 @@
     "line",
     "bitmap",
     "rect",
+    "oval",
     "path",
     "text",
     "all",
@@ -450,7 +451,7 @@
                 gLogger.logError("Missing scaleFactor for --scale\n");
                 PRINT_USAGE_AND_EXIT;
             }
-            scaleFactor = atof(*argv);
+            scaleFactor = SkDoubleToScalar(atof(*argv));
         } else if (0 == strcmp(*argv, "--tiles")) {
             ++argv;
             if (argv >= stop) {
@@ -774,7 +775,7 @@
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
-#ifdef SK_ENABLE_INST_COUNT
+#if SK_ENABLE_INST_COUNT
     gPrintInstCount = true;
 #endif
     SkAutoGraphics ag;
diff --git a/tools/build-tot-chromium.sh b/tools/build-tot-chromium.sh
new file mode 100755
index 0000000..9592462
--- /dev/null
+++ b/tools/build-tot-chromium.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# Author: Ravi Mistry
+#
+# Script to checkout and build a fresh copy of Chromium from head that uses a
+# writable, tip-of-tree Skia rather than the read-only, revision-locked Skia
+# specified in http://src.chromium.org/viewvc/chrome/trunk/src/DEPS
+#
+# Sample Usage:
+#   tools/build-tot-chromium.sh ~/chromiumtrunk
+
+if [[ $# -ne 1 ]] ; then
+  echo "usage: $0 chromium_location"
+  exit 1
+fi
+CHROMIUM_LOCATION=$1
+
+echo -e "\n\n========Deleting $CHROMIUM_LOCATION========\n\n"
+rm -rf $CHROMIUM_LOCATION
+
+mkdir $CHROMIUM_LOCATION
+cd $CHROMIUM_LOCATION
+gclient config https://src.chromium.org/chrome/trunk/src
+echo '
+solutions = [
+  { "name"        : "src",
+    "url"         : "https://src.chromium.org/chrome/trunk/src",
+    "deps_file"   : "DEPS",
+    "managed"     : True,
+    "custom_deps" : {
+      "src/third_party/skia": "https://skia.googlecode.com/svn/trunk",
+      "src/third_party/skia/gyp": None,
+      "src/third_party/skia/src": None,
+      "src/third_party/skia/include": None,
+    },
+    "safesync_url": "",
+  },
+]
+' > .gclient
+
+echo -e "\n\n========Starting gclient sync========\n\n"
+START_TIME=$SECONDS
+gclient sync
+END_TIME=$SECONDS
+echo -e "\n\n========gclient sync took $((END_TIME - START_TIME)) seconds========\n\n"
+
+cd src
+rm -rf out/Debug out/Release
+GYP_GENERATORS='ninja' ./build/gyp_chromium
+
+echo -e "\n\n========Starting ninja build========\n\n"
+START_TIME=$SECONDS
+ninja -C out/Release chrome
+END_TIME=$SECONDS
+echo -e "\n\n========ninja build took $((END_TIME - START_TIME)) seconds========\n\n"
+
+SVN_VERSION=`svnversion .`
+echo -e "\n\n========The Chromium & Skia versions are $SVN_VERSION========\n\n"
+
diff --git a/tools/filtermain.cpp b/tools/filtermain.cpp
index c1b2040..3715b3e 100644
--- a/tools/filtermain.cpp
+++ b/tools/filtermain.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkDebugCanvas.h"
 #include "SkDevice.h"
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
@@ -19,129 +20,27 @@
 
 static void usage() {
     SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n");
-    SkDebugf("                        [-p pathFile] [-t textureDir] [-h|--help]\n\n");
+    SkDebugf("                        [-h|--help]\n\n");
     SkDebugf("    -i inFile  : file to file.\n");
     SkDebugf("    -o outFile : result of filtering.\n");
     SkDebugf("    --input-dir : process all files in dir with .skp extension.\n");
     SkDebugf("    --output-dir : results of filtering the input dir.\n");
-    SkDebugf("    -p pathFile : file in which to place compileable path data.\n");
-    SkDebugf("    -t textureDir : directory in which to place textures. (only available w/ single file)\n");
     SkDebugf("    -h|--help  : Show this help message.\n");
 }
 
-// SkFilterRecord allows the filter to manipulate the read in SkPicture
-class SkFilterRecord : public SkPictureRecord {
-public:
-    SkFilterRecord(uint32_t recordFlags, SkDevice* device, SkFILEWStream* pathStream)
-        : INHERITED(recordFlags, device)
-        , fTransSkipped(0)
-        , fTransTot(0)
-        , fScalesSkipped(0)
-        , fScalesTot(0)
-        , fPathStream(pathStream) {
-    }
-
-    virtual ~SkFilterRecord() {
-    }
-
-    virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAntiAlias) SK_OVERRIDE {
-        if (!path.isRect(NULL) && 4 < path.countPoints()) {
-            sk_tools::dump_path(fPathStream, path);
-        }
-        return INHERITED::clipPath(path, op, doAntiAlias);
-    }
-
-    virtual void drawPath(const SkPath& path, const SkPaint& p) SK_OVERRIDE {
-        if (!path.isRect(NULL) && 4 < path.countPoints()) {
-            sk_tools::dump_path(fPathStream, path);
-        }
-        INHERITED::drawPath(path, p);
-    }
-
-    virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE {
-        ++fTransTot;
-
-#if 0
-        if (0 == dx && 0 == dy) {
-            ++fTransSkipped;
-            return true;
-        }
-#endif
-
-        return INHERITED::translate(dx, dy);
-    }
-
-    virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE {
-        ++fScalesTot;
-
-#if 0
-        if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
-            ++fScalesSkipped;
-            return true;
-        }
-#endif
-
-        return INHERITED::scale(sx, sy);
-    }
-
-    void saveImages(const SkString& path) {
-        SkTRefArray<SkBitmap>* bitmaps = fBitmapHeap->extractBitmaps();
-
-        if (NULL != bitmaps) {
-            for (int i = 0; i < bitmaps->count(); ++i) {
-                SkString filename(path);
-                if (!path.endsWith("\\")) {
-                    filename.append("\\");
-                }
-                filename.append("image");
-                filename.appendS32(i);
-                filename.append(".png");
-
-                SkImageEncoder::EncodeFile(filename.c_str(), (*bitmaps)[i],
-                                           SkImageEncoder::kPNG_Type, 0);
-            }
-        }
-
-        bitmaps->unref();
-    }
-
-    void report() {
-        SkDebugf("%d Trans skipped (out of %d)\n", fTransSkipped, fTransTot);
-        SkDebugf("%d Scales skipped (out of %d)\n", fScalesSkipped, fScalesTot);
-    }
-
-protected:
-    int fTransSkipped;
-    int fTransTot;
-
-    int fScalesSkipped;
-    int fScalesTot;
-
-    SkFILEWStream* fPathStream;
-private:
-    typedef SkPictureRecord INHERITED;
-};
-
-// Wrap SkPicture to allow installation of a SkFilterRecord object
-class SkFilterPicture : public SkPicture {
-public:
-    SkFilterPicture(int width, int height, SkPictureRecord* record) {
-        fWidth = width;
-        fHeight = height;
-        fRecord = record;
-        SkSafeRef(fRecord);
-    }
-
-private:
-    typedef SkPicture INHERITED;
-};
-
-static bool PNGEncodeBitmapToStream(SkWStream* stream, const SkBitmap& bitmap) {
-    return SkImageEncoder::EncodeStream(stream, bitmap, SkImageEncoder::kPNG_Type, 100);
+// Is the supplied paint simply a color?
+static bool is_simple(const SkPaint& p) {
+    return NULL == p.getPathEffect() &&
+           NULL == p.getShader() &&
+           NULL == p.getXfermode() &&
+           NULL == p.getMaskFilter() &&
+           NULL == p.getColorFilter() &&
+           NULL == p.getRasterizer() &&
+           NULL == p.getLooper() &&
+           NULL == p.getImageFilter();
 }
 
-int filter_picture(const SkString& inFile, const SkString& outFile,
-                   const SkString& textureDir, SkFILEWStream *pathStream) {
+static int filter_picture(const SkString& inFile, const SkString& outFile) {
     SkPicture* inPicture = NULL;
 
     SkFILEStream inStream(inFile.c_str());
@@ -154,35 +53,67 @@
         return -1;
     }
 
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kNo_Config, inPicture->width(), inPicture->height());
-    SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
+    SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height());
+    debugCanvas.setBounds(inPicture->width(), inPicture->height());
+    inPicture->draw(&debugCanvas);
 
-    SkAutoTUnref<SkFilterRecord> filterRecord(SkNEW_ARGS(SkFilterRecord, (0, dev, pathStream)));
+    const SkTDArray<SkDrawCommand*>& commands = debugCanvas.getDrawCommands();
 
-    // Playback the read in picture to the SkFilterRecorder to allow filtering
-    filterRecord->beginRecording();
-    inPicture->draw(filterRecord);
-    filterRecord->endRecording();
+    for (int i = 0; i < commands.count(); ++i) {
+        // Check for:
+        //    SAVE_LAYER
+        //      DRAW_BITMAP_RECT_TO_RECT
+        //    RESTORE
+        // where the saveLayer's color can be moved into the drawBitmapRect
+        if (SAVE_LAYER == commands[i]->getType() && commands.count() > i+2) {
+            if (DRAW_BITMAP_RECT_TO_RECT == commands[i+1]->getType() &&
+                RESTORE == commands[i+2]->getType()) {
+                SaveLayer* sl = (SaveLayer*) commands[i];
+                DrawBitmapRect* dbmr = (DrawBitmapRect*) commands[i+1];
 
-    filterRecord->report();
+                const SkPaint* p0 = sl->paint();
+                SkPaint* p1 = dbmr->paint();
+
+                if (NULL == p0) {
+                    commands[i]->setVisible(false);
+                    commands[i+2]->setVisible(false);
+                } else if (NULL == p1) {
+                    commands[i]->setVisible(false);
+                    dbmr->setPaint(*p0);
+                    commands[i+2]->setVisible(false);
+                } else if (is_simple(*p0) &&
+                           (SkColorGetR(p0->getColor()) == SkColorGetR(p1->getColor())) &&
+                           (SkColorGetG(p0->getColor()) == SkColorGetG(p1->getColor())) &&
+                           (SkColorGetB(p0->getColor()) == SkColorGetB(p1->getColor()))) {
+                    commands[i]->setVisible(false);
+                    SkColor newColor = SkColorSetA(p1->getColor(),
+                                                   SkColorGetA(p0->getColor()));
+                    p1->setColor(newColor);
+                    commands[i+2]->setVisible(false);
+                }
+            }
+        }
+    }
 
     if (!outFile.isEmpty()) {
-        SkFilterPicture outPicture(inPicture->width(), inPicture->height(), filterRecord);
+        SkPicture outPicture;
+
+        SkCanvas* canvas = outPicture.beginRecording(inPicture->width(), inPicture->height());
+        debugCanvas.draw(canvas);
+        outPicture.endRecording();
+
         SkFILEWStream outStream(outFile.c_str());
 
         outPicture.serialize(&outStream);
     }
 
-    if (!textureDir.isEmpty()) {
-        filterRecord->saveImages(textureDir);
-    }
-
     return 0;
 }
 
 // This function is not marked as 'static' so it can be referenced externally
 // in the iOS build.
+int tool_main(int argc, char** argv); // suppress a warning on mac
+
 int tool_main(int argc, char** argv) {
     SkGraphics::Init();
 
@@ -191,7 +122,7 @@
         return -1;
     }
 
-    SkString inFile, outFile, inDir, outDir, textureDir, pathFile;
+    SkString inFile, outFile, inDir, outDir;
 
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
@@ -231,24 +162,6 @@
                 usage();
                 return -1;
             }
-        } else if (strcmp(*argv, "-p") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                pathFile.set(*argv);
-            } else {
-                SkDebugf("missing arg for -p\n");
-                usage();
-                return -1;
-            }
-        } else if (strcmp(*argv, "-t") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                textureDir.set(*argv);
-            } else {
-                SkDebugf("missing arg for -t\n");
-                usage();
-                return -1;
-            }
         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
             usage();
             return 0;
@@ -259,27 +172,8 @@
         }
     }
 
-    if(!inDir.isEmpty() && !textureDir.isEmpty()) {
-        SkDebugf("ERROR: The textureDir option is not permitted when passing an input directory.\n");
-        usage();
-        return -1;
-    }
-
-    SkFILEWStream *pathStream = NULL;
-
-    if (!pathFile.isEmpty()) {
-        pathStream = new SkFILEWStream(pathFile.c_str());
-        if (!pathStream->isValid()) {
-            SkDebugf("Could open path file %s\n", pathFile.c_str());
-            delete pathStream;
-            return -1;
-        }
-
-        sk_tools::dump_path_prefix(pathStream);
-    }
-
     SkOSFile::Iter iter(inDir.c_str(), "skp");
-    int failures = 0;
+
     SkString inputFilename, outputFilename;
     if (iter.next(&inputFilename)) {
 
@@ -289,26 +183,16 @@
                 sk_tools::make_filepath(&outFile, outDir, inputFilename);
             }
             SkDebugf("Executing %s\n", inputFilename.c_str());
-            filter_picture(inFile, outFile, textureDir, pathStream);
+            filter_picture(inFile, outFile);
         } while(iter.next(&inputFilename));
 
     } else if (!inFile.isEmpty()) {
-        filter_picture(inFile, outFile, textureDir, pathStream);
+        filter_picture(inFile, outFile);
     } else {
         usage();
-        if (NULL != pathStream) {
-            delete pathStream;
-            pathStream = NULL;
-        }
         return -1;
     }
 
-    if (NULL != pathStream) {
-        sk_tools::dump_path_suffix(pathStream);
-        delete pathStream;
-        pathStream = NULL;
-    }
-
     SkGraphics::Term();
     return 0;
 }
diff --git a/tools/pinspect.cpp b/tools/pinspect.cpp
index b82bd33..b5a6727 100644
--- a/tools/pinspect.cpp
+++ b/tools/pinspect.cpp
@@ -39,9 +39,13 @@
 }
 
 static void dumpOps(SkPicture* pic) {
+#ifdef SK_DEVELOPER
     SkDebugfDumper dumper;
     SkDumpCanvas canvas(&dumper);
     canvas.drawPicture(*pic);
+#else
+    printf("SK_DEVELOPER mode not enabled\n");
+#endif
 }
 
 int tool_main(int argc, char** argv);
diff --git a/tools/render_pdfs_main.cpp b/tools/render_pdfs_main.cpp
index 231f52e..3aac682 100644
--- a/tools/render_pdfs_main.cpp
+++ b/tools/render_pdfs_main.cpp
@@ -33,7 +33,7 @@
     SkDebugf("SKP to PDF rendering tool\n");
     SkDebugf("\n"
 "Usage: \n"
-"     %s <input>... <outputDir> \n"
+"     %s <input>... -w <outputDir> \n"
 , argv0);
     SkDebugf("\n\n");
     SkDebugf(
@@ -89,15 +89,25 @@
 static bool write_output(const SkString& outputDir,
                          const SkString& inputFilename,
                          const sk_tools::PdfRenderer& renderer) {
+    if (outputDir.isEmpty()) {
+        SkDynamicMemoryWStream stream;
+        renderer.write(&stream);
+        return true;
+    }
+
     SkString outputPath;
     if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
         return false;
     }
-    bool isWritten = renderer.write(outputPath);
-    if (!isWritten) {
+
+    SkFILEWStream stream(outputPath.c_str());
+    if (!stream.isValid()) {
         SkDebugf("Could not write to file %s\n", outputPath.c_str());
+        return false;
     }
-    return isWritten;
+    renderer.write(&stream);
+
+    return true;
 }
 
 /** Reads an skp file, renders it to pdf and writes the output to a pdf file
@@ -168,7 +178,8 @@
 }
 
 static void parse_commandline(int argc, char* const argv[],
-                              SkTArray<SkString>* inputs) {
+                              SkTArray<SkString>* inputs,
+                              SkString* outputDir) {
     const char* argv0 = argv[0];
     char* const* stop = argv + argc;
 
@@ -176,12 +187,20 @@
         if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
             usage(argv0);
             exit(-1);
+        } else if (0 == strcmp(*argv, "-w")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing outputDir for -w\n");
+                usage(argv0);
+                exit(-1);
+            }
+            *outputDir = SkString(*argv);
         } else {
             inputs->push_back(SkString(*argv));
         }
     }
 
-    if (inputs->count() < 2) {
+    if (inputs->count() < 1) {
         usage(argv0);
         exit(-1);
     }
@@ -197,11 +216,11 @@
         renderer(SkNEW(sk_tools::SimplePdfRenderer));
     SkASSERT(renderer.get());
 
-    parse_commandline(argc, argv, &inputs);
-    SkString outputDir = inputs[inputs.count() - 1];
+    SkString outputDir;
+    parse_commandline(argc, argv, &inputs, &outputDir);
 
     int failures = 0;
-    for (int i = 0; i < inputs.count() - 1; i ++) {
+    for (int i = 0; i < inputs.count(); i ++) {
         failures += process_input(inputs[i], outputDir, *renderer);
     }
 
@@ -209,6 +228,7 @@
         SkDebugf("Failed to render %i PDFs.\n", failures);
         return 1;
     }
+    return 0;
 }
 
 #if !defined SK_BUILD_FOR_IOS
@@ -216,4 +236,3 @@
     return tool_main(argc, (char**) argv);
 }
 #endif
-
diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp
index fce4055..a459bf2 100644
--- a/tools/render_pictures_main.cpp
+++ b/tools/render_pictures_main.cpp
@@ -32,7 +32,7 @@
 "     [--pipe]\n"
 "     [--bbh bbhType]\n"
 "     [--multi count]\n"
-"     [--validate]\n"
+"     [--validate [--maxComponentDiff n]]\n"
 "     [--writeWholeImage]\n"
 "     [--clone n]\n"
 "     [--viewport width height][--scale sf]\n"
@@ -86,6 +86,8 @@
     SkDebugf(
 "     --validate: Verify that the rendered image contains the same pixels as "
 "the picture rendered in simple mode.\n"
+"     --maxComponentDiff: maximum diff on a component. Default is 256, "
+"which means we report but we do not generate an error.\n"
 "     --writeWholeImage: In tile mode, write the entire rendered image to a "
 "file, instead of an image for each tile.\n");
     SkDebugf(
@@ -166,19 +168,28 @@
         SkDELETE(outputPath);
     }
 
-    renderer.resetState();
-
     renderer.end();
 
     SkDELETE(picture);
     return success;
 }
 
+static inline int getByte(uint32_t value, int index) {
+    SkASSERT(0 <= index && index < 4);
+    return (value >> (index * 8)) & 0xFF;
+}
+
+static int MaxByteDiff(uint32_t v1, uint32_t v2) {
+    return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
+                   SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
+}
+
 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
                            sk_tools::PictureRenderer& renderer,
-                           bool validate,
+                           bool validate, int maxComponentDiff,
                            bool writeWholeImage,
                            int clones) {
+    int diffs[256] = {0};
     SkBitmap* bitmap = NULL;
     bool success = render_picture(inputPath,
         writeWholeImage ? NULL : outputDir,
@@ -218,13 +229,19 @@
             SkDELETE(referenceBitmap);
             return false;
         }
-        
+
         for (int y = 0; success && y < bitmap->height(); y++) {
             for (int x = 0; success && x < bitmap->width(); x++) {
-                if (*referenceBitmap->getAddr32(x, y) != *bitmap->getAddr32(x, y)) {
-                    SkDebugf("Expected pixel at (%i %i): 0x%x, actual 0x%x\n",
-                             x, y,
-                             referenceBitmap->getAddr32(x, y),
+                int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
+                                       *bitmap->getAddr32(x, y));
+                SkASSERT(diff >= 0 && diff <= 255);
+                diffs[diff]++;
+
+                if (diff > maxComponentDiff) {
+                    SkDebugf("Expected pixel at (%i %i) exceedds maximum "
+                                 "component diff of %i: 0x%x, actual 0x%x\n",
+                             x, y, maxComponentDiff,
+                             *referenceBitmap->getAddr32(x, y),
                              *bitmap->getAddr32(x, y));
                     SkDELETE(bitmap);
                     SkDELETE(referenceBitmap);
@@ -233,6 +250,12 @@
             }
         }
         SkDELETE(referenceBitmap);
+
+        for (int i = 1; i <= 255; ++i) {
+            if(diffs[i] > 0) {
+                SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
+            }
+        }
     }
 
     if (writeWholeImage) {
@@ -258,7 +281,8 @@
 
 static int process_input(const SkString& input, const SkString* outputDir,
                          sk_tools::PictureRenderer& renderer,
-                         bool validate, bool writeWholeImage, int clones) {
+                         bool validate, int maxComponentDiff,
+                         bool writeWholeImage, int clones) {
     SkOSFile::Iter iter(input.c_str(), "skp");
     SkString inputFilename;
     int failures = 0;
@@ -268,14 +292,16 @@
             SkString inputPath;
             sk_tools::make_filepath(&inputPath, input, inputFilename);
             if (!render_picture(inputPath, outputDir, renderer,
-                                validate, writeWholeImage, clones)) {
+                                validate, maxComponentDiff,
+                                writeWholeImage, clones)) {
                 ++failures;
             }
         } while(iter.next(&inputFilename));
     } else if (SkStrEndsWith(input.c_str(), ".skp")) {
         SkString inputPath(input);
         if (!render_picture(inputPath, outputDir, renderer,
-                            validate, writeWholeImage, clones)) {
+                            validate, maxComponentDiff,
+                            writeWholeImage, clones)) {
             ++failures;
         }
     } else {
@@ -288,7 +314,8 @@
 
 static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
                               sk_tools::PictureRenderer*& renderer, SkString*& outputDir,
-                              bool* validate, bool* writeWholeImage,
+                              bool* validate, int* maxComponentDiff,
+                              bool* writeWholeImage,
                               int* clones){
     const char* argv0 = argv[0];
     char* const* stop = argv + argc;
@@ -312,6 +339,7 @@
     sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
         sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
     *validate = false;
+    *maxComponentDiff = 256;
     *writeWholeImage = false;
     *clones = 0;
     SkISize viewport;
@@ -425,7 +453,7 @@
                 usage(argv0);
                 exit(-1);
             }
-            scaleFactor = atof(*argv);
+            scaleFactor = SkDoubleToScalar(atof(*argv));
         } else if (0 == strcmp(*argv, "--tiles")) {
             ++argv;
             if (argv >= stop) {
@@ -511,6 +539,25 @@
             outputDir = SkNEW_ARGS(SkString, (*argv));
         } else if (0 == strcmp(*argv, "--validate")) {
             *validate = true;
+        } else if (0 == strcmp(*argv, "--maxComponentDiff")) {
+            if (!*validate) {
+                SkDebugf("--maxComponentDiff must be used only with --validate\n");
+                usage(argv0);
+                exit(-1);
+            }
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing arg for --maxComponentDiff\n");
+                usage(argv0);
+                exit(-1);
+            }
+            *maxComponentDiff = atoi(*argv);
+            if (*maxComponentDiff < 0 || *maxComponentDiff > 256) {
+                SkSafeUnref(renderer);
+                SkDebugf("maxComponentDiff: 0 - 256.\n");
+                usage(argv0);
+                exit(-1);
+            }
         } else if (0 == strcmp(*argv, "--writeWholeImage")) {
             *writeWholeImage = true;
         } else {
@@ -676,16 +723,18 @@
     sk_tools::PictureRenderer* renderer = NULL;
     SkString* outputDir = NULL;
     bool validate = false;
+    int maxComponentDiff = 256;
     bool writeWholeImage = false;
     int clones = 0;
     parse_commandline(argc, argv, &inputs, renderer, outputDir,
-                      &validate, &writeWholeImage, &clones);
+                      &validate, &maxComponentDiff, &writeWholeImage, &clones);
     SkASSERT(renderer);
 
     int failures = 0;
     for (int i = 0; i < inputs.count(); i ++) {
         failures += process_input(inputs[i], outputDir, *renderer,
-                                  validate, writeWholeImage, clones);
+                                  validate, maxComponentDiff,
+                                  writeWholeImage, clones);
     }
     if (failures != 0) {
         SkDebugf("Failed to render %i pictures.\n", failures);
diff --git a/tools/sanitize_source_files.py b/tools/sanitize_source_files.py
index d6e3ec6..c7edaa0 100755
--- a/tools/sanitize_source_files.py
+++ b/tools/sanitize_source_files.py
@@ -23,7 +23,7 @@
     directory: string - The directory which will be recursively traversed to
         find source files to apply modifiers to.
     file_modifiers: list - file-modification methods which should be applied to
-        the complete file content (Eg: EOFNewlineAdder).
+        the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder).
     line_modifiers: list - line-modification methods which should be applied to
         lines in a file (Eg: TabReplacer).
   """
@@ -114,11 +114,12 @@
   return file_content
 
 
-def EOFNewlineAdder(file_content, file_path):
-  """Adds a LF at the end of the file if it does not have one."""
-  if file_content and file_content[-1] != '\n':
+def EOFOneAndOnlyOneNewlineAdder(file_content, file_path):
+  """Adds one and only one LF at the end of the file."""
+  if file_content and (file_content[-1] != '\n' or file_content[-2:-1] == '\n'):
+    file_content = file_content.rstrip()
     file_content += '\n'
-    print 'Added newline to %s' % file_path
+    print 'Added exactly one newline to %s' % file_path
   return file_content
 
 
@@ -140,7 +141,7 @@
       os.getcwd(),
       file_modifiers=[
           CopywriteChecker,
-          EOFNewlineAdder,
+          EOFOneAndOnlyOneNewlineAdder,
           SvnEOLChecker,
       ],
       line_modifiers=[
@@ -149,4 +150,3 @@
           TrailingWhitespaceRemover,
       ],
   ))
-
diff --git a/tools/skdiff.cpp b/tools/skdiff.cpp
index a1783a4..ae6d72c 100644
--- a/tools/skdiff.cpp
+++ b/tools/skdiff.cpp
@@ -166,6 +166,7 @@
     SkAutoLockPixels alpDiff(dr->fDifference.fBitmap);
     SkAutoLockPixels alpWhite(dr->fWhite.fBitmap);
     int mismatchedPixels = 0;
+    int totalMismatchA = 0;
     int totalMismatchR = 0;
     int totalMismatchG = 0;
     int totalMismatchB = 0;
@@ -177,17 +178,21 @@
         for (int x = 0; x < w; x++) {
             SkPMColor c0 = *dr->fBase.fBitmap.getAddr32(x, y);
             SkPMColor c1 = *dr->fComparison.fBitmap.getAddr32(x, y);
-            SkPMColor trueDifference = compute_diff_pmcolor(c0, c1);
             SkPMColor outputDifference = diffFunction(c0, c1);
-            uint32_t thisR = SkGetPackedR32(trueDifference);
-            uint32_t thisG = SkGetPackedG32(trueDifference);
-            uint32_t thisB = SkGetPackedB32(trueDifference);
+            uint32_t thisA = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
+            uint32_t thisR = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
+            uint32_t thisG = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
+            uint32_t thisB = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
+            totalMismatchA += thisA;
             totalMismatchR += thisR;
             totalMismatchG += thisG;
             totalMismatchB += thisB;
             // In HSV, value is defined as max RGB component.
             int value = MAX3(thisR, thisG, thisB);
             dr->fWeightedFraction += ((float) value) / 255;
+            if (thisA > dr->fMaxMismatchA) {
+                dr->fMaxMismatchA = thisA;
+            }
             if (thisR > dr->fMaxMismatchR) {
                 dr->fMaxMismatchR = thisR;
             }
@@ -215,6 +220,8 @@
     int pixelCount = w * h;
     dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount;
     dr->fWeightedFraction /= pixelCount;
+    dr->fTotalMismatchA = totalMismatchA;
+    dr->fAverageMismatchA = ((float) totalMismatchA) / pixelCount;
     dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount;
     dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount;
     dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount;
diff --git a/tools/skdiff.h b/tools/skdiff.h
index b9e69ce..6abaf6c 100644
--- a/tools/skdiff.h
+++ b/tools/skdiff.h
@@ -115,9 +115,12 @@
         , fWhite()
         , fFractionDifference(0)
         , fWeightedFraction(0)
+        , fAverageMismatchA(0)
         , fAverageMismatchR(0)
         , fAverageMismatchG(0)
         , fAverageMismatchB(0)
+        , fTotalMismatchA(0)
+        , fMaxMismatchA(0)
         , fMaxMismatchR(0)
         , fMaxMismatchG(0)
         , fMaxMismatchB(0)
@@ -135,10 +138,14 @@
     float fFractionDifference;
     float fWeightedFraction;
 
+    float fAverageMismatchA;
     float fAverageMismatchR;
     float fAverageMismatchG;
     float fAverageMismatchB;
 
+    uint32_t fTotalMismatchA;
+
+    uint32_t fMaxMismatchA;
     uint32_t fMaxMismatchR;
     uint32_t fMaxMismatchG;
     uint32_t fMaxMismatchB;
diff --git a/tools/skdiff_html.cpp b/tools/skdiff_html.cpp
index 85d8777..6f3c3b0 100644
--- a/tools/skdiff_html.cpp
+++ b/tools/skdiff_html.cpp
@@ -124,10 +124,23 @@
         if (diff.fFractionDifference < 0.01) {
             print_pixel_count(stream, diff);
         }
+        stream->writeText("<br>");
+        if (SkScalarRoundToInt(diff.fAverageMismatchA) > 0) {
+          stream->writeText("<br>Average alpha channel mismatch ");
+          stream->writeDecAsText(SkScalarRoundToInt(diff.fAverageMismatchA));
+        }
+
+        stream->writeText("<br>Max alpha channel mismatch ");
+        stream->writeDecAsText(SkScalarRoundToInt(diff.fMaxMismatchA));
+
+        stream->writeText("<br>Total alpha channel mismatch ");
+        stream->writeDecAsText(static_cast<int>(diff.fTotalMismatchA));
+
+        stream->writeText("<br>");
         stream->writeText("<br>Average color mismatch ");
-        stream->writeDecAsText(static_cast<int>(MAX3(diff.fAverageMismatchR,
-                                                     diff.fAverageMismatchG,
-                                                     diff.fAverageMismatchB)));
+        stream->writeDecAsText(SkScalarRoundToInt(MAX3(diff.fAverageMismatchR,
+                                                       diff.fAverageMismatchG,
+                                                       diff.fAverageMismatchB)));
         stream->writeText("<br>Max color mismatch ");
         stream->writeDecAsText(MAX3(diff.fMaxMismatchR,
                                     diff.fMaxMismatchG,
diff --git a/tools/skimage_main.cpp b/tools/skimage_main.cpp
index 83f115c..53cc6b3 100644
--- a/tools/skimage_main.cpp
+++ b/tools/skimage_main.cpp
@@ -108,4 +108,3 @@
     return tool_main(argc, (char**) argv);
 }
 #endif
-
diff --git a/tools/submit_try b/tools/submit_try
new file mode 100755
index 0000000..0a4cf2d
--- /dev/null
+++ b/tools/submit_try
@@ -0,0 +1,266 @@
+#!/usr/bin/python
+
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""
+submit_try: Submit a try request.
+
+This is a thin wrapper around the try request utilities in depot_tools which
+adds some validation and supports both git and svn.
+"""
+
+
+import httplib
+import json
+import os
+import subprocess
+import sys
+
+
+# Alias which can be used to run a try on every builder.
+ALL_BUILDERS = 'all'
+
+# Contact information for the build master.
+# TODO(borenet): Share this information from a single location. Filed bug:
+# http://code.google.com/p/skia/issues/detail?id=1081
+SKIA_BUILD_MASTER_HOST = '70.32.156.51'
+SKIA_BUILD_MASTER_PORT = '10117'
+
+# All try builders have this suffix.
+TRYBOT_SUFFIX = '_Trybot'
+
+# Location of the codereview.settings file in the Skia repo.
+SKIA_URL = 'skia.googlecode.com'
+CODEREVIEW_SETTINGS = '/svn/codereview.settings'
+
+# String for matching the svn url of the try server inside codereview.settings.
+TRYSERVER_SVN_URL = 'TRYSERVER_SVN_URL: '
+
+# Strings used for matching svn config properties.
+URL_STR = 'URL: '
+REPO_ROOT_STR = 'Repository Root: '
+
+
+def FindDepotTools():
+  """ Find depot_tools on the local machine and return its location. """
+  which_cmd = 'where' if os.name == 'nt' else 'which'
+  cmd = [which_cmd, 'gcl']
+  proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  if proc.wait() != 0:
+    raise Exception('Couldn\'t find depot_tools in PATH!')
+  gcl = proc.communicate()[0].split('\n')[0].rstrip()
+  depot_tools_dir = os.path.dirname(gcl)
+  return depot_tools_dir
+
+
+def GetCheckoutRoot(is_svn=True):
+  """ Determine where the local checkout is rooted.
+
+  is_svn: boolean; whether we're in an SVN checkout. If False, assume we're in
+      a git checkout.
+  """
+  if is_svn:
+    svn_cmd = 'svn.bat' if os.name == 'nt' else 'svn'
+    cmd = [svn_cmd, 'info']
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT)
+    if proc.wait() != 0:
+      raise Exception('Couldn\'t find checkout root!')
+    output = proc.communicate()[0].split('\n')
+    url = None
+    repo_root = None
+    for line in output:
+      if line.startswith(REPO_ROOT_STR):
+        repo_root = line[len(REPO_ROOT_STR):].rstrip()
+      elif line.startswith(URL_STR):
+        url = line[len(URL_STR):].rstrip()
+    if not url or not repo_root:
+      raise Exception('Couldn\'t find checkout root!')
+    if url == repo_root:
+      return 'svn'
+    return url[len(repo_root)+1:]
+  else:
+    cmd = ['git', 'rev-parse', '--show-toplevel']
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT)
+    if proc.wait() != 0:
+      raise Exception('Couldn\'t find checkout root!')
+    return os.path.basename(proc.communicate()[0])
+
+
+def GetTryRepo():
+  """ Determine the TRYSERVER_SVN_URL from the codereview.settings file in the
+  Skia repo. """
+  connection = httplib.HTTPConnection(SKIA_URL)
+  connection.request('GET', CODEREVIEW_SETTINGS)
+  content = connection.getresponse().read()
+  for line in content.split('\n'):
+    if line.startswith(TRYSERVER_SVN_URL):
+      return line[len(TRYSERVER_SVN_URL):].rstrip()
+  raise Exception('Couldn\'t determine the TRYSERVER_SVN_URL. Make sure it is '
+                  'defined in the %s file.' % CODEREVIEW_SETTINGS)
+
+
+def RetrieveTrybotList():
+  """ Retrieve the list of known trybots from the build master, stripping
+  TRYBOT_SUFFIX from the name. """
+  trybots = []
+  connection = httplib.HTTPConnection(SKIA_BUILD_MASTER_HOST,  
+                                      SKIA_BUILD_MASTER_PORT)  
+  connection.request('GET', '/json/builders')  
+  response = connection.getresponse()  
+  builders = json.load(response)
+
+  for builder in builders:
+    if builder.endswith(TRYBOT_SUFFIX):
+      trybots.append(builder[:-len(TRYBOT_SUFFIX)])
+  return trybots
+
+
+def ValidateArgs(argv, trybots, is_svn=True):
+  """ Parse and validate command-line arguments. If the arguments are valid,
+  returns a tuple of (<changelist name>, <list of trybots>).
+
+  trybots: A list of the known try builders.
+  """
+
+  class CollectedArgs(object):
+    def __init__(self, bots, changelist, revision):
+      self._bots = bots
+      self._changelist = changelist
+      self._revision = revision
+
+    @property
+    def bots(self):
+      for bot in self._bots:
+        yield bot
+
+    @property
+    def changelist(self):
+      return self._changelist
+
+    @property
+    def revision(self):
+      return self._revision
+
+  usage = (
+"""submit_try: Submit a try request.
+submit_try %s--bot <buildername> [<buildername> ...]
+
+--bot               Builder on which to run the try. Required.
+-h, --help          Show this message.
+-r <revision#>      Revision from which to run the try.
+-l, --list_bots     List the available try builders and exit.
+""" % ('<changelist> ' if is_svn else ''))
+
+  def Error(msg=None):
+    if msg:
+      print msg
+    print usage
+    sys.exit(1)
+
+  using_bots = None
+  changelist = None
+  revision = None
+
+  while argv:
+    arg = argv.pop(0)
+    if arg == '-h' or arg == '--help':
+      Error()
+    elif arg == '-l' or arg == '--list_bots':
+      print 'submit_try: Available builders:\n  %s' % '\n  '.join(trybots)
+      sys.exit(0)
+    elif arg == '--bot':
+      if using_bots:
+        Error('--bot specified multiple times.')
+      if len(argv) < 1:
+        Error('You must specify a builder with "--bot".')
+      using_bots = []
+      while argv and not argv[0].startswith('-'):
+        bot = argv.pop(0)
+        if bot == ALL_BUILDERS:
+          if using_bots:
+            Error('Cannot specify "all" with additional builder names.')
+          using_bots = trybots
+          break
+        else:
+          if not bot in trybots:
+            Error('Unrecognized builder: %s' % bot)
+          using_bots.append(bot)
+    elif arg == '-r':
+      if len(argv) < 1:
+        Error('You must specify a revision with "-r".')
+      revision = argv.pop(0)
+    else:
+      if changelist or not is_svn:
+        Error('Unknown argument: %s' % arg)
+      changelist = arg
+  if is_svn and not changelist:
+    Error('You must specify a changelist name.')
+  if not using_bots:
+    Error('You must specify one or more builders using --bot.')
+  return CollectedArgs(bots=using_bots, changelist=changelist,
+                       revision=revision)
+
+
+def SubmitTryRequest(args, is_svn=True):
+  """ Submits a try request for the given changelist on the given list of
+  trybots.
+
+  args: Object whose properties are derived from command-line arguments. If
+      is_svn is True, it should contain:
+      - changelist: string; the name of the changelist to try.
+      - bot: list of strings; the names of the try builders to run.
+      - revision: optional, int; the revision number from which to run the try.
+      If is_svn is False, it should contain:
+      - bot: list of strings; the names of the try builders to run.
+      - revision: optional, int; the revision number from which to run the try. 
+  is_svn: boolean; are we in an SVN repo?
+  """
+  botlist = ','.join(['%s%s' % (bot, TRYBOT_SUFFIX) for bot in args.bots])
+  if is_svn:
+    gcl_cmd = 'gcl.bat' if os.name == 'nt' else 'gcl'
+    try_args = [gcl_cmd, 'try', args.changelist,
+                '--root', GetCheckoutRoot(is_svn),
+                '--bot', botlist]
+    if args.revision:
+      try_args.extend(['-r', args.revision])
+    print ' '.join(try_args)
+    proc = subprocess.Popen(try_args, stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT)
+    if proc.wait() != 0:
+      raise Exception('Failed to submit try request: %s' % (
+          proc.communicate()[0]))
+    print proc.communicate()[0]
+  else:
+    # First, find depot_tools. This is needed to import trychange.
+    sys.path.append(FindDepotTools())
+    import trychange
+    try_args = ['--use_svn',
+                '--svn_repo', GetTryRepo(),
+                '--root', GetCheckoutRoot(is_svn),
+                '--bot', botlist]
+    if args.revision:
+      try_args.extend(['-r', args.revision])
+    trychange.TryChange(try_args, None, False)
+
+
+def main():
+  # Retrieve the list of active try builders from the build master.
+  trybots = RetrieveTrybotList()
+
+  # Determine if we're in an SVN checkout.
+  is_svn = os.path.isdir('.svn')
+
+  # Parse and validate the command-line arguments.
+  args = ValidateArgs(sys.argv[1:], trybots=trybots, is_svn=is_svn)
+
+  # Submit the try request.
+  SubmitTryRequest(args, is_svn=is_svn)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
\ No newline at end of file
diff --git a/tools/submit_try.bat b/tools/submit_try.bat
new file mode 100644
index 0000000..d1d5392
--- /dev/null
+++ b/tools/submit_try.bat
@@ -0,0 +1 @@
+python tools\submit_try %*
\ No newline at end of file
diff --git a/tools/tests/bench_pictures_cfg_test.py b/tools/tests/bench_pictures_cfg_test.py
index 77ad553..9b0e0ac 100644
--- a/tools/tests/bench_pictures_cfg_test.py
+++ b/tools/tests/bench_pictures_cfg_test.py
@@ -39,7 +39,7 @@
         if type(value).__name__ == 'list':
           for item in value:
             ThrowIfNotAString(item)
-        else:
+        elif not value is True:
           ThrowIfNotAString(value)
 
 if __name__ == '__main__':
diff --git a/tools/tests/rebaseline.sh b/tools/tests/rebaseline.sh
new file mode 100755
index 0000000..eee8db8
--- /dev/null
+++ b/tools/tests/rebaseline.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+# Rebaseline the skdiff/*/output-expected/ subdirectories used by the skdiff
+# self-tests.
+# Use with caution: are you sure the new results are actually correct?
+#
+# YOU MUST RE-RUN THIS UNTIL THE SELF-TESTS SUCCEED!
+#
+# TODO: currently, this must be run on Linux to generate baselines that match
+# the ones on the housekeeper bot (which runs on Linux... see
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1417/steps/RunGmSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
+
+function replace_expected_with_actual {
+  # Delete all the expected output files
+  EXPECTED_FILES=$(find skdiff/*/output-expected -type f | grep -v /\.svn/)
+  for EXPECTED_FILE in $EXPECTED_FILES; do
+    rm $EXPECTED_FILE
+  done
+
+  # Copy all the actual output files into the "expected" directories,
+  # creating new subdirs as we go.
+  ACTUAL_FILES=$(find skdiff/*/output-actual -type f | grep -v /\.svn/)
+  for ACTUAL_FILE in $ACTUAL_FILES; do
+    EXPECTED_FILE=${ACTUAL_FILE//actual/expected}
+    mkdir -p $(dirname $EXPECTED_FILE)
+    cp $ACTUAL_FILE $EXPECTED_FILE
+  done
+}
+
+function svn_add_new_files {
+  # Delete all the "actual" directories, so we can svn-add any new "expected"
+  # directories without adding the "actual" ones.
+  rm -rf skdiff/*/output-actual
+  FILES=$(svn stat skdiff/* | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+  FILES=$(svn stat skdiff/*/output-expected | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+}
+
+function svn_delete_old_files {
+  FILES=$(svn stat skdiff/*/output-expected | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+  FILES=$(svn stat skdiff/* | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+}
+
+
+# cd into the gm self-test dir
+cd $(dirname $0)
+
+./run.sh
+SELFTEST_RESULT=$?
+echo
+if [ "$SELFTEST_RESULT" != "0" ]; then
+  replace_expected_with_actual
+  echo "Self-tests still failing, you should probably run this again..."
+else
+  svn_add_new_files
+  svn_delete_old_files
+  echo "Self-tests succeeded this time, you should be done!"
+fi
+exit $SELFTEST_RESULT
+
diff --git a/tools/tests/run.sh b/tools/tests/run.sh
index 1acb124..7731805 100755
--- a/tools/tests/run.sh
+++ b/tools/tests/run.sh
@@ -1,7 +1,17 @@
 #!/bin/bash
 
 # Tests for our tools.
+#
 # TODO: for now, it only tests skdiff
+#
+# TODO: currently, this only passes on Linux (which is the platform that
+# the housekeeper bot runs on, e.g.
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1415/steps/RunToolSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
+# Ideally, these tests should pass on all development platforms...
+# otherwise, how can developers be expected to test them before committing a
+# change?
 
 # cd into .../trunk so all the paths will work
 cd $(dirname $0)/../..
diff --git a/tools/tests/skdiff/test1/output-expected/index.html b/tools/tests/skdiff/test1/output-expected/index.html
index a3e192e..4bf8eee 100644
--- a/tools/tests/skdiff/test1/output-expected/index.html
+++ b/tools/tests/skdiff/test1/output-expected/index.html
@@ -39,10 +39,10 @@
 <td><input type="checkbox" name="different-bits/very-different-sizes.png" checked="yes"></td><td><b>different-bits/very-different-sizes.png</b><br>Image sizes differ</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png" height="128px"></a></td></tr>
 <tr>
 <td><input type="checkbox" name="different-bits/very-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/very-different-pixels-same-size.png</b><br>97.9926% of pixels differ
-  (42.8911% weighted)<br>Average color mismatch 89<br>Max color mismatch 239</td><td><a href="different-bits_very-different-pixels-same-size-white.png"><img src="different-bits_very-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_very-different-pixels-same-size-diff.png"><img src="different-bits_very-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td></tr>
+  (42.8911% weighted)<br><br>Max alpha channel mismatch 0<br>Total alpha channel mismatch 0<br><br>Average color mismatch 89<br>Max color mismatch 239</td><td><a href="different-bits_very-different-pixels-same-size-white.png"><img src="different-bits_very-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_very-different-pixels-same-size-diff.png"><img src="different-bits_very-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td></tr>
 <tr>
 <td><input type="checkbox" name="different-bits/slightly-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/slightly-different-pixels-same-size.png</b><br>0.6630% of pixels differ
-  (0.1904% weighted)<br>(2164 pixels)<br>Average color mismatch 0<br>Max color mismatch 213</td><td><a href="different-bits_slightly-different-pixels-same-size-white.png"><img src="different-bits_slightly-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_slightly-different-pixels-same-size-diff.png"><img src="different-bits_slightly-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td></tr>
+  (0.1904% weighted)<br>(2164 pixels)<br><br>Max alpha channel mismatch 0<br>Total alpha channel mismatch 0<br><br>Average color mismatch 0<br>Max color mismatch 213</td><td><a href="different-bits_slightly-different-pixels-same-size-white.png"><img src="different-bits_slightly-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_slightly-different-pixels-same-size-diff.png"><img src="different-bits_slightly-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td></tr>
 </table>
 <input type="button" onclick="generateCheckedList()" value="Create Rebaseline List">
 <div id="checkedList"></div>
diff --git a/whitespace.txt b/whitespace.txt
index 05f0de5..45655c9 100644
--- a/whitespace.txt
+++ b/whitespace.txt
@@ -62,3 +62,41 @@
 
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+